File indexing completed on 2024-06-02 05:43:47

0001 /*
0002     KHelper - KDE Font Installer
0003 
0004     SPDX-FileCopyrightText: 2003-2010 Craig Drummond <craig@kde.org>
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "Helper.h"
0009 #include "Folder.h"
0010 #include "FontInst.h"
0011 #include "Misc.h"
0012 #include "Utils.h"
0013 #include <KAuth/HelperSupport>
0014 #include <QCoreApplication>
0015 #include <QDebug>
0016 #include <QDomDocument>
0017 #include <kio/global.h>
0018 #include <signal.h>
0019 #include <sys/errno.h>
0020 #include <sys/types.h>
0021 #include <unistd.h>
0022 
0023 KAUTH_HELPER_MAIN("org.kde.fontinst", KFI::Helper)
0024 
0025 namespace KFI
0026 {
0027 static Folder theFontFolder;
0028 
0029 typedef void (*SignalHandler)(int);
0030 
0031 static void registerSignalHandler(SignalHandler handler)
0032 {
0033     if (!handler) {
0034         handler = SIG_DFL;
0035     }
0036 
0037     sigset_t mask;
0038     sigemptyset(&mask);
0039 
0040 #ifdef SIGSEGV
0041     signal(SIGSEGV, handler);
0042     sigaddset(&mask, SIGSEGV);
0043 #endif
0044 #ifdef SIGFPE
0045     signal(SIGFPE, handler);
0046     sigaddset(&mask, SIGFPE);
0047 #endif
0048 #ifdef SIGILL
0049     signal(SIGILL, handler);
0050     sigaddset(&mask, SIGILL);
0051 #endif
0052 #ifdef SIGABRT
0053     signal(SIGABRT, handler);
0054     sigaddset(&mask, SIGABRT);
0055 #endif
0056 
0057     sigprocmask(SIG_UNBLOCK, &mask, nullptr);
0058 }
0059 
0060 static void signalHander(int)
0061 {
0062     static bool inHandler = false;
0063 
0064     if (!inHandler) {
0065         inHandler = true;
0066         theFontFolder.saveDisabled();
0067         inHandler = false;
0068     }
0069 }
0070 
0071 static void cleanup()
0072 {
0073     theFontFolder.saveDisabled();
0074 }
0075 
0076 Helper::Helper()
0077 {
0078     registerSignalHandler(signalHander);
0079     qAddPostRoutine(cleanup);
0080     theFontFolder.init(true, true);
0081     theFontFolder.loadDisabled();
0082 }
0083 
0084 Helper::~Helper()
0085 {
0086     theFontFolder.saveDisabled();
0087 }
0088 
0089 ActionReply Helper::manage(const QVariantMap &args)
0090 {
0091     int result = KIO::ERR_UNSUPPORTED_ACTION;
0092     QString method = args["method"].toString();
0093 
0094     // qDebug() << method;
0095 
0096     if ("install" == method) {
0097         result = install(args);
0098     } else if ("uninstall" == method) {
0099         result = uninstall(args);
0100     } else if ("move" == method) {
0101         result = move(args);
0102     } else if ("toggle" == method) {
0103         result = toggle(args);
0104     } else if ("removeFile" == method) {
0105         result = removeFile(args);
0106     } else if ("reconfigure" == method) {
0107         result = reconfigure();
0108     } else if ("saveDisabled" == method) {
0109         result = saveDisabled();
0110     } else {
0111         // qDebug() << "Uknown action";
0112     }
0113 
0114     if (FontInst::STATUS_OK == result) {
0115         return ActionReply::SuccessReply();
0116     }
0117 
0118     ActionReply reply(ActionReply::HelperErrorType);
0119     reply.setErrorCode(static_cast<KAuth::ActionReply::Error>(result));
0120     return reply;
0121 }
0122 
0123 int Helper::install(const QVariantMap &args)
0124 {
0125     QString file(args["file"].toString()), name(args["name"].toString()), destFolder(args["destFolder"].toString());
0126     bool createAfm(args["createAfm"].toBool());
0127     int type(args["type"].toInt());
0128 
0129     // qDebug() << file << destFolder << name << createAfm;
0130 
0131     int result = FontInst::STATUS_OK;
0132 
0133     if (!Misc::dExists(destFolder)) {
0134         result = Misc::createDir(destFolder) ? (int)FontInst::STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED;
0135     }
0136 
0137     if (FontInst::STATUS_OK == result) {
0138         result = QFile::copy(file, destFolder + name) ? (int)FontInst::STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED;
0139     }
0140 
0141     if (FontInst::STATUS_OK == result) {
0142         Misc::setFilePerms(QFile::encodeName(destFolder + name));
0143         if ((Utils::FILE_SCALABLE == type || Utils::FILE_PFM == type) && createAfm) {
0144             Utils::createAfm(destFolder + name, (KFI::Utils::EFileType)type);
0145         }
0146         theFontFolder.addModifiedDir(destFolder);
0147     }
0148 
0149     return result;
0150 }
0151 
0152 int Helper::uninstall(const QVariantMap &args)
0153 {
0154     QStringList files(args["files"].toStringList());
0155     int result = checkWriteAction(files);
0156 
0157     if (FontInst::STATUS_OK == result) {
0158         QStringList::ConstIterator it(files.constBegin()), end(files.constEnd());
0159 
0160         for (; it != end; ++it) {
0161             if (!Misc::fExists(*it) || QFile::remove(*it)) {
0162                 // Also remove any AFM or PFM files...
0163                 QStringList other;
0164                 Misc::getAssociatedFiles(*it, other);
0165                 QStringList::ConstIterator oit(other.constBegin()), oend(other.constEnd());
0166                 for (; oit != oend; ++oit) {
0167                     QFile::remove(*oit);
0168                 }
0169 
0170                 theFontFolder.addModifiedDir(Misc::getDir(*it));
0171             }
0172         }
0173     }
0174 
0175     return result;
0176 }
0177 
0178 static bool renameFontFile(const QString &from, const QString &to, int uid = -1, int gid = -1)
0179 {
0180     if (!QFile::rename(from, to)) {
0181         return false;
0182     }
0183 
0184     QByteArray dest(QFile::encodeName(to));
0185 
0186     Misc::setFilePerms(dest);
0187     if (-1 != uid && -1 != gid) {
0188         ::chown(dest.data(), uid, gid);
0189     }
0190     return true;
0191 }
0192 
0193 int Helper::move(const QVariantMap &args)
0194 {
0195     QStringList files(args["files"].toStringList());
0196     bool toSystem(args["toSystem"].toBool());
0197     QString dest(args["dest"].toString());
0198     int uid(args["uid"].toInt()), gid(args["gid"].toInt());
0199 
0200     // qDebug() << files << dest << toSystem;
0201 
0202     int result = FontInst::STATUS_OK;
0203     QStringList::ConstIterator it(files.constBegin()), end(files.constEnd());
0204 
0205     // Cant move hidden fonts - need to unhide first.
0206     for (; it != end && FontInst::STATUS_OK == result; ++it) {
0207         if (Misc::isHidden(Misc::getFile(*it))) {
0208             result = KIO::ERR_UNSUPPORTED_ACTION;
0209         }
0210     }
0211 
0212     if (FontInst::STATUS_OK == result) {
0213         QHash<QString, QString> movedFiles;
0214         int toUid = toSystem ? getuid() : uid, fromUid = toSystem ? uid : getuid(), toGid = toSystem ? getgid() : gid, fromGid = toSystem ? gid : getgid();
0215 
0216         // Move fonts!
0217         for (it = files.constBegin(); it != end && FontInst::STATUS_OK == result; ++it) {
0218             QString name(Misc::modifyName(Misc::getFile(*it))), destFolder(Misc::getDestFolder(dest, name));
0219 
0220             if (!Misc::dExists(destFolder)) {
0221                 result = Misc::createDir(destFolder) ? (int)FontInst::STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED;
0222                 if (FontInst::STATUS_OK == result) {
0223                     ::chown(QFile::encodeName(destFolder).data(), toUid, toGid);
0224                 }
0225             }
0226 
0227             if (renameFontFile(*it, destFolder + name, toUid, toGid)) {
0228                 movedFiles[*it] = destFolder + name;
0229                 // Now try to move an associated AFM or PFM files...
0230                 QStringList assoc;
0231 
0232                 Misc::getAssociatedFiles(*it, assoc);
0233 
0234                 QStringList::ConstIterator ait(assoc.constBegin()), aend(assoc.constEnd());
0235 
0236                 for (; ait != aend && FontInst::STATUS_OK == result; ++ait) {
0237                     name = Misc::getFile(*ait);
0238                     if (renameFontFile(*ait, destFolder + name, toUid, toGid)) {
0239                         movedFiles[*ait] = destFolder + name;
0240                     } else {
0241                         result = KIO::ERR_WRITE_ACCESS_DENIED;
0242                     }
0243                 }
0244 
0245                 if (toSystem) {
0246                     theFontFolder.addModifiedDir(theFontFolder.location());
0247                 }
0248             } else {
0249                 result = KIO::ERR_WRITE_ACCESS_DENIED;
0250             }
0251         }
0252 
0253         if (FontInst::STATUS_OK != result) // un-move fonts!
0254         {
0255             QHash<QString, QString>::ConstIterator it(movedFiles.constBegin()), end(movedFiles.constEnd());
0256 
0257             for (; it != end; ++it) {
0258                 renameFontFile(it.value(), it.key(), fromUid, fromGid);
0259             }
0260         }
0261     }
0262     return result;
0263 }
0264 
0265 int Helper::toggle(const QVariantMap &args)
0266 {
0267     QDomDocument doc;
0268     doc.setContent(args["xml"].toString());
0269     Family font(doc.documentElement(), true);
0270     bool enable(args["enable"].toBool());
0271 
0272     // qDebug() << font.name() << enable;
0273 
0274     if (1 != font.styles().count()) {
0275         return KIO::ERR_WRITE_ACCESS_DENIED;
0276     }
0277 
0278     int result = FontInst::STATUS_OK;
0279     FileCont files((*font.styles().begin()).files()), toggledFiles;
0280     FileCont::ConstIterator it(files.constBegin()), end(files.constEnd());
0281     QHash<File, QString> movedFonts;
0282     QHash<QString, QString> movedAssoc;
0283     QSet<QString> modifiedDirs;
0284     // Move fonts!
0285     for (; it != end && FontInst::STATUS_OK == result; ++it) {
0286         QString to = Misc::getDir((*it).path()) + QString(enable ? Misc::unhide(Misc::getFile((*it).path())) : Misc::hide(Misc::getFile((*it).path())));
0287 
0288         if (to != (*it).path()) {
0289             // qDebug() << "MOVE:" << (*it).path() << " to " << to;
0290             if (renameFontFile((*it).path(), to)) {
0291                 modifiedDirs.insert(Misc::getDir(enable ? to : (*it).path()));
0292                 toggledFiles.insert(File(to, (*it).foundry(), (*it).index()));
0293                 // Now try to move an associated AFM or PFM files...
0294                 QStringList assoc;
0295 
0296                 movedFonts[*it] = to;
0297                 Misc::getAssociatedFiles((*it).path(), assoc);
0298 
0299                 QStringList::ConstIterator ait(assoc.constBegin()), aend(assoc.constEnd());
0300 
0301                 for (; ait != aend && FontInst::STATUS_OK == result; ++ait) {
0302                     to = Misc::getDir(*ait) + QString(enable ? Misc::unhide(Misc::getFile(*ait)) : Misc::hide(Misc::getFile(*ait)));
0303 
0304                     if (to != *ait) {
0305                         if (renameFontFile(*ait, to)) {
0306                             movedAssoc[*ait] = to;
0307                         } else {
0308                             result = KIO::ERR_WRITE_ACCESS_DENIED;
0309                         }
0310                     }
0311                 }
0312             } else {
0313                 result = KIO::ERR_WRITE_ACCESS_DENIED;
0314             }
0315         }
0316     }
0317 
0318     theFontFolder.addModifiedDirs(modifiedDirs);
0319 
0320     if (FontInst::STATUS_OK == result) {
0321         FamilyCont::ConstIterator f = theFontFolder.fonts().find(font);
0322 
0323         if (theFontFolder.fonts().end() == f) {
0324             f = theFontFolder.addFont(font);
0325         }
0326 
0327         StyleCont::ConstIterator st = (*f).styles().find(*font.styles().begin());
0328 
0329         if ((*f).styles().end() == st) {
0330             st = (*f).add(*font.styles().begin());
0331         }
0332 
0333         // This helper only needs to store list of disabled fonts,
0334         // for writing back to disk - therefore no need to store
0335         // list of enabled font files.
0336         FileCont empty;
0337         (*st).setFiles(enable ? empty : toggledFiles);
0338         if ((*st).files().isEmpty()) {
0339             (*f).remove(*st);
0340         }
0341         if ((*f).styles().isEmpty()) {
0342             theFontFolder.removeFont(*f);
0343         }
0344         theFontFolder.setDisabledDirty();
0345     } else {
0346         QHash<File, QString>::ConstIterator fit(movedFonts.constBegin()), fend(movedFonts.constEnd());
0347         QHash<QString, QString>::ConstIterator ait(movedAssoc.constBegin()), aend(movedAssoc.constEnd());
0348 
0349         for (; fit != fend; ++fit) {
0350             renameFontFile(fit.value(), fit.key().path());
0351         }
0352         for (; ait != aend; ++ait) {
0353             renameFontFile(ait.value(), ait.key());
0354         }
0355     }
0356 
0357     return result;
0358 }
0359 
0360 int Helper::removeFile(const QVariantMap &args)
0361 {
0362     QString file(args["file"].toString());
0363 
0364     // qDebug() << file;
0365 
0366     QString dir(Misc::getDir(file));
0367     int result = Misc::fExists(file) ? QFile::remove(file) ? (int)FontInst::STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED : (int)KIO::ERR_DOES_NOT_EXIST;
0368 
0369     if (FontInst::STATUS_OK == result) {
0370         theFontFolder.addModifiedDir(dir);
0371     }
0372 
0373     return result;
0374 }
0375 
0376 int Helper::reconfigure()
0377 {
0378     saveDisabled();
0379     // qDebug() << theFontFolder.isModified();
0380     if (theFontFolder.isModified()) {
0381         theFontFolder.configure();
0382     }
0383     return FontInst::STATUS_OK;
0384 }
0385 
0386 int Helper::saveDisabled()
0387 {
0388     // Load internally calls save!
0389     theFontFolder.loadDisabled();
0390     return FontInst::STATUS_OK;
0391 }
0392 
0393 int Helper::checkWriteAction(const QStringList &files)
0394 {
0395     QStringList::ConstIterator it(files.constBegin()), end(files.constEnd());
0396 
0397     for (; it != end; ++it) {
0398         if (!Misc::dWritable(Misc::getDir(*it))) {
0399             return KIO::ERR_WRITE_ACCESS_DENIED;
0400         }
0401     }
0402     return FontInst::STATUS_OK;
0403 }
0404 
0405 }