aboutsummaryrefslogtreecommitdiffstats
path: root/lang/qt/src/util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lang/qt/src/util.cpp')
-rw-r--r--lang/qt/src/util.cpp105
1 files changed, 105 insertions, 0 deletions
diff --git a/lang/qt/src/util.cpp b/lang/qt/src/util.cpp
index a9a70290..297c76a6 100644
--- a/lang/qt/src/util.cpp
+++ b/lang/qt/src/util.cpp
@@ -40,6 +40,8 @@
#include "qgpgme_debug.h"
#include <QFile>
+#include <QFileInfo>
+#include <QRandomGenerator>
#include <key.h>
@@ -76,3 +78,106 @@ void removeFile(const QString &fileName)
}
}
}
+
+/**
+ * Generates a string of random characters for the file names of temporary files.
+ * Never use this for generating passwords or similar use cases requiring highly
+ * secure random data.
+ */
+static QString getRandomCharacters(const int count)
+{
+ if (count < 0) {
+ return {};
+ }
+
+ QString randomChars;
+ randomChars.reserve(count);
+
+ do {
+ // get a 32-bit random number to generate up to 5 random characters from
+ // the set {A-Z, a-z, 0-9}; set the highest bit for the break condition
+ for (quint32 rnd = QRandomGenerator::global()->generate() | (1 << 31); rnd > 3; rnd = rnd >> 6)
+ {
+ // take the last 6 bits; ignore 62 and 63
+ const char ch = rnd & ((1 << 6) - 1);
+ if (ch < 26) {
+ randomChars += QLatin1Char(ch + 'A');
+ } else if (ch < 26 + 26) {
+ randomChars += QLatin1Char(ch - 26 + 'a');
+ } else if (ch < 26 + 26 + 10) {
+ randomChars += QLatin1Char(ch - 26 - 26 + '0');
+ }
+ if (randomChars.size() >= count) {
+ break;
+ }
+ }
+ } while (randomChars.size() < count);
+
+ return randomChars;
+}
+
+/**
+ * Creates a temporary file name with extension \c .part for the given file name
+ * \a fileName. The function makes sure that the created file name is not in use
+ * at the time the file name is chosen.
+ *
+ * Example: For the file name "this.is.an.archive.tar.gpg" the temporary file name
+ * "this.YHgf2tEl.is.an.archive.tar.gpg.part" could be returned.
+ */
+static QString createPartFileName(const QString &fileName)
+{
+ static const int maxAttempts = 10;
+
+ const QFileInfo fi{fileName};
+ const QString path = fi.path(); // path without trailing '/'
+ const QString baseName = fi.baseName();
+ const QString suffix = fi.completeSuffix();
+ for (int attempt = 0; attempt < maxAttempts; ++attempt) {
+ const QString candidate = (path + QLatin1Char('/')
+ + baseName + QLatin1Char('.')
+ + getRandomCharacters(8) + QLatin1Char('.')
+ + suffix
+ + QLatin1String(".part"));
+ if (!QFile::exists(candidate)) {
+ return candidate;
+ }
+ }
+
+ qCWarning(QGPGME_LOG) << __func__ << "- Failed to create temporary file name for" << fileName;
+ return {};
+}
+
+PartialFileGuard::PartialFileGuard(const QString &fileName)
+ : mFileName{fileName}
+ , mTempFileName{createPartFileName(fileName)}
+{
+ qCDebug(QGPGME_LOG) << __func__ << "- Using temporary file name" << mTempFileName;
+}
+
+PartialFileGuard::~PartialFileGuard()
+{
+ if (!mTempFileName.isEmpty()) {
+ removeFile(mTempFileName);
+ }
+}
+
+QString PartialFileGuard::tempFileName() const
+{
+ return mTempFileName;
+}
+
+bool PartialFileGuard::commit()
+{
+ if (mTempFileName.isEmpty()) {
+ qCWarning(QGPGME_LOG) << "PartialFileGuard::commit: Called more than once";
+ return false;
+ }
+ const bool success = QFile::rename(mTempFileName, mFileName);
+ if (success) {
+ qCDebug(QGPGME_LOG) << __func__ << "- Renamed" << mTempFileName << "to" << mFileName;
+ mTempFileName.clear();
+ } else {
+ qCDebug(QGPGME_LOG) << __func__ << "- Renaming" << mTempFileName << "to" << mFileName << "failed";
+ }
+ return success;
+}