1/*
2 * Copyright (C) 2003-2008 Justin Karneges <justin@affinix.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19// since keyring files are often modified by creating a new copy and
20// overwriting the original file, this messes up Qt's file watching
21// capability since the original file goes away. to work around this
22// problem, we'll watch the directories containing the keyring files
23// instead of watching the actual files themselves.
24//
25// FIXME: qca 2.0.1 FileWatch has this logic already, so we can probably
26// simplify this class.
27
28#include "ringwatch.h"
29#include "qca_safetimer.h"
30#include "qca_support.h"
31#include <QFileInfo>
32
33using namespace QCA;
34
35namespace gpgQCAPlugin {
36
37RingWatch::RingWatch(QObject *parent)
38 : QObject(parent)
39{
40}
41
42RingWatch::~RingWatch()
43{
44 clear();
45}
46
47void RingWatch::add(const QString &filePath)
48{
49 QFileInfo fi(filePath);
50 // Try to avoid symbolic links
51 QString path = fi.canonicalPath();
52 if (path.isEmpty())
53 path = fi.absolutePath();
54
55 // watching this path already?
56 DirWatch *dirWatch = nullptr;
57 foreach (const DirItem &di, dirs) {
58 if (di.dirWatch->dirName() == path) {
59 dirWatch = di.dirWatch;
60 break;
61 }
62 }
63
64 // if not, make a watcher
65 if (!dirWatch) {
66 // printf("creating dirwatch for [%s]\n", qPrintable(path));
67
68 DirItem di;
69 di.dirWatch = new DirWatch(path, this);
70 connect(sender: di.dirWatch, signal: &DirWatch::changed, context: this, slot: &RingWatch::dirChanged);
71
72 di.changeTimer = new SafeTimer(this);
73 di.changeTimer->setSingleShot(true);
74 connect(sender: di.changeTimer, signal: &SafeTimer::timeout, context: this, slot: &RingWatch::handleChanged);
75
76 dirWatch = di.dirWatch;
77 dirs += di;
78 }
79
80 FileItem i;
81 i.dirWatch = dirWatch;
82 i.fileName = fi.fileName();
83 i.exists = fi.exists();
84 if (i.exists) {
85 i.size = fi.size();
86 i.lastModified = fi.lastModified();
87 }
88 files += i;
89
90 // printf("watching [%s] in [%s]\n", qPrintable(fi.fileName()), qPrintable(i.dirWatch->dirName()));
91}
92
93void RingWatch::clear()
94{
95 files.clear();
96
97 foreach (const DirItem &di, dirs) {
98 delete di.changeTimer;
99 delete di.dirWatch;
100 }
101
102 dirs.clear();
103}
104
105void RingWatch::dirChanged()
106{
107 DirWatch *dirWatch = (DirWatch *)sender();
108
109 int at = -1;
110 for (int n = 0; n < dirs.count(); ++n) {
111 if (dirs[n].dirWatch == dirWatch) {
112 at = n;
113 break;
114 }
115 }
116 if (at == -1)
117 return;
118
119 // we get a ton of change notifications for the dir when
120 // something happens.. let's collect them and only
121 // report after 100ms
122
123 if (!dirs[at].changeTimer->isActive())
124 dirs[at].changeTimer->start(msec: 100);
125}
126
127void RingWatch::handleChanged()
128{
129 SafeTimer *t = (SafeTimer *)sender();
130
131 int at = -1;
132 for (int n = 0; n < dirs.count(); ++n) {
133 if (dirs[n].changeTimer == t) {
134 at = n;
135 break;
136 }
137 }
138 if (at == -1)
139 return;
140
141 DirWatch *dirWatch = dirs[at].dirWatch;
142 const QString dir = dirWatch->dirName();
143
144 // see which files changed
145 QStringList changeList;
146 for (int n = 0; n < files.count(); ++n) {
147 FileItem &i = files[n];
148 QString filePath = dir + QLatin1Char('/') + i.fileName;
149 QFileInfo fi(filePath);
150
151 // if the file didn't exist, and still doesn't, skip
152 if (!i.exists && !fi.exists())
153 continue;
154
155 // size/lastModified should only get checked here if
156 // the file existed and still exists
157 if (fi.exists() != i.exists || fi.size() != i.size || fi.lastModified() != i.lastModified) {
158 changeList += filePath;
159
160 i.exists = fi.exists();
161 if (i.exists) {
162 i.size = fi.size();
163 i.lastModified = fi.lastModified();
164 }
165 }
166 }
167
168 foreach (const QString &s, changeList)
169 emit changed(filePath: s);
170}
171
172} // end namespace gpgQCAPlugin
173

source code of qca/plugins/qca-gnupg/ringwatch.cpp