/* * KHelper - KDE Font Installer * * Copyright 2003-2010 Craig Drummond * * ---- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "Helper.h" #include "Folder.h" #include "FontInst.h" #include "Misc.h" #include "Utils.h" #include #include #include #include #include #include #include #include #include #define KFI_DBUG qDebug() << time(nullptr) KAUTH_HELPER_MAIN("org.kde.fontinst", KFI::Helper) namespace KFI { static Folder theFontFolder; typedef void (*SignalHandler)(int); static void registerSignalHandler(SignalHandler handler) { if (!handler) handler = SIG_DFL; sigset_t mask; sigemptyset(&mask); #ifdef SIGSEGV signal(SIGSEGV, handler); sigaddset(&mask, SIGSEGV); #endif #ifdef SIGFPE signal(SIGFPE, handler); sigaddset(&mask, SIGFPE); #endif #ifdef SIGILL signal(SIGILL, handler); sigaddset(&mask, SIGILL); #endif #ifdef SIGABRT signal(SIGABRT, handler); sigaddset(&mask, SIGABRT); #endif sigprocmask(SIG_UNBLOCK, &mask, nullptr); } static void signalHander(int) { static bool inHandler = false; if (!inHandler) { inHandler = true; theFontFolder.saveDisabled(); inHandler = false; } } static void cleanup() { theFontFolder.saveDisabled(); } Helper::Helper() { KFI_DBUG; QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); registerSignalHandler(signalHander); qAddPostRoutine(cleanup); theFontFolder.init(true, true); theFontFolder.loadDisabled(); } Helper::~Helper() { theFontFolder.saveDisabled(); } ActionReply Helper::manage(const QVariantMap &args) { int result = KIO::ERR_UNSUPPORTED_ACTION; QString method = args["method"].toString(); KFI_DBUG << method; if ("install" == method) result = install(args); else if ("uninstall" == method) result = uninstall(args); else if ("move" == method) result = move(args); else if ("toggle" == method) result = toggle(args); else if ("removeFile" == method) result = removeFile(args); else if ("reconfigure" == method) result = reconfigure(); else if ("saveDisabled" == method) result = saveDisabled(); else KFI_DBUG << "Uknown action"; if (FontInst::STATUS_OK == result) return ActionReply::SuccessReply(); ActionReply reply(ActionReply::HelperErrorType); reply.setErrorCode(static_cast(result)); return reply; } int Helper::install(const QVariantMap &args) { QString file(args["file"].toString()), name(args["name"].toString()), destFolder(args["destFolder"].toString()); bool createAfm(args["createAfm"].toBool()); int type(args["type"].toInt()); KFI_DBUG << file << destFolder << name << createAfm; int result = FontInst::STATUS_OK; if (!Misc::dExists(destFolder)) result = Misc::createDir(destFolder) ? (int)FontInst::STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED; if (FontInst::STATUS_OK == result) result = QFile::copy(file, destFolder + name) ? (int)FontInst::STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED; if (FontInst::STATUS_OK == result) { Misc::setFilePerms(QFile::encodeName(destFolder + name)); if ((Utils::FILE_SCALABLE == type || Utils::FILE_PFM == type) && createAfm) Utils::createAfm(destFolder + name, (KFI::Utils::EFileType)type); theFontFolder.addModifiedDir(destFolder); } return result; } int Helper::uninstall(const QVariantMap &args) { QStringList files(args["files"].toStringList()); int result = checkWriteAction(files); if (FontInst::STATUS_OK == result) { QStringList::ConstIterator it(files.constBegin()), end(files.constEnd()); for (; it != end; ++it) if (!Misc::fExists(*it) || QFile::remove(*it)) { // Also remove any AFM or PFM files... QStringList other; Misc::getAssociatedFiles(*it, other); QStringList::ConstIterator oit(other.constBegin()), oend(other.constEnd()); for (; oit != oend; ++oit) QFile::remove(*oit); theFontFolder.addModifiedDir(Misc::getDir(*it)); } } return result; } static bool renameFontFile(const QString &from, const QString &to, int uid = -1, int gid = -1) { if (!QFile::rename(from, to)) return false; QByteArray dest(QFile::encodeName(to)); Misc::setFilePerms(dest); if (-1 != uid && -1 != gid) ::chown(dest.data(), uid, gid); return true; } int Helper::move(const QVariantMap &args) { QStringList files(args["files"].toStringList()); bool toSystem(args["toSystem"].toBool()); QString dest(args["dest"].toString()); int uid(args["uid"].toInt()), gid(args["gid"].toInt()); KFI_DBUG << files << dest << toSystem; int result = FontInst::STATUS_OK; QStringList::ConstIterator it(files.constBegin()), end(files.constEnd()); // Cant move hidden fonts - need to unhide first. for (; it != end && FontInst::STATUS_OK == result; ++it) if (Misc::isHidden(Misc::getFile(*it))) result = KIO::ERR_UNSUPPORTED_ACTION; if (FontInst::STATUS_OK == result) { QHash movedFiles; int toUid = toSystem ? getuid() : uid, fromUid = toSystem ? uid : getuid(), toGid = toSystem ? getgid() : gid, fromGid = toSystem ? gid : getgid(); // Move fonts! for (it = files.constBegin(); it != end && FontInst::STATUS_OK == result; ++it) { QString name(Misc::modifyName(Misc::getFile(*it))), destFolder(Misc::getDestFolder(dest, name)); if (!Misc::dExists(destFolder)) { result = Misc::createDir(destFolder) ? (int)FontInst::STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED; if (FontInst::STATUS_OK == result) ::chown(QFile::encodeName(destFolder).data(), toUid, toGid); } if (renameFontFile(*it, destFolder + name, toUid, toGid)) { movedFiles[*it] = destFolder + name; // Now try to move an associated AFM or PFM files... QStringList assoc; Misc::getAssociatedFiles(*it, assoc); QStringList::ConstIterator ait(assoc.constBegin()), aend(assoc.constEnd()); for (; ait != aend && FontInst::STATUS_OK == result; ++ait) { name = Misc::getFile(*ait); if (renameFontFile(*ait, destFolder + name, toUid, toGid)) movedFiles[*ait] = destFolder + name; else result = KIO::ERR_WRITE_ACCESS_DENIED; } if (toSystem) theFontFolder.addModifiedDir(theFontFolder.location()); } else result = KIO::ERR_WRITE_ACCESS_DENIED; } if (FontInst::STATUS_OK != result) // un-move fonts! { QHash::ConstIterator it(movedFiles.constBegin()), end(movedFiles.constEnd()); for (; it != end; ++it) renameFontFile(it.value(), it.key(), fromUid, fromGid); } } return result; } int Helper::toggle(const QVariantMap &args) { QDomDocument doc; doc.setContent(args["xml"].toString()); Family font(doc.documentElement(), true); bool enable(args["enable"].toBool()); KFI_DBUG << font.name() << enable; if (1 != font.styles().count()) return KIO::ERR_WRITE_ACCESS_DENIED; int result = FontInst::STATUS_OK; FileCont files((*font.styles().begin()).files()), toggledFiles; FileCont::ConstIterator it(files.constBegin()), end(files.constEnd()); QHash movedFonts; QHash movedAssoc; QSet modifiedDirs; // Move fonts! for (; it != end && FontInst::STATUS_OK == result; ++it) { QString to = Misc::getDir((*it).path()) + QString(enable ? Misc::unhide(Misc::getFile((*it).path())) : Misc::hide(Misc::getFile((*it).path()))); if (to != (*it).path()) { KFI_DBUG << "MOVE:" << (*it).path() << " to " << to; if (renameFontFile((*it).path(), to)) { modifiedDirs.insert(Misc::getDir(enable ? to : (*it).path())); toggledFiles.insert(File(to, (*it).foundry(), (*it).index())); // Now try to move an associated AFM or PFM files... QStringList assoc; movedFonts[*it] = to; Misc::getAssociatedFiles((*it).path(), assoc); QStringList::ConstIterator ait(assoc.constBegin()), aend(assoc.constEnd()); for (; ait != aend && FontInst::STATUS_OK == result; ++ait) { to = Misc::getDir(*ait) + QString(enable ? Misc::unhide(Misc::getFile(*ait)) : Misc::hide(Misc::getFile(*ait))); if (to != *ait) { if (renameFontFile(*ait, to)) movedAssoc[*ait] = to; else result = KIO::ERR_WRITE_ACCESS_DENIED; } } } else result = KIO::ERR_WRITE_ACCESS_DENIED; } } theFontFolder.addModifiedDirs(modifiedDirs); if (FontInst::STATUS_OK == result) { FamilyCont::ConstIterator f = theFontFolder.fonts().find(font); if (theFontFolder.fonts().end() == f) f = theFontFolder.addFont(font); StyleCont::ConstIterator st = (*f).styles().find(*font.styles().begin()); if ((*f).styles().end() == st) st = (*f).add(*font.styles().begin()); // This helper only needs to store list of disabled fonts, // for writing back to disk - therefore no need to store // list of enabled font files. FileCont empty; (*st).setFiles(enable ? empty : toggledFiles); if ((*st).files().isEmpty()) (*f).remove(*st); if ((*f).styles().isEmpty()) theFontFolder.removeFont(*f); theFontFolder.setDisabledDirty(); } else { QHash::ConstIterator fit(movedFonts.constBegin()), fend(movedFonts.constEnd()); QHash::ConstIterator ait(movedAssoc.constBegin()), aend(movedAssoc.constEnd()); for (; fit != fend; ++fit) renameFontFile(fit.value(), fit.key().path()); for (; ait != aend; ++ait) renameFontFile(ait.value(), ait.key()); } return result; } int Helper::removeFile(const QVariantMap &args) { QString file(args["file"].toString()); KFI_DBUG << file; QString dir(Misc::getDir(file)); int result = Misc::fExists(file) ? QFile::remove(file) ? (int)FontInst::STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED : (int)KIO::ERR_DOES_NOT_EXIST; if (FontInst::STATUS_OK == result) theFontFolder.addModifiedDir(dir); return result; } int Helper::reconfigure() { KFI_DBUG; saveDisabled(); KFI_DBUG << theFontFolder.isModified(); if (theFontFolder.isModified()) theFontFolder.configure(); return FontInst::STATUS_OK; } int Helper::saveDisabled() { KFI_DBUG; // Load internally calls save! theFontFolder.loadDisabled(); return FontInst::STATUS_OK; } int Helper::checkWriteAction(const QStringList &files) { QStringList::ConstIterator it(files.constBegin()), end(files.constEnd()); for (; it != end; ++it) if (!Misc::dWritable(Misc::getDir(*it))) return KIO::ERR_WRITE_ACCESS_DENIED; return FontInst::STATUS_OK; } }