File indexing completed on 2024-05-19 05:16:11

0001 /*
0002  * SPDX-FileCopyrightText: 2013 Christian Mollekopf <mollekopf@kolabsys.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  *
0006  */
0007 
0008 #include "migratorbase.h"
0009 #include "migration_debug.h"
0010 #include <Akonadi/ServerManager>
0011 #include <KLocalizedString>
0012 #include <QCoreApplication>
0013 #include <QDateTime>
0014 #include <QDir>
0015 #include <QFileInfo>
0016 #include <QStandardPaths>
0017 
0018 static QString messageTypeToString(MigratorBase::MessageType type)
0019 {
0020     switch (type) {
0021     case MigratorBase::Success:
0022         return QStringLiteral("Success");
0023     case MigratorBase::Skip:
0024         return QStringLiteral("Skipped");
0025     case MigratorBase::Info:
0026         return QStringLiteral("Info   ");
0027     case MigratorBase::Warning:
0028         return QStringLiteral("WARNING");
0029     case MigratorBase::Error:
0030         return QStringLiteral("ERROR  ");
0031     }
0032     Q_ASSERT(false);
0033     return {};
0034 }
0035 
0036 static QMap<QString, MigratorBase::MigrationState> fillMigrationStateMapping()
0037 {
0038     QMap<QString, MigratorBase::MigrationState> map;
0039     map.insert(QStringLiteral("Complete"), MigratorBase::Complete);
0040     map.insert(QStringLiteral("Aborted"), MigratorBase::Aborted);
0041     map.insert(QStringLiteral("InProgress"), MigratorBase::InProgress);
0042     map.insert(QStringLiteral("Failed"), MigratorBase::Failed);
0043     return map;
0044 }
0045 
0046 static QMap<QString, MigratorBase::MigrationState> migrationStateMapping = fillMigrationStateMapping();
0047 
0048 static QString stateToIdentifier(MigratorBase::MigrationState state)
0049 {
0050     Q_ASSERT(migrationStateMapping.values().contains(state));
0051     return migrationStateMapping.key(state);
0052 }
0053 
0054 static MigratorBase::MigrationState identifierToState(const QString &identifier)
0055 {
0056     Q_ASSERT(migrationStateMapping.contains(identifier));
0057     return migrationStateMapping.value(identifier);
0058 }
0059 
0060 MigratorBase::MigratorBase(const QString &identifier, QObject *parent)
0061     : QObject(parent)
0062     , mIdentifier(identifier)
0063     , mConfig(new KConfig(Akonadi::ServerManager::addNamespace(QStringLiteral("akonadi-migrationrc"))))
0064 {
0065     const QString logFileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QCoreApplication::applicationName()
0066         + QLatin1Char('/') + identifier + QStringLiteral("migration.log");
0067     QFileInfo fileInfo(logFileName);
0068     QDir().mkpath(fileInfo.absolutePath());
0069     setLogfile(logFileName);
0070     connect(this, &MigratorBase::message, this, &MigratorBase::logMessage);
0071     loadState();
0072 }
0073 
0074 MigratorBase::MigratorBase(const QString &identifier, const QString &configFile, const QString &logFile, QObject *parent)
0075     : QObject(parent)
0076     , mIdentifier(identifier)
0077     , mMigrationState(None)
0078 {
0079     if (!configFile.isEmpty()) {
0080         mConfig.reset(new KConfig(configFile));
0081     }
0082     setLogfile(logFile);
0083     connect(this, &MigratorBase::message, this, &MigratorBase::logMessage);
0084     loadState();
0085 }
0086 
0087 MigratorBase::~MigratorBase() = default;
0088 
0089 void MigratorBase::setLogfile(const QString &logfile)
0090 {
0091     if (!logfile.isEmpty()) {
0092         mLogFile.reset(new QFile(logfile));
0093         if (!mLogFile->open(QFile::Append)) {
0094             mLogFile.reset();
0095             qCWarning(MIGRATION_LOG) << "Unable to open log file: " << logfile;
0096         }
0097     } else {
0098         mLogFile.reset();
0099     }
0100 }
0101 
0102 QString MigratorBase::identifier() const
0103 {
0104     return mIdentifier;
0105 }
0106 
0107 QString MigratorBase::displayName() const
0108 {
0109     return {};
0110 }
0111 
0112 QString MigratorBase::description() const
0113 {
0114     return {};
0115 }
0116 
0117 QString MigratorBase::logfile() const
0118 {
0119     if (mLogFile) {
0120         return mLogFile->fileName();
0121     }
0122     return {};
0123 }
0124 
0125 bool MigratorBase::canStart()
0126 {
0127     if (mIdentifier.isEmpty()) {
0128         Q_EMIT message(Error, i18n("Missing Identifier"));
0129         return false;
0130     }
0131     return true;
0132 }
0133 
0134 void MigratorBase::start()
0135 {
0136     if (mMigrationState == InProgress) {
0137         qCWarning(MIGRATION_LOG) << "already running";
0138         return;
0139     }
0140     if (!canStart()) {
0141         Q_EMIT message(Error, i18n("Failed to start migration because migrator is not ready"));
0142         Q_EMIT stoppedProcessing();
0143         return;
0144     }
0145     // TODO acquire dbus lock
0146     logMessage(Info, displayName());
0147     Q_EMIT message(Info, i18n("Starting migration..."));
0148     setMigrationState(InProgress);
0149     setProgress(0);
0150     startWork();
0151 }
0152 
0153 void MigratorBase::pause()
0154 {
0155     qCWarning(MIGRATION_LOG) << "pause is not implemented";
0156 }
0157 
0158 void MigratorBase::resume()
0159 {
0160     qCWarning(MIGRATION_LOG) << "resume is not implemented";
0161 }
0162 
0163 void MigratorBase::abort()
0164 {
0165     qCWarning(MIGRATION_LOG) << "abort is not implemented";
0166 }
0167 
0168 void MigratorBase::logMessage(MigratorBase::MessageType type, const QString &msg)
0169 {
0170     if (mLogFile) {
0171         mLogFile->write(QString(QLatin1Char('[') + QDateTime::currentDateTime().toString() + QStringLiteral("] ") + messageTypeToString(type)
0172                                 + QStringLiteral(": ") + msg + QLatin1Char('\n'))
0173                             .toUtf8());
0174         mLogFile->flush();
0175     }
0176 }
0177 
0178 bool MigratorBase::shouldAutostart() const
0179 {
0180     return false;
0181 }
0182 
0183 void MigratorBase::setMigrationState(MigratorBase::MigrationState state)
0184 {
0185     mMigrationState = state;
0186     switch (state) {
0187     case Complete:
0188         setProgress(100);
0189         Q_EMIT message(Success, i18n("Migration complete"));
0190         Q_EMIT stoppedProcessing();
0191         break;
0192     case Aborted:
0193         Q_EMIT message(Skip, i18n("Migration aborted"));
0194         Q_EMIT stoppedProcessing();
0195         break;
0196     case InProgress:
0197         break;
0198     case Failed:
0199         Q_EMIT message(Error, i18n("Migration failed"));
0200         Q_EMIT stoppedProcessing();
0201         break;
0202     case Paused:
0203         Q_EMIT message(Info, i18n("Migration paused"));
0204         Q_EMIT stateChanged(mMigrationState);
0205         return;
0206     default:
0207         qCWarning(MIGRATION_LOG) << "invalid state " << state;
0208         Q_ASSERT(false);
0209         return;
0210     }
0211     saveState();
0212     Q_EMIT stateChanged(mMigrationState);
0213 }
0214 
0215 MigratorBase::MigrationState MigratorBase::migrationState() const
0216 {
0217     return mMigrationState;
0218 }
0219 
0220 void MigratorBase::saveState()
0221 {
0222     config().writeEntry(QStringLiteral("MigrationState"), stateToIdentifier(mMigrationState));
0223 }
0224 
0225 void MigratorBase::loadState()
0226 {
0227     const QString state = config().readEntry(QStringLiteral("MigrationState"), QString());
0228     if (!state.isEmpty()) {
0229         mMigrationState = identifierToState(state);
0230     }
0231 
0232     if (mMigrationState == InProgress) {
0233         Q_EMIT message(Warning, i18n("This migration has already been started once but was aborted"));
0234         mMigrationState = NeedsUpdate;
0235     }
0236     switch (mMigrationState) {
0237     case Complete:
0238         mProgress = 100;
0239         break;
0240     default:
0241         mProgress = 0;
0242     }
0243 }
0244 
0245 NullableConfigGroup MigratorBase::config()
0246 {
0247     if (mConfig) {
0248         return NullableConfigGroup(mConfig->group(mIdentifier));
0249     }
0250     return {};
0251 }
0252 
0253 int MigratorBase::progress() const
0254 {
0255     return mProgress;
0256 }
0257 
0258 void MigratorBase::setProgress(int prog)
0259 {
0260     if (mProgress != prog) {
0261         mProgress = prog;
0262         Q_EMIT progress(prog);
0263     }
0264 }
0265 
0266 QString MigratorBase::status() const
0267 {
0268     switch (mMigrationState) {
0269     case None:
0270         return i18nc("@info:status", "Not started");
0271     case InProgress:
0272         return i18nc("@info:status", "Running...");
0273     case Complete:
0274         return i18nc("@info:status", "Complete");
0275     case Aborted:
0276         return i18nc("@info:status", "Aborted");
0277     case Paused:
0278         return i18nc("@info:status", "Paused");
0279     case NeedsUpdate:
0280         return i18nc("@info:status", "Needs Update");
0281     case Failed:
0282         return i18nc("@info:status", "Failed");
0283     }
0284     return {};
0285 }
0286 
0287 #include "moc_migratorbase.cpp"