File indexing completed on 2024-05-05 04:45:05

0001 /*
0002  * Copyright (C) 2003-2008  Justin Karneges <justin@affinix.com>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public
0015  * License along with this library; if not, write to the Free Software
0016  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
0017  */
0018 
0019 // since keyring files are often modified by creating a new copy and
0020 //   overwriting the original file, this messes up Qt's file watching
0021 //   capability since the original file goes away.  to work around this
0022 //   problem, we'll watch the directories containing the keyring files
0023 //   instead of watching the actual files themselves.
0024 //
0025 // FIXME: qca 2.0.1 FileWatch has this logic already, so we can probably
0026 //   simplify this class.
0027 
0028 #include "ringwatch.h"
0029 #include "qca_safetimer.h"
0030 #include "qca_support.h"
0031 #include <QFileInfo>
0032 
0033 using namespace QCA;
0034 
0035 namespace gpgQCAPlugin {
0036 
0037 RingWatch::RingWatch(QObject *parent)
0038     : QObject(parent)
0039 {
0040 }
0041 
0042 RingWatch::~RingWatch()
0043 {
0044     clear();
0045 }
0046 
0047 void RingWatch::add(const QString &filePath)
0048 {
0049     QFileInfo fi(filePath);
0050     // Try to avoid symbolic links
0051     QString path = fi.canonicalPath();
0052     if (path.isEmpty())
0053         path = fi.absolutePath();
0054 
0055     // watching this path already?
0056     DirWatch *dirWatch = nullptr;
0057     foreach (const DirItem &di, dirs) {
0058         if (di.dirWatch->dirName() == path) {
0059             dirWatch = di.dirWatch;
0060             break;
0061         }
0062     }
0063 
0064     // if not, make a watcher
0065     if (!dirWatch) {
0066         // printf("creating dirwatch for [%s]\n", qPrintable(path));
0067 
0068         DirItem di;
0069         di.dirWatch = new DirWatch(path, this);
0070         connect(di.dirWatch, &DirWatch::changed, this, &RingWatch::dirChanged);
0071 
0072         di.changeTimer = new SafeTimer(this);
0073         di.changeTimer->setSingleShot(true);
0074         connect(di.changeTimer, &SafeTimer::timeout, this, &RingWatch::handleChanged);
0075 
0076         dirWatch = di.dirWatch;
0077         dirs += di;
0078     }
0079 
0080     FileItem i;
0081     i.dirWatch = dirWatch;
0082     i.fileName = fi.fileName();
0083     i.exists   = fi.exists();
0084     if (i.exists) {
0085         i.size         = fi.size();
0086         i.lastModified = fi.lastModified();
0087     }
0088     files += i;
0089 
0090     // printf("watching [%s] in [%s]\n", qPrintable(fi.fileName()), qPrintable(i.dirWatch->dirName()));
0091 }
0092 
0093 void RingWatch::clear()
0094 {
0095     files.clear();
0096 
0097     foreach (const DirItem &di, dirs) {
0098         delete di.changeTimer;
0099         delete di.dirWatch;
0100     }
0101 
0102     dirs.clear();
0103 }
0104 
0105 void RingWatch::dirChanged()
0106 {
0107     DirWatch *dirWatch = (DirWatch *)sender();
0108 
0109     int at = -1;
0110     for (int n = 0; n < dirs.count(); ++n) {
0111         if (dirs[n].dirWatch == dirWatch) {
0112             at = n;
0113             break;
0114         }
0115     }
0116     if (at == -1)
0117         return;
0118 
0119     // we get a ton of change notifications for the dir when
0120     //   something happens..   let's collect them and only
0121     //   report after 100ms
0122 
0123     if (!dirs[at].changeTimer->isActive())
0124         dirs[at].changeTimer->start(100);
0125 }
0126 
0127 void RingWatch::handleChanged()
0128 {
0129     SafeTimer *t = (SafeTimer *)sender();
0130 
0131     int at = -1;
0132     for (int n = 0; n < dirs.count(); ++n) {
0133         if (dirs[n].changeTimer == t) {
0134             at = n;
0135             break;
0136         }
0137     }
0138     if (at == -1)
0139         return;
0140 
0141     DirWatch     *dirWatch = dirs[at].dirWatch;
0142     const QString dir      = dirWatch->dirName();
0143 
0144     // see which files changed
0145     QStringList changeList;
0146     for (int n = 0; n < files.count(); ++n) {
0147         FileItem &i        = files[n];
0148         QString   filePath = dir + QLatin1Char('/') + i.fileName;
0149         QFileInfo fi(filePath);
0150 
0151         // if the file didn't exist, and still doesn't, skip
0152         if (!i.exists && !fi.exists())
0153             continue;
0154 
0155         // size/lastModified should only get checked here if
0156         //   the file existed and still exists
0157         if (fi.exists() != i.exists || fi.size() != i.size || fi.lastModified() != i.lastModified) {
0158             changeList += filePath;
0159 
0160             i.exists = fi.exists();
0161             if (i.exists) {
0162                 i.size         = fi.size();
0163                 i.lastModified = fi.lastModified();
0164             }
0165         }
0166     }
0167 
0168     foreach (const QString &s, changeList)
0169         emit changed(s);
0170 }
0171 
0172 } // end namespace gpgQCAPlugin