File indexing completed on 2024-04-14 05:41:10

0001 /**
0002  * SPDX-FileCopyrightText: (C) 2003 by Sébastien Laoût <slaout@linux62.org>
0003  * SPDX-License-Identifier: GPL-2.0-or-later
0004  */
0005 
0006 #include "common.h"
0007 
0008 #include <QApplication>
0009 #include <QByteArray>
0010 #include <QFile>
0011 #include <QSaveFile>
0012 #include <QString>
0013 
0014 #include <KLocalizedString>
0015 
0016 #include "bnpview.h"
0017 #include "global.h"
0018 
0019 
0020 bool FileStorage::loadFromFile(const QString &fullPath, QString *string)
0021 {
0022     QByteArray array;
0023 
0024     if (loadFromFile(fullPath, &array)) {
0025         *string = QString::fromUtf8(array.data(), array.size());
0026         return true;
0027     } else
0028         return false;
0029 }
0030 
0031 
0032 bool FileStorage::loadFromFile(const QString &fullPath, QByteArray *array)
0033 {
0034     QFile file(fullPath);
0035     bool encrypted = false;
0036 
0037     if (file.open(QIODevice::ReadOnly)) {
0038         *array = file.readAll();
0039         const QByteArray magic = "-----BEGIN PGP MESSAGE-----";
0040         int i = 0;
0041 
0042         if (array->size() > magic.size())
0043             for (i = 0; array->at(i) == magic[i]; ++i)
0044                 ;
0045         if (i == magic.size()) {
0046             encrypted = true;
0047         }
0048         file.close();
0049 #ifdef HAVE_LIBGPGME
0050         if (encrypted) {
0051             QByteArray tmp(*array);
0052 
0053             tmp.detach();
0054             // Only use gpg-agent for private key encryption since it doesn't
0055             // cache password used in symmetric encryption.
0056             m_gpg->setUseGnuPGAgent(Settings::useGnuPGAgent() && m_encryptionType == PrivateKeyEncryption);
0057             if (m_encryptionType == PrivateKeyEncryption)
0058                 m_gpg->setText(i18n("Please enter the password for the following private key:"), false);
0059             else
0060                 m_gpg->setText(i18n("Please enter the password for the basket <b>%1</b>:", basketName()), false); // Used when decrypting
0061             return m_gpg->decrypt(tmp, array);
0062         }
0063 #else
0064         if (encrypted) {
0065             return false;
0066         }
0067 #endif
0068         return true;
0069     } else
0070         return false;
0071 }
0072 
0073 bool FileStorage::saveToFile(const QString &fullPath, const QString &string, bool isEncrypted)
0074 {
0075     const QByteArray array = string.toUtf8();
0076     return saveToFile(fullPath, array, isEncrypted);
0077 }
0078 
0079 bool FileStorage::saveToFile(const QString &fullPath, const QByteArray &array, bool isEncrypted)
0080 {
0081     ulong length = array.size();
0082 
0083     bool success = true;
0084     QByteArray tmp;
0085 
0086 #ifdef HAVE_LIBGPGME
0087     if (isEncrypted) {
0088         QString key;
0089 
0090         // We only use gpg-agent for private key encryption and saving without
0091         // public key doesn't need one.
0092         m_gpg->setUseGnuPGAgent(false);
0093         if (m_encryptionType == PrivateKeyEncryption) {
0094             key = m_encryptionKey;
0095             // public key doesn't need password
0096             m_gpg->setText(QString(), false);
0097         } else
0098             m_gpg->setText(i18n("Please assign a password to the basket <b>%1</b>:", basketName()), true); // Used when defining a new password
0099 
0100         success = m_gpg->encrypt(array, length, &tmp, key);
0101         length = tmp.size();
0102     } else
0103         tmp = array;
0104 
0105 #else
0106     success = !isEncrypted;
0107     if (success)
0108         tmp = array;
0109 #endif
0110     /*if (success && (success = file.open(QIODevice::WriteOnly))){
0111         success = (file.write(tmp) == (Q_LONG)tmp.size());
0112         file.close();
0113     }*/
0114 
0115     if (success)
0116         return safelySaveToFile(fullPath, tmp, length);
0117     else
0118         return false;
0119 }
0120 
0121 /**
0122  * A safer version of saveToFile, that doesn't perform encryption.  To save a
0123  * file owned by a basket (i.e. a basket or a note file), use saveToFile(), but
0124  * to save to another file, (e.g. the basket hierarchy), use this function
0125  * instead.
0126  */
0127 bool FileStorage::safelySaveToFile(const QString &fullPath, const QByteArray &array, unsigned long length)
0128 {
0129     // Modulus operandi:
0130     // 1. Use QSaveFile to try and save the file
0131     // 2. Show a modal dialog (with the error) when bad things happen
0132     // 3. We keep trying (at increasing intervals, up until every minute)
0133     //    until we finally save the file.
0134 
0135     // The error dialog is static to make sure we never show the dialog twice,
0136     static const uint maxDelay = 60 * 1000; // ms
0137     uint retryDelay = 1000;                 // ms
0138     bool success = false;
0139     do {
0140         QSaveFile saveFile(fullPath);
0141         if (saveFile.open(QIODevice::WriteOnly)) {
0142             saveFile.write(array, length);
0143             if (saveFile.commit())
0144                 success = true;
0145         }
0146 
0147         if (!success) {
0148             Q_EMIT Global::bnpView->showErrorMessage(i18n("Error while saving: ") + saveFile.errorString());
0149 
0150             static const uint sleepDelay = 50; // ms
0151             for (uint i = 0; i < retryDelay / sleepDelay; ++i) {
0152                 qApp->processEvents();
0153             }
0154 
0155             // Double the retry delay, but don't go over the max.
0156             retryDelay = qMin(maxDelay, retryDelay * 2); // ms
0157         }
0158     } while (!success);
0159 
0160     return true; // Guess we can't really return a fail
0161 }
0162 
0163 bool FileStorage::safelySaveToFile(const QString &fullPath, const QString &string)
0164 {
0165     QByteArray bytes = string.toUtf8();
0166     return safelySaveToFile(fullPath, bytes, bytes.length());
0167 }