KCoreAddons

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

KDE's Doxygen guidelines are available online.