You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
407 lines
12 KiB
407 lines
12 KiB
/* |
|
KHelper - KDE Font Installer |
|
|
|
SPDX-FileCopyrightText: 2003-2010 Craig Drummond <craig@kde.org> |
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "Helper.h" |
|
#include "Folder.h" |
|
#include "FontInst.h" |
|
#include "Misc.h" |
|
#include "Utils.h" |
|
#include <KAuth/HelperSupport> |
|
#include <QCoreApplication> |
|
#include <QDebug> |
|
#include <QDomDocument> |
|
#include <QTextCodec> |
|
#include <kio/global.h> |
|
#include <signal.h> |
|
#include <sys/errno.h> |
|
#include <sys/types.h> |
|
#include <unistd.h> |
|
|
|
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() |
|
{ |
|
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(); |
|
|
|
// qDebug() << 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 { |
|
// qDebug() << "Uknown action"; |
|
} |
|
|
|
if (FontInst::STATUS_OK == result) { |
|
return ActionReply::SuccessReply(); |
|
} |
|
|
|
ActionReply reply(ActionReply::HelperErrorType); |
|
reply.setErrorCode(static_cast<KAuth::ActionReply::Error>(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()); |
|
|
|
// qDebug() << 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()); |
|
|
|
// qDebug() << 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<QString, QString> 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<QString, QString>::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()); |
|
|
|
// qDebug() << 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<File, QString> movedFonts; |
|
QHash<QString, QString> movedAssoc; |
|
QSet<QString> 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()) { |
|
// qDebug() << "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<File, QString>::ConstIterator fit(movedFonts.constBegin()), fend(movedFonts.constEnd()); |
|
QHash<QString, QString>::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()); |
|
|
|
// qDebug() << 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() |
|
{ |
|
saveDisabled(); |
|
// qDebug() << theFontFolder.isModified(); |
|
if (theFontFolder.isModified()) { |
|
theFontFolder.configure(); |
|
} |
|
return FontInst::STATUS_OK; |
|
} |
|
|
|
int Helper::saveDisabled() |
|
{ |
|
// 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; |
|
} |
|
|
|
}
|
|
|