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 <stdio.h> // 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 <QLatin1Char>
22 #include <QCoreApplication>
23 #include <QDir>
24 #include <QFile>
25 #include <QLockFile>
26 #include <QStandardPaths>
27 #include "krandom.h"
28 #include "kcoreaddons_debug.h"
29 
30 class KAutoSaveFilePrivate
31 {
32 public:
33  enum {NamePadding=8};
34 
35  KAutoSaveFilePrivate()
36  : lock(nullptr),
37  managedFileNameChanged(false)
38  {}
39 
40  QString tempFileName();
41  QUrl managedFile;
42  QLockFile *lock;
43  bool managedFileNameChanged;
44 };
45 
46 static QStringList findAllStales(const QString &appName)
47 {
48  const QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
49  QStringList files;
50 
51  for (const QString &dir : dirs) {
52  QDir appDir(dir + QLatin1String("/stalefiles/") + appName);
53  //qCDebug(KCOREADDONS_DEBUG) << "Looking in" << appDir.absolutePath();
54  const auto listFiles = appDir.entryList(QDir::Files);
55  for (const QString &file : listFiles) {
56  files << (appDir.absolutePath() + QLatin1Char('/') + file);
57  }
58  }
59  return files;
60 }
61 
62 QString KAutoSaveFilePrivate::tempFileName()
63 {
64  // Note: we drop any query string and user/pass info
65  const QString protocol(managedFile.scheme());
66  const QByteArray encodedDirectory = QUrl::toPercentEncoding(managedFile.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path());
67  const QString directory = QString::fromLatin1(encodedDirectory);
68  const QByteArray encodedFileName = QUrl::toPercentEncoding(managedFile.fileName());
69  QString fileName = QString::fromLatin1(encodedFileName);
70 
71  // Remove any part of the path to the right if it is longer than the maximum file name length;
72  // note that "file name" in this context means the file name component only (e.g. test.txt), and
73  // not the whole path (e.g. /home/simba/text.txt).
74  // Ensure that the max. file name length takes into account the other parts of the tempFileName
75  // Subtract 1 for the _ char, 3 for the padding separator, 5 is for the .lock,
76  // 7 for QLockFile's internal code (adding tmp .rmlock) = 16
77  const int pathLengthLimit = maxNameLength - NamePadding - fileName.size() - protocol.size() - 16;
78 
79  QString junk = KRandom::randomString(NamePadding);
80  // This is done so that the separation between the filename and path can be determined
81  fileName += junk.rightRef(3) + protocol + QLatin1Char('_') + directory.leftRef(pathLengthLimit) + junk;
82 
83  return fileName;
84 }
85 
86 KAutoSaveFile::KAutoSaveFile(const QUrl &filename, QObject *parent)
87  : QFile(parent),
88  d(new KAutoSaveFilePrivate)
89 {
90  setManagedFile(filename);
91 }
92 
94  : QFile(parent),
95  d(new KAutoSaveFilePrivate)
96 {
97 
98 }
99 
101 {
102  releaseLock();
103  delete d->lock;
104  delete d;
105 }
106 
108 {
109  return d->managedFile;
110 }
111 
112 void KAutoSaveFile::setManagedFile(const QUrl &filename)
113 {
114  releaseLock();
115 
116  d->managedFile = filename;
117  d->managedFileNameChanged = true;
118 }
119 
121 {
122  if (d->lock && d->lock->isLocked()) {
123  delete d->lock;
124  d->lock = nullptr;
125  if (!fileName().isEmpty()) {
126  remove();
127  }
128  }
129 }
130 
132 {
133  if (d->managedFile.isEmpty()) {
134  return false;
135  }
136 
137  QString tempFile;
138  if (d->managedFileNameChanged) {
139  QString staleFilesDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +
141  if (!QDir().mkpath(staleFilesDir)) {
142  return false;
143  }
144  tempFile = staleFilesDir + QChar::fromLatin1('/') + d->tempFileName();
145  } else {
146  tempFile = fileName();
147  }
148 
149  d->managedFileNameChanged = false;
150 
151  setFileName(tempFile);
152 
153  if (QFile::open(openmode)) {
154 
155  if (!d->lock)
156  {
157  d->lock = new QLockFile(tempFile + QLatin1String(".lock"));
158  d->lock->setStaleLockTime(60 * 1000); // HARDCODE, 1 minute
159  }
160 
161  if (d->lock->isLocked() || d->lock->tryLock()) {
162  return true;
163  } else {
164  qCWarning(KCOREADDONS_DEBUG)<<"Could not lock file:"<<tempFile;
165  close();
166  }
167  }
168 
169  return false;
170 }
171 
172 static QUrl extractManagedFilePath(const QString& staleFileName)
173 {
174  // Warning, if we had a long path, it was truncated by tempFileName()
175  // So in that case, extractManagedFilePath will return an incorrect truncated path for original source
176  const QStringRef sep = staleFileName.rightRef(3);
177  int sepPos = staleFileName.indexOf(sep);
178  const QByteArray managedFilename = staleFileName.leftRef(sepPos).toLatin1();
179 
180  const int pathPos = staleFileName.indexOf(QChar::fromLatin1('_'), sepPos);
181  QUrl managedFileName;
182  //name.setScheme(file.mid(sepPos + 3, pathPos - sep.size() - 3));
183  const QByteArray encodedPath = staleFileName.midRef(pathPos+1, staleFileName.length()-pathPos-1-KAutoSaveFilePrivate::NamePadding).toLatin1();
184  managedFileName.setPath(QUrl::fromPercentEncoding(encodedPath) + QLatin1Char('/') + QFileInfo(QUrl::fromPercentEncoding(managedFilename)).fileName());
185  return managedFileName;
186 }
187 
188 bool staleMatchesManaged(const QString& staleFileName, const QUrl &managedFile)
189 {
190  const QStringRef sep = staleFileName.rightRef(3);
191  int sepPos = staleFileName.indexOf(sep);
192  // Check filenames first
193  if (managedFile.fileName() != QUrl::fromPercentEncoding(staleFileName.leftRef(sepPos).toLatin1())) {
194  return false;
195  }
196  // Check paths
197  const int pathPos = staleFileName.indexOf(QChar::fromLatin1('_'), sepPos);
198  const QByteArray encodedPath = staleFileName.midRef(pathPos + 1, staleFileName.length() - pathPos - 1 - KAutoSaveFilePrivate::NamePadding).toLatin1();
199  return QUrl::toPercentEncoding(managedFile.path()).startsWith(encodedPath);
200 }
201 
202 QList<KAutoSaveFile *> KAutoSaveFile::staleFiles(const QUrl &filename, const QString &applicationName)
203 {
204  QString appName(applicationName);
205  if (appName.isEmpty()) {
207  }
208 
209  // get stale files
210  const QStringList files = findAllStales(appName);
211 
213 
214  // contruct a KAutoSaveFile for stale files corresponding given filename
215  for (const QString &file : files) {
216  if (file.endsWith(QLatin1String(".lock")) || (!filename.isEmpty() && !staleMatchesManaged(QFileInfo(file).fileName(), filename))) {
217  continue;
218  }
219 
220  // sets managedFile
221  KAutoSaveFile *asFile = new KAutoSaveFile(filename.isEmpty() ? extractManagedFilePath(file) : filename);
222  asFile->setFileName(file);
223  asFile->d->managedFileNameChanged = false; // do not regenerate tempfile name
224  list.append(asFile);
225  }
226 
227  return list;
228 }
229 
231 {
232  return staleFiles(QUrl(), applicationName);
233 }
234 
235 #include "moc_kautosavefile.cpp"
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:48
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-2020 The KDE developers.
Generated on Sat May 30 2020 23:10:51 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.