File indexing completed on 2025-01-05 03:54:13

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-11-14
0007  * Description : Mysql internal database server
0008  *
0009  * SPDX-FileCopyrightText: 2009-2011 by Holger Foerster <Hamsi2k at freenet dot de>
0010  * SPDX-FileCopyrightText: 2010-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText: 2016      by Swati Lodha <swatilodha27 at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "databaseserver.h"
0018 
0019 // Qt includes
0020 
0021 #include <QProgressDialog>
0022 #include <QStandardPaths>
0023 #include <QApplication>
0024 #include <QTcpServer>
0025 #include <QDateTime>
0026 #include <QFileInfo>
0027 #include <QFile>
0028 #include <QSqlDatabase>
0029 #include <QSqlQuery>
0030 #include <QSqlError>
0031 #include <QtGlobal>
0032 #include <QPointer>
0033 #include <QDir>
0034 
0035 // KDE includes
0036 
0037 #include <klocalizedstring.h>
0038 
0039 // Local includes
0040 
0041 #include "digikam_debug.h"
0042 #include "digikam_globals.h"
0043 
0044 namespace Digikam
0045 {
0046 
0047 class Q_DECL_HIDDEN DatabaseServer::Private
0048 {
0049 public:
0050 
0051     explicit Private()
0052         : databaseProcess(nullptr),
0053           serverPort(3307)
0054     {
0055     }
0056 
0057     DbEngineParameters     params;
0058     QProcess*              databaseProcess;
0059 
0060     QString                internalDBName;
0061     QString                mysqlUpgradePath;
0062     QString                mysqlServerPath;
0063     QString                mysqlAdminPath;
0064     QString                mysqlInitPath;
0065     QString                dataDir;
0066     QString                miscDir;
0067     QString                fileDataDir;
0068     QString                actualConfig;
0069     QString                globalConfig;
0070 
0071     int                    serverPort;
0072 };
0073 
0074 DatabaseServer::DatabaseServer(const DbEngineParameters& params, DatabaseServerStarter* const parent)
0075     : QThread(parent),
0076       d      (new Private)
0077 {
0078     d->params = params;
0079 
0080     qCDebug(DIGIKAM_DATABASESERVER_LOG) << d->params;
0081 
0082     QString defaultAkDir = DbEngineParameters::serverPrivatePath();
0083     QString dataDir;
0084 
0085     if (d->params.internalServerPath().isEmpty())
0086     {
0087         dataDir = QDir(defaultAkDir).absoluteFilePath(QLatin1String("db_data"));
0088         qCDebug(DIGIKAM_DATABASESERVER_LOG) << "No internal server data path is given, we will use the default." << dataDir;
0089     }
0090     else
0091     {
0092         dataDir = QDir(d->params.internalServerPath()).absoluteFilePath(QLatin1String(".mysql.digikam/db_data"));
0093     }
0094 
0095     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Internal Server data path:" << dataDir;
0096 
0097     d->internalDBName       = QLatin1String("digikam");
0098     d->mysqlUpgradePath     = d->params.internalServerMysqlUpgradeCmd;
0099     d->mysqlServerPath      = d->params.internalServerMysqlServerCmd;
0100     d->mysqlAdminPath       = d->params.internalServerMysqlAdminCmd;
0101     d->mysqlInitPath        = d->params.internalServerMysqlInitCmd;
0102     d->dataDir              = dataDir;
0103     d->miscDir              = QDir(defaultAkDir).absoluteFilePath(QLatin1String("db_misc"));
0104     d->fileDataDir          = QDir(defaultAkDir).absoluteFilePath(QLatin1String("file_db_data"));
0105     d->actualConfig         = QDir(defaultAkDir).absoluteFilePath(QLatin1String("mysql.conf"));
0106     d->globalConfig         = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
0107                                                      QLatin1String("digikam/database/mysql-global.conf"));
0108     databaseServerStateEnum = started;
0109 }
0110 
0111 DatabaseServer::~DatabaseServer()
0112 {
0113     delete d;
0114 }
0115 
0116 void DatabaseServer::run()
0117 {
0118     quint64 runningTime = 0;
0119     int debugTime       = 0;
0120     int waitTime        = 1;
0121 
0122     // Loop to wait for stopping the server.
0123 
0124     do
0125     {
0126         if (!debugTime)
0127         {
0128             qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Running" << runningTime << "seconds...";
0129             debugTime = 30;
0130         }
0131 
0132         QThread::sleep(waitTime);
0133         ++runningTime;
0134         --debugTime;
0135     }
0136     while (databaseServerStateEnum != stopped);
0137 
0138     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Shutting down database server";
0139     Q_EMIT done();
0140 }
0141 
0142 DatabaseServerError DatabaseServer::startDatabaseProcess()
0143 {
0144     DatabaseServerError error;
0145 
0146     if (d->params.isMySQL())
0147     {
0148         error = startMysqlDatabaseProcess();
0149     }
0150     else
0151     {
0152         error = DatabaseServerError(DatabaseServerError::NotSupported,
0153                                     i18n("Database type is not supported."));
0154     }
0155 
0156     if      (error.getErrorType() == DatabaseServerError::StartError)
0157     {
0158         databaseServerStateEnum = notRunning;
0159     }
0160     else if (error.getErrorType() == DatabaseServerError::NotSupported)
0161     {
0162         qCDebug(DIGIKAM_DATABASESERVER_LOG) << "This database type is not supported.";
0163         databaseServerStateEnum = notRunning;
0164     }
0165     else
0166     {
0167         databaseServerStateEnum = running;
0168     }
0169 
0170     return error;
0171 }
0172 
0173 void DatabaseServer::stopDatabaseProcess()
0174 {
0175     if (!d->databaseProcess)
0176     {
0177         return;
0178     }
0179 
0180     QStringList mysqlShutDownArgs;
0181     mysqlShutDownArgs << QLatin1String("-u");
0182     mysqlShutDownArgs << QLatin1String("root");
0183     mysqlShutDownArgs << QLatin1String("shutdown");
0184 
0185 #ifdef Q_OS_WIN
0186 
0187     mysqlShutDownArgs << QString::fromLatin1("--port=%1").arg(d->serverPort);
0188 
0189 #else
0190 
0191     mysqlShutDownArgs << QString::fromLatin1("--socket=%1/mysql.socket").arg(d->miscDir);
0192 
0193 #endif
0194 
0195     QProcess mysqlShutDownProcess;
0196     mysqlShutDownProcess.setProcessEnvironment(adjustedEnvironmentForAppImage());
0197 
0198     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Send stop to database server";
0199 
0200     mysqlShutDownProcess.start(d->mysqlAdminPath, mysqlShutDownArgs);
0201     mysqlShutDownProcess.waitForFinished();
0202 
0203     if (!d->databaseProcess->waitForFinished() && (d->databaseProcess->state() == QProcess::Running))
0204     {
0205         qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Database process will be killed now";
0206         d->databaseProcess->kill();
0207         d->databaseProcess->waitForFinished();
0208     }
0209 
0210     delete d->databaseProcess;
0211     d->databaseProcess      = nullptr;
0212     databaseServerStateEnum = stopped;
0213 
0214     wait();
0215 }
0216 
0217 bool DatabaseServer::isRunning() const
0218 {
0219     if (!d->databaseProcess)
0220     {
0221         return false;
0222     }
0223 
0224     return (databaseServerStateEnum == running);
0225 }
0226 
0227 DatabaseServerError DatabaseServer::startMysqlDatabaseProcess()
0228 {
0229     DatabaseServerError error = checkDatabaseDirs();
0230 
0231     if (error.getErrorType() != DatabaseServerError::NoErrors)
0232     {
0233         return error;
0234     }
0235 
0236     error = initMysqlConfig();
0237 
0238     if (error.getErrorType() != DatabaseServerError::NoErrors)
0239     {
0240         return error;
0241     }
0242 
0243     copyAndRemoveMysqlLogs();
0244 
0245     error = createMysqlFiles();
0246 
0247     if (error.getErrorType() != DatabaseServerError::NoErrors)
0248     {
0249         return error;
0250     }
0251 
0252     error = startMysqlServer();
0253 
0254     if (error.getErrorType() != DatabaseServerError::NoErrors)
0255     {
0256         return error;
0257     }
0258 
0259     error = initMysqlDatabase(false);
0260 
0261     if (error.getErrorType() != DatabaseServerError::NoErrors)
0262     {
0263         return error;
0264     }
0265 
0266     error = upgradeMysqlDatabase();
0267 
0268     if (error.getErrorType() != DatabaseServerError::NoErrors)
0269     {
0270         return error;
0271     }
0272 
0273     error = initMysqlDatabase(true);
0274 
0275     if (error.getErrorType() != DatabaseServerError::NoErrors)
0276     {
0277         return error;
0278     }
0279 
0280     databaseServerStateEnum = running;
0281 
0282     return error;
0283 }
0284 
0285 DatabaseServerError DatabaseServer::checkDatabaseDirs() const
0286 {
0287     DatabaseServerError error;
0288 
0289     if (d->mysqlUpgradePath.isEmpty())
0290     {
0291         qCDebug(DIGIKAM_DATABASESERVER_LOG) << "No path to mysql upgrade command set in configuration file!";
0292 
0293         return DatabaseServerError(DatabaseServerError::StartError,
0294                                    i18n("No path to mysql upgrade command set "
0295                                         "in configuration file."));
0296     }
0297 
0298     if (d->mysqlServerPath.isEmpty())
0299     {
0300         qCDebug(DIGIKAM_DATABASESERVER_LOG) << "No path to mysql server command set in configuration file!";
0301 
0302         return DatabaseServerError(DatabaseServerError::StartError,
0303                                    i18n("No path to mysql server command set "
0304                                         "in configuration file."));
0305     }
0306 
0307     if (d->mysqlAdminPath.isEmpty())
0308     {
0309         qCDebug(DIGIKAM_DATABASESERVER_LOG) << "No path to mysql administration command set in configuration file!";
0310 
0311         return DatabaseServerError(DatabaseServerError::StartError,
0312                                    i18n("No path to mysql administration "
0313                                         "command set in configuration file."));
0314     }
0315 
0316     if (d->mysqlInitPath.isEmpty())
0317     {
0318         qCDebug(DIGIKAM_DATABASESERVER_LOG) << "No path to mysql initialization command set in configuration file!";
0319 
0320         return DatabaseServerError(DatabaseServerError::StartError,
0321                                    i18n("No path to mysql initialization "
0322                                         "command set in configuration file."));
0323     }
0324 
0325     if (!QFile::exists(d->dataDir) && !QDir().mkpath(d->dataDir))
0326     {
0327         qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Cannot create directory "
0328                                             << d->dataDir;
0329 
0330         return DatabaseServerError(DatabaseServerError::StartError,
0331                                    i18n("Cannot create directory %1",
0332                                         QDir::toNativeSeparators(d->dataDir)));
0333     }
0334 
0335     if (!QFile::exists(d->miscDir) && !QDir().mkpath(d->miscDir))
0336     {
0337         qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Cannot create directory "
0338                                             << d->miscDir;
0339 
0340         return DatabaseServerError(DatabaseServerError::StartError,
0341                                    i18n("Cannot create directory %1",
0342                                         QDir::toNativeSeparators(d->miscDir)));
0343     }
0344 
0345     if (!QFile::exists(d->fileDataDir) && !QDir().mkpath(d->fileDataDir))
0346     {
0347         qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Cannot create directory "
0348                                             << d->fileDataDir;
0349 
0350         return DatabaseServerError(DatabaseServerError::StartError,
0351                                    i18n("Cannot create directory %1",
0352                                         QDir::toNativeSeparators(d->fileDataDir)));
0353     }
0354 
0355     return error;
0356 }
0357 
0358 DatabaseServerError DatabaseServer::initMysqlConfig() const
0359 {
0360     DatabaseServerError error;
0361 
0362     QString localConfig = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
0363                                                  QLatin1String("digikam/database/mysql-local.conf"));
0364 
0365     if (!d->globalConfig.isEmpty())
0366     {
0367         bool confUpdate       = false;
0368         bool confShouldUpdate = false;
0369         QFile actualFile(d->actualConfig);
0370 
0371         // Update actualconf only if either global or local is newer than actual
0372         // If actual does not yet exist it was initialised with datetime 0
0373         // so it will get updated too
0374 
0375         if ((QFileInfo(d->globalConfig).lastModified() > QFileInfo(actualFile).lastModified()) ||
0376             (QFileInfo(localConfig).lastModified()     > QFileInfo(actualFile).lastModified()))
0377         {
0378             confShouldUpdate = true;
0379 
0380             qCDebug(DIGIKAM_DATABASESERVER_LOG) << "The mysql configuration is outdated,"
0381                                                 << d->actualConfig
0382                                                 << "will be updated.";
0383 
0384             QFile globalFile(d->globalConfig);
0385             QFile localFile(localConfig);
0386 
0387             if (globalFile.open(QFile::ReadOnly) && actualFile.open(QFile::WriteOnly))
0388             {
0389                 actualFile.write(globalFile.readAll());
0390                 qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Updated mysql configuration with" << d->globalConfig;
0391 
0392                 if (!localConfig.isEmpty() && localFile.open(QFile::ReadOnly))
0393                 {
0394                     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Updated mysql configuration with" << localConfig;
0395                     actualFile.write(localFile.readAll());
0396                     localFile.close();
0397                 }
0398 
0399                 globalFile.close();
0400                 actualFile.close();
0401 
0402                 confUpdate = true;
0403             }
0404         }
0405 
0406         // MySQL doesn't like world writeable config files (which makes sense), but
0407         // our config file somehow ends up being world-writable on some systems for no
0408         // apparent reason nevertheless, so fix that
0409 
0410         if      (confUpdate)
0411         {
0412             const QFile::Permissions allowedPerms = actualFile.permissions() &
0413                                                     (QFile::ReadOwner | QFile::WriteOwner |
0414                                                      QFile::ReadGroup | QFile::WriteGroup | QFile::ReadOther);
0415 
0416             if (allowedPerms != actualFile.permissions())
0417             {
0418                 actualFile.setPermissions(allowedPerms);
0419                 qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Fixed permissions of mysql configuration file.";
0420             }
0421         }
0422         else if (confShouldUpdate)
0423         {
0424             qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Unable to create MySQL server configuration file."
0425                                                 << "This means that either the default configuration file"
0426                                                 << d->globalConfig
0427                                                 << "was not readable or the target file"
0428                                                 << d->actualConfig
0429                                                 << "could not be written.";
0430 
0431             QString errorMsg = i18n("Unable to create MySQL server configuration file."
0432                                     "<p>This means that either the default configuration file</p>"
0433                                     "<p>%1</p>"
0434                                     "<p>was not readable or the target file</p>"
0435                                     "<p>%2</p>"
0436                                     "<p>could not be written.</p>",
0437                                     d->globalConfig,
0438                                     d->actualConfig);
0439 
0440             error = DatabaseServerError(DatabaseServerError::StartError, errorMsg);
0441         }
0442         else
0443         {
0444             qCDebug(DIGIKAM_DATABASESERVER_LOG) << "The mysql configuration was already up-to-date:"
0445                                                 << d->actualConfig;
0446         }
0447     }
0448     else
0449     {
0450         qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Cannot find MySQL server default configuration (mysql-global.conf)";
0451 
0452         error = DatabaseServerError(DatabaseServerError::StartError,
0453                                     i18n("Cannot find MySQL server default "
0454                                          "configuration (mysql-global.conf)."));
0455     }
0456 
0457     return error;
0458 }
0459 
0460 void DatabaseServer::copyAndRemoveMysqlLogs() const
0461 {
0462     // Move mysql error log file out of the way
0463 
0464     const QFileInfo errorLog(d->dataDir,
0465                              QLatin1String("mysql.err"));
0466 
0467     if (errorLog.exists())
0468     {
0469         QFile logFile(errorLog.absoluteFilePath());
0470         QFile oldLogFile(QDir(d->dataDir).absoluteFilePath(QLatin1String("mysql.err.old")));
0471 
0472         if (oldLogFile.exists() && (oldLogFile.size() > (100 * 1024 * 1024)))
0473         {
0474             oldLogFile.remove();
0475         }
0476 
0477         if (logFile.open(QFile::ReadOnly) && oldLogFile.open(QFile::Append))
0478         {
0479             QByteArray ba = logFile.readAll();
0480             oldLogFile.write(ba);
0481             oldLogFile.close();
0482             logFile.close();
0483             logFile.remove();
0484         }
0485         else
0486         {
0487             qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Failed to open MySQL error log.";
0488         }
0489     }
0490 }
0491 
0492 DatabaseServerError DatabaseServer::createMysqlFiles() const
0493 {
0494     DatabaseServerError error;
0495 
0496     // Initialize the database
0497 
0498     if (!QFile(QDir(d->dataDir).absoluteFilePath(QLatin1String("mysql"))).exists())
0499     {
0500         QPointer<QProgressDialog> dialog = new QProgressDialog;
0501         dialog->setLabelText(i18n("The internal MySQL database is "
0502                                   "initializing, please wait..."));
0503         dialog->setCancelButton(nullptr);
0504         dialog->setMinimumDuration(2000);
0505         dialog->setModal(true);
0506         dialog->setMinimum(0);
0507         dialog->setMaximum(0);
0508 
0509         // Synthesize the server initialization command line arguments
0510 
0511         QStringList mysqlInitCmdArgs;
0512 
0513 #ifndef Q_OS_WIN
0514 
0515         mysqlInitCmdArgs << QDir::toNativeSeparators(QString::fromLatin1("--defaults-file=%1")
0516                                                      .arg(d->globalConfig));
0517 
0518 #endif
0519 
0520 #ifdef Q_OS_MACOS
0521 
0522         mysqlInitCmdArgs << QDir::toNativeSeparators(QString::fromLatin1("--basedir=%1/lib/mariadb/")
0523                                                      .arg(macOSBundlePrefix()));
0524 
0525 #endif
0526 
0527         mysqlInitCmdArgs << QDir::toNativeSeparators(QString::fromLatin1("--datadir=%1")
0528                                                      .arg(d->dataDir));
0529 
0530         QProcess initProcess;
0531         initProcess.setProcessEnvironment(adjustedEnvironmentForAppImage());
0532         initProcess.start(d->mysqlInitPath, mysqlInitCmdArgs);
0533 
0534         qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Database initializer:"
0535                                             << initProcess.program()
0536                                             << initProcess.arguments();
0537 
0538         while (!initProcess.waitForFinished(250))
0539         {
0540             qApp->processEvents();
0541         }
0542 
0543         delete dialog;
0544 
0545         if (initProcess.exitCode() != 0)
0546         {
0547             error = DatabaseServerError(DatabaseServerError::StartError,
0548                                         processErrorLog(&initProcess,
0549                                                         i18n("Could not start database initializer.")));
0550         }
0551     }
0552 
0553     return error;
0554 }
0555 
0556 DatabaseServerError DatabaseServer::startMysqlServer()
0557 {
0558     DatabaseServerError error;
0559 
0560     // Synthesize the server command line arguments
0561 
0562     QStringList mysqldServCmdArgs;
0563     mysqldServCmdArgs << QDir::toNativeSeparators(QString::fromLatin1("--defaults-file=%1").arg(d->actualConfig))
0564                       << QDir::toNativeSeparators(QString::fromLatin1("--datadir=%1").arg(d->dataDir));
0565 
0566 #ifdef Q_OS_MACOS
0567 
0568     mysqldServCmdArgs << QDir::toNativeSeparators(QString::fromLatin1("--basedir=%1/lib/mariadb/")
0569                                                   .arg(macOSBundlePrefix()));
0570 
0571 #endif
0572 
0573 #ifdef Q_OS_WIN
0574 
0575     QTcpServer* const server = new QTcpServer();
0576 
0577     if (!server->listen(QHostAddress::LocalHost, d->serverPort))
0578     {
0579         qCWarning(DIGIKAM_DATABASESERVER_LOG) << "Port 3307 not free for the MySQL server";
0580 
0581         server->listen(QHostAddress::LocalHost, 0);
0582         d->serverPort = server->serverPort();
0583 
0584         qCWarning(DIGIKAM_DATABASESERVER_LOG) << "Now use the free port:" << d->serverPort;
0585     }
0586 
0587     server->close();
0588     delete server;
0589 
0590     mysqldServCmdArgs << QLatin1String("--skip-networking=0")
0591                       << QString::fromLatin1("--port=%1").arg(d->serverPort);
0592 
0593 #else
0594 
0595     mysqldServCmdArgs << QString::fromLatin1("--socket=%1/mysql.socket").arg(d->miscDir);
0596 
0597 #endif
0598 
0599     // Start the database server
0600 
0601     d->databaseProcess = new QProcess();
0602     d->databaseProcess->setProcessEnvironment(adjustedEnvironmentForAppImage());
0603     d->databaseProcess->start(d->mysqlServerPath, mysqldServCmdArgs);
0604 
0605     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Database server:"
0606                                         << d->databaseProcess->program()
0607                                         << d->databaseProcess->arguments();
0608 
0609     if (!d->databaseProcess->waitForStarted() || (d->databaseProcess->exitCode() != 0))
0610     {
0611         QString errorMsg = processErrorLog(d->databaseProcess,
0612                                            i18n("Could not start database server."));
0613 
0614         delete d->databaseProcess;
0615         d->databaseProcess = nullptr;
0616 
0617         error = DatabaseServerError(DatabaseServerError::StartError, errorMsg);
0618     }
0619 
0620     return error;
0621 }
0622 
0623 DatabaseServerError DatabaseServer::initMysqlDatabase(bool useDatabase) const
0624 {
0625     DatabaseServerError error;
0626 
0627     const QLatin1String initCon("initConnection");
0628 
0629     {
0630         QSqlDatabase db = QSqlDatabase::addDatabase(DbEngineParameters::MySQLDatabaseType(), initCon);
0631 
0632 #ifdef Q_OS_WIN
0633 
0634         db.setHostName(QLatin1String("localhost"));
0635         db.setPort(d->serverPort);
0636 
0637 #else
0638 
0639         db.setConnectOptions(QString::fromLatin1("UNIX_SOCKET=%1/mysql.socket").arg(d->miscDir));
0640 
0641 #endif
0642 
0643         db.setUserName(QLatin1String("root"));
0644 
0645         // might not exist yet, then connecting to the actual db will fail
0646 
0647         db.setDatabaseName(QString());
0648 
0649         if (!db.isValid())
0650         {
0651             qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Invalid database object during database server startup";
0652 
0653             return DatabaseServerError(DatabaseServerError::StartError,
0654                                        i18n("Invalid database object during database "
0655                                             "server startup"));
0656         }
0657 
0658         bool opened = false;
0659         bool exited = false;
0660 
0661         for (int i = 0 ; i < 120 ; ++i)
0662         {
0663             opened = db.open();
0664 
0665             if (opened)
0666             {
0667                 break;
0668             }
0669 
0670             if (d->databaseProcess && d->databaseProcess->waitForFinished(500))
0671             {
0672                 exited = true;
0673                 break;
0674             }
0675         }
0676 
0677         if (!opened)
0678         {
0679             QString firstLine;
0680             QString errorMsg;
0681 
0682             if (exited)
0683             {
0684                 errorMsg = processErrorLog(d->databaseProcess,
0685                                            i18n("Database process exited unexpectedly "
0686                                                 "during initial connection."));
0687             }
0688             else
0689             {
0690                 errorMsg = processErrorLog(d->databaseProcess,
0691                                            i18n("Could not connect to Database after "
0692                                                 "trying for 60 seconds."));
0693             }
0694 
0695             return DatabaseServerError(DatabaseServerError::StartError, errorMsg);
0696         }
0697 
0698         if (useDatabase)
0699         {
0700             QSqlQuery query(db);
0701 
0702             if (!query.exec(QString::fromLatin1("USE %1;").arg(d->internalDBName)))
0703             {
0704                 qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Failed to use database"
0705                                                     << d->internalDBName;
0706                 qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Query error:"
0707                                                     << query.lastError().text();
0708                 qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Database error:"
0709                                                     << db.lastError().text();
0710                 qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Trying to create database now";
0711 
0712                 if (query.exec(QString::fromLatin1("CREATE DATABASE %1;").arg(d->internalDBName)))
0713                 {
0714                     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Database was successfully created";
0715                 }
0716                 else
0717                 {
0718                     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Failed to create database";
0719                     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Query error:"
0720                                                         << query.lastError().text();
0721                     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Database error:"
0722                                                         << db.lastError().text();
0723 
0724                     QString  errorMsg = i18n("Failed to create database"
0725                                              "<p>Query error: %1</p>"
0726                                              "<p>Database error: %2</p>",
0727                                              query.lastError().text(),
0728                                              db.lastError().text());
0729 
0730                     error = DatabaseServerError(DatabaseServerError::StartError, errorMsg);
0731                 }
0732             }
0733         }
0734 
0735         // Make sure query is destroyed before we close the db
0736 
0737         db.close();
0738     }
0739 
0740     QSqlDatabase::removeDatabase(initCon);
0741 
0742     return error;
0743 }
0744 
0745 DatabaseServerError DatabaseServer::upgradeMysqlDatabase()
0746 {
0747     QPointer<QProgressDialog> dialog = new QProgressDialog;
0748     dialog->setLabelText(i18n("A MySQL database upgrade is "
0749                               "in progress, please wait..."));
0750     dialog->setCancelButton(nullptr);
0751     dialog->setMinimumDuration(5000);
0752     dialog->setModal(true);
0753     dialog->setMinimum(0);
0754     dialog->setMaximum(0);
0755 
0756     DatabaseServerError error;
0757 
0758     // Synthesize the mysql upgrade command line arguments
0759 
0760     QStringList upgradeCmdArgs;
0761 
0762 #ifdef Q_OS_WIN
0763 
0764     upgradeCmdArgs << QString::fromLatin1("--port=%1").arg(d->serverPort);
0765 
0766 #else
0767 
0768     upgradeCmdArgs << QString::fromLatin1("--socket=%1/mysql.socket").arg(d->miscDir);
0769 
0770 #endif
0771 
0772     // Start the upgrade program
0773 
0774     QProcess upgradeProcess;
0775     upgradeProcess.setProcessEnvironment(adjustedEnvironmentForAppImage());
0776     upgradeProcess.start(d->mysqlUpgradePath, upgradeCmdArgs);
0777 
0778     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Upgrade database:"
0779                                         << upgradeProcess.program()
0780                                         << upgradeProcess.arguments();
0781 
0782     while (!upgradeProcess.waitForFinished(250))
0783     {
0784         qApp->processEvents();
0785     }
0786 
0787     delete dialog;
0788 
0789     if (upgradeProcess.exitCode() != 0)
0790     {
0791         QString errorMsg = processErrorLog(&upgradeProcess,
0792                                            i18n("Could not upgrade database."));
0793 
0794         error = DatabaseServerError(DatabaseServerError::StartError, errorMsg);
0795     }
0796 
0797     return error;
0798 }
0799 
0800 QString DatabaseServer::getcurrentAccountUserName() const
0801 {
0802     QString name = QString::fromUtf8(qgetenv("USER"));   // Linux and OSX
0803 
0804     if (name.isEmpty())
0805     {
0806         name = QString::fromUtf8(qgetenv("USERNAME"));   // Windows
0807     }
0808 
0809     return name;
0810 }
0811 
0812 QString DatabaseServer::processErrorLog(QProcess* const process, const QString& msg) const
0813 {
0814     qCDebug(DIGIKAM_DATABASESERVER_LOG) << msg;
0815     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Executable:"
0816                                         << process->program();
0817     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Arguments:"
0818                                         << process->arguments().join(QLatin1String(", "));
0819     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Stdout:"
0820                                         << process->readAllStandardOutput();
0821     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Stderr:"
0822                                         << process->readAllStandardError();
0823     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Exit code:"
0824                                         << process->exitCode();
0825     qCDebug(DIGIKAM_DATABASESERVER_LOG) << "Process error:"
0826                                         << process->errorString();
0827 
0828     return i18n("%1"
0829                 "<p>Executable: %2</p>"
0830                 "<p>Arguments: %3</p>"
0831                 "<p>Process error: %4</p>",
0832                 msg,
0833                 process->program(),
0834                 process->arguments().join(QLatin1String(", ")),
0835                 process->errorString());
0836 }
0837 
0838 } // namespace Digikam
0839 
0840 #include "moc_databaseserver.cpp"