KCoreAddons

kautosavefile.cpp
1 /*
2  This file is part of the KDE libraries
3 
4  SPDX-FileCopyrightText: 2006 Jacob R Rideout <[email protected]>
5  SPDX-FileCopyrightText: 2015 Nick Shaforostoff <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include "kautosavefile.h"
11 
12 #include <climits> // for NAME_MAX
13 
14 #ifdef Q_OS_WIN
15 #include <stdlib.h> // for _MAX_FNAME
16 static const int maxNameLength = _MAX_FNAME;
17 #else
18 static const int maxNameLength = NAME_MAX;
19 #endif
20 
21 #include "kcoreaddons_debug.h"
22 #include "krandom.h"
23 #include <QCoreApplication>
24 #include <QDir>
25 #include <QLatin1Char>
26 #include <QLockFile>
27 #include <QStandardPaths>
28 
29 class KAutoSaveFilePrivate
30 {
31 public:
32  enum {
33  NamePadding = 8,
34  };
35 
36  QString tempFileName();
37  QUrl managedFile;
38  QLockFile *lock = nullptr;
39  bool managedFileNameChanged = false;
40 };
41 
42 static QStringList findAllStales(const QString &appName)
43 {
45  QStringList files;
46 
47  for (const QString &dir : dirs) {
48  QDir appDir(dir + QLatin1String("/stalefiles/") + appName);
49  qCDebug(KCOREADDONS_DEBUG) << "Looking in" << appDir.absolutePath();
50  const auto listFiles = appDir.entryList(QDir::Files);
51  for (const QString &file : listFiles) {
52  files << (appDir.absolutePath() + QLatin1Char('/') + file);
53  }
54  }
55  return files;
56 }
57 
58 QString KAutoSaveFilePrivate::tempFileName()
59 {
60  // Note: we drop any query string and user/pass info
61  const QString protocol(managedFile.scheme());
62  const QByteArray encodedDirectory = QUrl::toPercentEncoding(managedFile.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path());
63  const QString directory = QString::fromLatin1(encodedDirectory);
64  const QByteArray encodedFileName = QUrl::toPercentEncoding(managedFile.fileName());
65  QString fileName = QString::fromLatin1(encodedFileName);
66 
67  // Remove any part of the path to the right if it is longer than the maximum file name length;
68  // note that "file name" in this context means the file name component only (e.g. test.txt), and
69  // not the whole path (e.g. /home/simba/text.txt).
70  // Ensure that the max. file name length takes into account the other parts of the tempFileName
71  // Subtract 1 for the _ char, 3 for the padding separator, 5 is for the .lock,
72  // 7 for QLockFile's internal code (adding tmp .rmlock) = 16
73  const int pathLengthLimit = maxNameLength - NamePadding - fileName.size() - protocol.size() - 16;
74 
75  QString junk = KRandom::randomString(NamePadding);
76  // This is done so that the separation between the filename and path can be determined
77  fileName += junk.rightRef(3) + protocol + QLatin1Char('_') + directory.leftRef(pathLengthLimit) + junk;
78 
79  return fileName;
80 }
81 
82 KAutoSaveFile::KAutoSaveFile(const QUrl &filename, QObject *parent)
83  : QFile(parent)
84  , d(new KAutoSaveFilePrivate)
85 {
86  setManagedFile(filename);
87 }
88 
90  : QFile(parent)
91  , d(new KAutoSaveFilePrivate)
92 {
93 }
94 
96 {
97  releaseLock();
98  delete d->lock;
99 }
100 
102 {
103  return d->managedFile;
104 }
105 
106 void KAutoSaveFile::setManagedFile(const QUrl &filename)
107 {
108  releaseLock();
109 
110  d->managedFile = filename;
111  d->managedFileNameChanged = true;
112 }
113 
115 {
116  if (d->lock && d->lock->isLocked()) {
117  delete d->lock;
118  d->lock = nullptr;
119  if (!fileName().isEmpty()) {
120  remove();
121  }
122  }
123 }
124 
126 {
127  if (d->managedFile.isEmpty()) {
128  return false;
129  }
130 
131  QString tempFile;
132  if (d->managedFileNameChanged) {
135  if (!QDir().mkpath(staleFilesDir)) {
136  return false;
137  }
138  tempFile = staleFilesDir + QChar::fromLatin1('/') + d->tempFileName();
139  } else {
140  tempFile = fileName();
141  }
142 
143  d->managedFileNameChanged = false;
144 
145  setFileName(tempFile);
146 
147  if (QFile::open(openmode)) {
148  if (!d->lock) {
149  d->lock = new QLockFile(tempFile + QLatin1String(".lock"));
150  d->lock->setStaleLockTime(60 * 1000); // HARDCODE, 1 minute
151  }
152 
153  if (d->lock->isLocked() || d->lock->tryLock()) {
154  return true;
155  } else {
156  qCWarning(KCOREADDONS_DEBUG) << "Could not lock file:" << tempFile;
157  close();
158  }
159  }
160 
161  return false;
162 }
163 
164 static QUrl extractManagedFilePath(const QString &staleFileName)
165 {
166  // Warning, if we had a long path, it was truncated by tempFileName()
167  // So in that case, extractManagedFilePath will return an incorrect truncated path for original source
168  const QStringRef sep = staleFileName.rightRef(3);
169  int sepPos = staleFileName.indexOf(sep);
170  const QByteArray managedFilename = staleFileName.leftRef(sepPos).toLatin1();
171 
172  const int pathPos = staleFileName.indexOf(QChar::fromLatin1('_'), sepPos);
173  QUrl managedFileName;
174  // name.setScheme(file.mid(sepPos + 3, pathPos - sep.size() - 3));
175  const QByteArray encodedPath = staleFileName.midRef(pathPos + 1, staleFileName.length() - pathPos - 1 - KAutoSaveFilePrivate::NamePadding).toLatin1();
176  managedFileName.setPath(QUrl::fromPercentEncoding(encodedPath) + QLatin1Char('/') + QFileInfo(QUrl::fromPercentEncoding(managedFilename)).fileName());
177  return managedFileName;
178 }
179 
180 bool staleMatchesManaged(const QString &staleFileName, const QUrl &managedFile)
181 {
182  const QStringRef sep = staleFileName.rightRef(3);
183  int sepPos = staleFileName.indexOf(sep);
184  // Check filenames first
185  if (managedFile.fileName() != QUrl::fromPercentEncoding(staleFileName.leftRef(sepPos).toLatin1())) {
186  return false;
187  }
188  // Check paths
189  const int pathPos = staleFileName.indexOf(QChar::fromLatin1('_'), sepPos);
190  const QByteArray encodedPath = staleFileName.midRef(pathPos + 1, staleFileName.length() - pathPos - 1 - KAutoSaveFilePrivate::NamePadding).toLatin1();
191  return QUrl::toPercentEncoding(managedFile.path()).startsWith(encodedPath);
192 }
193 
194 QList<KAutoSaveFile *> KAutoSaveFile::staleFiles(const QUrl &filename, const QString &applicationName)
195 {
196  QString appName(applicationName);
197  if (appName.isEmpty()) {
199  }
200 
201  // get stale files
202  const QStringList files = findAllStales(appName);
203 
205 
206  // contruct a KAutoSaveFile for stale files corresponding given filename
207  for (const QString &file : files) {
208  if (file.endsWith(QLatin1String(".lock")) || (!filename.isEmpty() && !staleMatchesManaged(QFileInfo(file).fileName(), filename))) {
209  continue;
210  }
211 
212  // sets managedFile
213  KAutoSaveFile *asFile = new KAutoSaveFile(filename.isEmpty() ? extractManagedFilePath(file) : filename);
214  asFile->setFileName(file);
215  asFile->d->managedFileNameChanged = false; // do not regenerate tempfile name
216  list.append(asFile);
217  }
218 
219  return list;
220 }
221 
223 {
224  return staleFiles(QUrl(), applicationName);
225 }
226 
227 #include "moc_kautosavefile.cpp"
RemoveFilename
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
~KAutoSaveFile() override
Destroys the KAutoSaveFile object, removes the autosave file and drops the lock being held (if any)...
virtual void releaseLock()
Closes the autosave file resource and removes the lock file.
QString writableLocation(QStandardPaths::StandardLocation type)
static QList< KAutoSaveFile * > allStaleFiles(const QString &applicationName=QString())
Returns all stale autosave files left behind by crashed or otherwise gone instances of this applicati...
virtual QString fileName() const const override
void setFileName(const QString &name)
typedef OpenMode
QUrl managedFile() const
Retrieves the URL of the file managed by KAutoSaveFile.
bool isEmpty() const const
QStringList standardLocations(QStandardPaths::StandardLocation type)
void setPath(const QString &path, QUrl::ParsingMode mode)
void append(const T &value)
QStringRef leftRef(int n) const const
QChar fromLatin1(char c)
Creates and manages a temporary "auto-save" file.
bool isEmpty() const const
QString path(QUrl::ComponentFormattingOptions options) const const
QStringRef rightRef(int n) const const
void setManagedFile(const QUrl &filename)
Sets the URL of the file managed by KAutoSaveFile.
QCoreApplication * instance()
QString fromPercentEncoding(const QByteArray &input)
virtual bool open(QIODevice::OpenMode mode) override
QStringRef midRef(int position, int n) const const
virtual void close() override
QByteArray toLatin1() const const
QByteArray toPercentEncoding(const QString &input, const QByteArray &exclude, const QByteArray &include)
int length() const const
static QList< KAutoSaveFile * > staleFiles(const QUrl &url, const QString &applicationName=QString())
Checks for stale autosave files for the file url.
QString fromLatin1(const char *str, int size)
KAutoSaveFile(const QUrl &filename, QObject *parent=nullptr)
Constructs a KAutoSaveFile for file filename.
QObject * parent() const const
KCOREADDONS_EXPORT QString randomString(int length)
Generates a random string.
Definition: krandom.cpp:58
QString fileName(QUrl::ComponentFormattingOptions options) const const
bool open(OpenMode openmode) override
Opens the autosave file and locks it if it wasn&#39;t already locked.
KStandardDirs * dirs()
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Wed Apr 21 2021 23:01:58 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.