File indexing completed on 2024-12-22 05:16:01

0001 /*
0002     localegenhelper.cpp
0003     SPDX-FileCopyrightText: 2021 Han Young <hanyoung@protonmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 #include "localegenhelper.h"
0008 #include "localegenhelperadaptor.h"
0009 
0010 #include <KLocalizedString>
0011 
0012 #include <QDBusConnection>
0013 #include <QDebug>
0014 
0015 #include <chrono>
0016 
0017 LocaleGenHelper::LocaleGenHelper()
0018     : m_authority(PolkitQt1::Authority::instance())
0019 {
0020     new LocaleGenHelperAdaptor(this);
0021     if (!QDBusConnection::systemBus().registerService(QStringLiteral("org.kde.localegenhelper"))) {
0022         qWarning() << "another helper is already running";
0023         QCoreApplication::instance()->exit();
0024     }
0025     if (!QDBusConnection::systemBus().registerObject(QStringLiteral("/LocaleGenHelper"), this)) {
0026         qWarning() << "unable to register service interface to dbus";
0027         QCoreApplication::instance()->exit();
0028     }
0029     connect(m_authority, &PolkitQt1::Authority::checkAuthorizationFinished, this, &LocaleGenHelper::enableLocalesPrivate);
0030     connect(&m_timer, &QTimer::timeout, this, [] {
0031         QCoreApplication::instance()->exit();
0032     });
0033     exitAfterTimeOut();
0034 }
0035 
0036 void LocaleGenHelper::enableLocales(const QStringList &locales)
0037 {
0038     qDebug() << locales;
0039     if (m_timer.isActive()) {
0040         m_timer.stop();
0041     }
0042     if (m_isGenerating) {
0043         Q_EMIT error(i18n("Another process is already running, please retry later"));
0044         exitAfterTimeOut();
0045         return;
0046     }
0047     processLocales(locales);
0048     m_isGenerating = true;
0049     if (shouldGenerate()) {
0050         m_authority->checkAuthorization(QStringLiteral("org.kde.localegenhelper.enableLocales"),
0051                                         PolkitQt1::SystemBusNameSubject(message().service()),
0052                                         PolkitQt1::Authority::AllowUserInteraction);
0053     } else {
0054         exitAfterTimeOut();
0055         Q_EMIT success();
0056     }
0057 }
0058 
0059 void LocaleGenHelper::enableLocalesPrivate(PolkitQt1::Authority::Result result)
0060 {
0061     qDebug() << result;
0062     if (result != PolkitQt1::Authority::Result::Yes) {
0063         Q_EMIT error(i18n("Unauthorized to edit locale configuration file"));
0064         exitAfterTimeOut();
0065         return;
0066     }
0067 
0068     // if success, handleLocaleGen will call exit
0069     if (editLocaleGen()) {
0070         exitAfterTimeOut();
0071     }
0072 }
0073 
0074 bool LocaleGenHelper::shouldGenerate()
0075 {
0076     QFile localegen(QStringLiteral("/etc/locale.gen"));
0077     if (!localegen.open(QIODevice::ReadOnly)) {
0078         return false;
0079     }
0080     m_alreadyEnabled.clear();
0081     while (!localegen.atEnd()) {
0082         QString locale = localegen.readLine().simplified();
0083         if (!m_comment && locale == QStringLiteral("# generated by KDE Plasma Region & Language KCM")) {
0084             m_comment = true;
0085         }
0086         if (locale.isEmpty() || locale.front() == QLatin1Char('#')) {
0087             continue;
0088         }
0089         QStringList localeAndCharset = locale.split(QLatin1Char(' '));
0090         if (localeAndCharset.size() != 2 || localeAndCharset.at(1) != QStringLiteral("UTF-8")) {
0091             continue;
0092         } else {
0093             QString localeNameWithoutCharset = localeAndCharset.front().remove(QStringLiteral(".UTF-8"));
0094             m_alreadyEnabled.insert(localeNameWithoutCharset);
0095         }
0096     }
0097     for (const auto &locale : std::as_const(m_locales)) {
0098         if (locale == QStringLiteral("C")) {
0099             continue;
0100         }
0101         if (m_alreadyEnabled.count(locale) == 0) {
0102             return true;
0103         }
0104     }
0105     return false;
0106 }
0107 
0108 bool LocaleGenHelper::editLocaleGen()
0109 {
0110     bool result = false;
0111     QFile localegen(QStringLiteral("/etc/locale.gen"));
0112     if (!localegen.open(QIODevice::Append)) {
0113         Q_EMIT error(i18n("Can't open file `/etc/locale.gen`"));
0114         return result;
0115     }
0116     for (const auto &locale : std::as_const(m_locales)) {
0117         if (m_alreadyEnabled.count(locale) || locale == QStringLiteral("C")) {
0118             continue;
0119         }
0120         // start at newline first time
0121         if (!m_comment) {
0122             localegen.write("\n# generated by KDE Plasma Region & Language KCM\n");
0123             m_comment = true;
0124         }
0125         localegen.write(locale.toUtf8() + ".UTF-8 UTF-8\n");
0126     }
0127 
0128     QString localeGenPath = QStandardPaths::findExecutable(QStringLiteral("locale-gen"));
0129     if (localeGenPath.isEmpty()) {
0130         localeGenPath = QStandardPaths::findExecutable(QStringLiteral("locale-gen"),
0131                                                        {
0132                                                            QStringLiteral("/usr/sbin"),
0133                                                            QStringLiteral("/sbin"),
0134                                                            QStringLiteral("/usr/local/sbin"),
0135                                                        });
0136     }
0137     if (!localeGenPath.isEmpty()) {
0138         auto *process = new QProcess(this);
0139         process->setProgram(localeGenPath);
0140         connect(process, &QProcess::finished, this, [this, process](int statusCode, QProcess::ExitStatus status) {
0141             handleLocaleGen(statusCode, status, process);
0142         });
0143         process->start();
0144         result = true;
0145     } else {
0146         Q_EMIT error(i18n("Can't locate executable `locale-gen`"));
0147     }
0148     return result;
0149 }
0150 
0151 void LocaleGenHelper::handleLocaleGen(int statusCode, QProcess::ExitStatus status, QProcess *process)
0152 {
0153     Q_UNUSED(status)
0154     if (statusCode == 0) {
0155         Q_EMIT success();
0156     } else {
0157         QString all_error;
0158         if (!process) {
0159             all_error = i18n("Unknown");
0160         } else {
0161             all_error.append(process->readAllStandardOutput());
0162             all_error.append(QChar('\n'));
0163             all_error.append(process->readAllStandardError());
0164         }
0165         Q_EMIT error(all_error);
0166     }
0167     exitAfterTimeOut();
0168 }
0169 
0170 void LocaleGenHelper::exitAfterTimeOut()
0171 {
0172     m_isGenerating = false;
0173     m_timer.start(30s);
0174 }
0175 
0176 void LocaleGenHelper::processLocales(const QStringList &locales)
0177 {
0178     QStringList processedLocales = locales;
0179     for (auto &locale : processedLocales) {
0180         locale.remove(QStringLiteral(".UTF-8"));
0181         if (locale == QStringLiteral("C")) {
0182             continue;
0183         }
0184     }
0185     m_locales = std::move(processedLocales);
0186 }
0187 
0188 int main(int argc, char *argv[])
0189 {
0190     QCoreApplication app(argc, argv);
0191     LocaleGenHelper generator;
0192     return app.exec();
0193 }