diff --git a/kcms/kfontinst/dbus/FcConfig.cpp b/kcms/kfontinst/dbus/FcConfig.cpp index 7fb53820a..408bb20e5 100644 --- a/kcms/kfontinst/dbus/FcConfig.cpp +++ b/kcms/kfontinst/dbus/FcConfig.cpp @@ -1,194 +1,194 @@ /* * KFontInst - KDE Font Installer * * Copyright 2003-2009 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 "FcConfig.h" #include "Misc.h" #include #include #include #include #include #include #include #include #include #include #include -#define KFI_DBUG qDebug() << time(0L) +#define KFI_DBUG qDebug() << time(nullptr) namespace KFI { namespace FcConfig { inline QString xDirSyntax(const QString &d) { return Misc::fileSyntax(d); } // // Obtain location of config file to use. // // For system, prefer the following: // // <...>/config.d/00kde.conf = preferred method from FCConfig >= 2.3 // <...>/local.conf // // Non-system, prefer: // // $HOME/<...>/.fonts.conf // $HOME/<...>/fonts.conf // QString getConfigFile(bool system) { #if (FC_VERSION>=20300) static const char constKdeRootFcFile[] = "00kde.conf"; #endif FcStrList *list=FcConfigGetConfigFiles(FcConfigGetCurrent()); QStringList files; FcChar8 *file; QString home(Misc::dirSyntax(QDir::homePath())); while((file=FcStrListNext(list))) { QString f((const char *)file); if(Misc::fExists(f)) { // For nonsystem, only consider file within $HOME if(system || 0==Misc::fileSyntax(f).indexOf(home)) files.append(f); } #if (FC_VERSION>=20300) if(system && Misc::dExists(f) && (f.contains(QRegExp("/conf\\.d/?$")) || f.contains(QRegExp("/conf\\.d?$"))) ) return Misc::dirSyntax(f)+constKdeRootFcFile; // This ones good enough for me! #endif } // // Go through list of files, looking for the preferred one... if(!files.isEmpty()) { QStringList::const_iterator it(files.begin()), end(files.end()); for(; it!=end; ++it) if(-1!=(*it).indexOf(QRegExp(system ? "/local\\.conf$" : "/\\.?fonts\\.conf$"))) return *it; return files.front(); // Just return the 1st one... } else // Hmmm... no known files? return system ? "/etc/fonts/local.conf" : Misc::fileSyntax(home+"/.fonts.conf"); } void addDir(const QString &dir, bool system) { QDomDocument doc("fontconfig"); QString fileName=getConfigFile(system); QFile f(fileName); bool hasDir(false); KFI_DBUG << "Using fontconfig file:" << fileName; // Load existing file - and check to see whether it has the dir... if(f.open(QIODevice::ReadOnly)) { doc.clear(); if(doc.setContent(&f)) { QDomNode n = doc.documentElement().firstChild(); while(!n.isNull() && !hasDir) { QDomElement e = n.toElement(); if(!e.isNull() && "dir"==e.tagName()) if(0==Misc::expandHome(Misc::dirSyntax(e.text())).indexOf(dir)) hasDir=true; n=n.nextSibling(); } } f.close(); } // Add dir, and save, if config does not already have this dir. if(!hasDir) { if(doc.documentElement().isNull()) doc.appendChild(doc.createElement("fontconfig")); QDomElement newNode = doc.createElement("dir"); QDomText text = doc.createTextNode(Misc::contractHome(xDirSyntax(dir))); newNode.appendChild(text); doc.documentElement().appendChild(newNode); FcAtomic *atomic=FcAtomicCreate((const unsigned char *)(QFile::encodeName(fileName).data())); if(atomic) { if(FcAtomicLock(atomic)) { FILE *f=fopen((char *)FcAtomicNewFile(atomic), "w"); if(f) { // // Check document syntax... static const char qtXmlHeader[] = ""; static const char xmlHeader[] = ""; static const char qtDocTypeLine[] = ""; static const char docTypeLine[] = ""; QString str(doc.toString()); int idx; if(0!=str.indexOf(" * * ---- * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "Folder.h" #include "FcConfig.h" #include "Misc.h" #include "KfiConstants.h" #include "XmlStrings.h" #include "Style.h" #include "File.h" #include "config-fontinst.h" #define DISABLED_FONTS "disabledfonts" -#define KFI_DBUG qDebug() << time(0L) +#define KFI_DBUG qDebug() << time(nullptr) namespace KFI { bool Folder::CfgFile::modified() { return timestamp!=Misc::getTimeStamp(name); } void Folder::CfgFile::updateTimeStamp() { timestamp=Misc::getTimeStamp(name); } Folder::~Folder() { saveDisabled(); } void Folder::init(bool system, bool systemBus) { itsIsSystem=system; if(!system) { FcStrList *list=FcConfigGetFontDirs(FcInitLoadConfigAndFonts()); QStringList dirs; FcChar8 *fcDir; while((fcDir=FcStrListNext(list))) dirs.append(Misc::dirSyntax((const char *)fcDir)); itsLocation=Misc::getFolder(Misc::dirSyntax(QDir::homePath()+"/.fonts/"), Misc::dirSyntax(QDir::homePath()), dirs); } else itsLocation=KFI_DEFAULT_SYS_FONTS_FOLDER; if((!system && !systemBus) || (system && systemBus)) FcConfig::addDir(itsLocation, system); itsDisabledCfg.dirty=false; if(itsDisabledCfg.name.isEmpty()) { QString fileName("/" DISABLED_FONTS ".xml"); if(system) itsDisabledCfg.name=QString::fromLatin1(KFI_ROOT_CFG_DIR)+fileName; else { QString path=QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/'); if(!Misc::dExists(path)) Misc::createDir(path); itsDisabledCfg.name=path+fileName; } itsDisabledCfg.timestamp=0; } } bool Folder::allowToggling() const { return Misc::fExists(itsDisabledCfg.name) ? Misc::fWritable(itsDisabledCfg.name) : Misc::dWritable(Misc::getDir(itsDisabledCfg.name)); } void Folder::loadDisabled() { if(itsDisabledCfg.dirty) saveDisabled(); QFile f(itsDisabledCfg.name); KFI_DBUG << itsDisabledCfg.name; itsDisabledCfg.dirty=false; if(f.open(QIODevice::ReadOnly)) { QDomDocument doc; if(doc.setContent(&f)) for(QDomNode n=doc.documentElement().firstChild(); !n.isNull(); n=n.nextSibling()) { QDomElement e=n.toElement(); if(FONT_TAG==e.tagName()) { Family fam(e, false); if(!fam.name().isEmpty()) { Style style(e, false); if(KFI_NO_STYLE_INFO!=style.value()) { QList files; if(e.hasAttribute(PATH_ATTR)) { File file(e, true); if(!file.path().isEmpty()) files.append(file); else itsDisabledCfg.dirty=true; } else { for(QDomNode n=e.firstChild(); !n.isNull(); n=n.nextSibling()) { QDomElement ent=n.toElement(); if(FILE_TAG==ent.tagName()) { File file(ent, true); if(!file.path().isEmpty()) files.append(file); else { KFI_DBUG << "Set dirty from load"; itsDisabledCfg.dirty=true; } } } } if(files.count()>0) { QList::ConstIterator it(files.begin()), end(files.end()); FamilyCont::ConstIterator f(itsFonts.insert(fam)); StyleCont::ConstIterator s((*f).add(style)); for(; it!=end; ++it) (*s).add(*it); } } } } } f.close(); itsDisabledCfg.updateTimeStamp(); } saveDisabled(); } void Folder::saveDisabled() { if(itsDisabledCfg.dirty) { if(!itsIsSystem || Misc::root()) { KFI_DBUG << itsDisabledCfg.name; QSaveFile file; file.setFileName(itsDisabledCfg.name); if (!file.open(QIODevice::WriteOnly)) { KFI_DBUG << "Exit - cant open save file"; qApp->exit(0); } QTextStream str(&file); str << "<" DISABLED_FONTS ">" << endl; FamilyCont::ConstIterator it(itsFonts.begin()), end(itsFonts.end()); for(; it!=end; ++it) (*it).toXml(true, str); str << "" << endl; str.flush(); if(!file.commit()) { KFI_DBUG << "Exit - cant finalize save file"; qApp->exit(0); } } itsDisabledCfg.updateTimeStamp(); itsDisabledCfg.dirty=false; } } QStringList Folder::toXml(int max) { QStringList rv; FamilyCont::ConstIterator it(itsFonts.begin()), end(itsFonts.end()); QString string; QTextStream str(&string); for(int i=0; it!=end; ++it, ++i) { if(0==(i%max)) { if(i) { str << "" << endl; rv.append(string); string=QString(); } str << "<" FONTLIST_TAG " " << SYSTEM_ATTR "=\"" << (itsIsSystem ? "true" : "false") << "\">" << endl; } (*it).toXml(false, str); } if(!string.isEmpty()) { str << "" << endl; rv.append(string); } return rv; } Families Folder::list() { Families fam(itsIsSystem); FamilyCont::ConstIterator it(itsFonts.begin()), end(itsFonts.end()); for(int i=0; it!=end; ++it, ++i) fam.items.insert(*it); return fam; } bool Folder::contains(const QString &family, quint32 style) { FamilyCont::ConstIterator fam=itsFonts.find(Family(family)); if(fam==itsFonts.end()) return false; StyleCont::ConstIterator st=(*fam).styles().find(Style(style)); return st!=(*fam).styles().end(); } void Folder::add(const Family &family) { FamilyCont::ConstIterator existingFamily=itsFonts.find(family); if(existingFamily==itsFonts.end()) itsFonts.insert(family); else { StyleCont::ConstIterator it(family.styles().begin()), end(family.styles().end()); for(; it!=end; ++it) { StyleCont::ConstIterator existingStyle=(*existingFamily).styles().find(*it); if(existingStyle==(*existingFamily).styles().end()) (*existingFamily).add(*it); else { FileCont::ConstIterator fit((*it).files().begin()), fend((*it).files().end()); for(; fit!=fend; ++fit) { FileCont::ConstIterator f=(*existingStyle).files().find(*fit); if(f==(*existingStyle).files().end()) (*existingStyle).add(*fit); } (*existingStyle).setWritingSystems((*existingStyle).writingSystems()|(*it).writingSystems()); if(!(*existingStyle).scalable() && (*it).scalable()) (*existingStyle).setScalable(true); } } } } void Folder::configure(bool force) { KFI_DBUG << "EMPTY MODIFIED " << itsModifiedDirs.isEmpty(); if(force || !itsModifiedDirs.isEmpty()) { saveDisabled(); QSet::ConstIterator it(itsModifiedDirs.constBegin()), end(itsModifiedDirs.constEnd()); QSet dirs; for(; it!=end; ++it) if(Misc::fExists((*it)+"fonts.dir")) dirs.insert(KShell::quoteArg(*it)); if(!dirs.isEmpty()) QProcess::startDetached(QLatin1String(KFONTINST_LIB_EXEC_DIR"/fontinst_x11"), dirs.toList()); itsModifiedDirs.clear(); KFI_DBUG << "RUN FC"; Misc::doCmd("fc-cache"); KFI_DBUG << "DONE"; } } Folder::Flat Folder::flatten() const { FamilyCont::ConstIterator fam=itsFonts.begin(), famEnd=itsFonts.end(); Flat rv; for(; fam!=famEnd; ++fam) { StyleCont::ConstIterator style((*fam).styles().begin()), styleEnd((*fam).styles().end()); for(; style!=styleEnd; ++style) { FileCont::ConstIterator file((*style).files().begin()), fileEnd((*style).files().end()); for(; file!=fileEnd; ++file) rv.insert(FlatFont(*fam, *style, *file)); } } return rv; } Families Folder::Flat::build(bool system) const { ConstIterator it(begin()), e(end()); Families families(system); for(; it!=e; ++it) { Family f((*it).family); Style s((*it).styleInfo, (*it).scalable, (*it).writingSystems); FamilyCont::ConstIterator fam=families.items.constFind(f); if(families.items.constEnd()==fam) { s.add((*it).file); f.add(s); families.items.insert(f); } else { StyleCont::ConstIterator st=(*fam).styles().constFind(s); if((*fam).styles().constEnd()==st) { s.add((*it).file); (*fam).add(s); } else (*st).add((*it).file); } } return families; } } diff --git a/kcms/kfontinst/dbus/FontInst.cpp b/kcms/kfontinst/dbus/FontInst.cpp index bce0e09d4..3d32b173b 100644 --- a/kcms/kfontinst/dbus/FontInst.cpp +++ b/kcms/kfontinst/dbus/FontInst.cpp @@ -1,974 +1,974 @@ /* * KFontInst - KDE Font Installer * * Copyright 2003-2009 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 #include #include #include #include #include #include #include #include #include "FontInst.h" #include "fontinstadaptor.h" #include "Misc.h" #include "Fc.h" #include "WritingSystems.h" #include "Utils.h" #include "FontinstIface.h" -#define KFI_DBUG qDebug() << time(0L) +#define KFI_DBUG qDebug() << time(nullptr) namespace KFI { static void decompose(const QString &name, QString &family, QString &style) { int commaPos=name.lastIndexOf(','); family=-1==commaPos ? name : name.left(commaPos); style=-1==commaPos ? KFI_WEIGHT_REGULAR : name.mid(commaPos+2); } static bool isSystem=false; static Folder theFolders[FontInst::FOLDER_COUNT]; static const int constSystemReconfigured=-1; static const int constConnectionsTimeout = 30 * 1000; static const int constFontListTimeout = 10 * 1000; 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); } void signalHander(int) { static bool inHandler=false; if(!inHandler) { inHandler=true; theFolders[isSystem ? FontInst::FOLDER_SYS : FontInst::FOLDER_USER].saveDisabled(); inHandler=false; } } FontInst::FontInst() { isSystem=Misc::root(); registerTypes(); new FontinstAdaptor(this); QDBusConnection bus=QDBusConnection::sessionBus(); KFI_DBUG << "Connecting to session bus"; if(!bus.registerService(OrgKdeFontinstInterface::staticInterfaceName())) { KFI_DBUG << "Failed to register service!"; ::exit(-1); } if(!bus.registerObject(FONTINST_PATH, this)) { KFI_DBUG << "Failed to register object!"; ::exit(-1); } registerSignalHandler(signalHander); itsConnectionsTimer=new QTimer(this); itsFontListTimer=new QTimer(this); connect(itsConnectionsTimer, SIGNAL(timeout()), SLOT(connectionsTimeout())); connect(itsFontListTimer, SIGNAL(timeout()), SLOT(fontListTimeout())); itsConnectionsTimer->start(constConnectionsTimeout); itsFontListTimer->start(constFontListTimeout); for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) theFolders[i].init(FOLDER_SYS==i, isSystem); updateFontList(false); } FontInst::~FontInst() { for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) theFolders[i].saveDisabled(); } void FontInst::list(int folders, int pid) { KFI_DBUG << folders << pid; itsConnections.insert(pid); updateFontList(false); QList fonts; for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) if(0==folders || folders&(1<start(constConnectionsTimeout); itsFontListTimer->start(constFontListTimeout); emit fontList(pid, fonts); } void FontInst::statFont(const QString &name, int folders, int pid) { KFI_DBUG << name << folders << pid; bool checkSystem=0==folders || folders&SYS_MASK || isSystem, checkUser=0==folders || (folders&USR_MASK && !isSystem); FamilyCont::ConstIterator fam; StyleCont::ConstIterator st; itsConnections.insert(pid); if( (checkSystem && findFont(name, FOLDER_SYS, fam, st)) || (checkUser && findFont(name, FOLDER_USER, fam, st, !checkSystem)) ) { Family rv((*fam).name()); rv.add(*st); KFI_DBUG << "Found font, emit details..."; emit fontStat(pid, rv); } else { KFI_DBUG << "Font not found, emit empty details..."; emit fontStat(pid, Family(name)); } } void FontInst::install(const QString &file, bool createAfm, bool toSystem, int pid, bool checkConfig) { KFI_DBUG << file << createAfm << toSystem << pid << checkConfig; itsConnections.insert(pid); if(checkConfig) updateFontList(); EFolder folder=isSystem || toSystem ? FOLDER_SYS : FOLDER_USER; Family font; Utils::EFileType type=Utils::check(file, font); int result=Utils::FILE_BITMAP==type && !FC::bitmapsEnabled() ? STATUS_BITMAPS_DISABLED : Utils::FILE_INVALID==type ? STATUS_NOT_FONT_FILE : STATUS_OK; if(STATUS_OK==result) { if(Utils::FILE_AFM!=type && Utils::FILE_PFM!=type) for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT) && STATUS_OK==result; ++i) if(theFolders[i].contains(font.name(), (*font.styles().begin()).value())) result=STATUS_ALREADY_INSTALLED; if(STATUS_OK==result) { QString name(Misc::modifyName(Misc::getFile(file))), destFolder(Misc::getDestFolder(theFolders[folder].location(), name)); result=Utils::FILE_AFM!=type && Utils::FILE_PFM!=type && Misc::fExists(destFolder+name) ? (int)KIO::ERR_FILE_ALREADY_EXIST : (int)STATUS_OK; if(STATUS_OK==result) { if(toSystem && !isSystem) { KFI_DBUG << "Send request to system helper" << file << destFolder << name; QVariantMap args; args["method"] = "install"; args["file"] = file; args["name"] = name; args["destFolder"] = destFolder; args["createAfm"] = createAfm; args["type"] = (int)type; result=performAction(args); } else { if(!Misc::dExists(destFolder)) result=Misc::createDir(destFolder) ? (int)STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED; if(STATUS_OK==result) result=QFile::copy(file, destFolder+name) ? (int)STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED; if(STATUS_OK==result) { Misc::setFilePerms(QFile::encodeName(destFolder+name)); if((Utils::FILE_SCALABLE==type || Utils::FILE_PFM==type) && createAfm) Utils::createAfm(destFolder+name, type); } } if(STATUS_OK==result && Utils::FILE_AFM!=type && Utils::FILE_PFM!=type) { StyleCont::ConstIterator st(font.styles().begin()); FileCont::ConstIterator f((*st).files().begin()); File df(destFolder+name, (*f).foundry(), (*f).index()); (*st).clearFiles(); (*st).add(df); theFolders[folder].add(font); theFolders[folder].addModifiedDir(destFolder); emit fontsAdded(Families(font, FOLDER_SYS==folder)); } } } } emit status(pid, result); itsConnectionsTimer->start(constConnectionsTimeout); itsFontListTimer->start(constFontListTimeout); } void FontInst::uninstall(const QString &family, quint32 style, bool fromSystem, int pid, bool checkConfig) { KFI_DBUG << family << style << fromSystem << pid << checkConfig; itsConnections.insert(pid); if(checkConfig) updateFontList(); EFolder folder=isSystem || fromSystem ? FOLDER_SYS : FOLDER_USER; FamilyCont::ConstIterator fam; StyleCont::ConstIterator st; int result=findFont(family, style, folder, fam, st) ? (int)STATUS_OK : (int)KIO::ERR_DOES_NOT_EXIST; if(STATUS_OK==result) { Family del((*fam).name()); Style s((*st).value(), (*st).scalable(), (*st).writingSystems()); FileCont files((*st).files()); FileCont::ConstIterator it(files.begin()), end(files.end()); if(fromSystem && !isSystem) { QStringList fileList; bool wasDisabled(false); for(; it!=end; ++it) { fileList.append((*it).path()); theFolders[FOLDER_SYS].addModifiedDir(Misc::getDir((*it).path())); if(!wasDisabled && Misc::isHidden(Misc::getFile((*it).path()))) wasDisabled=true; } QVariantMap args; args["method"] = "uninstall"; args["files"] = fileList; result=performAction(args); if(STATUS_OK==result) { FileCont empty; s.setFiles(files); (*st).setFiles(empty); if(wasDisabled) theFolders[FOLDER_SYS].setDisabledDirty(); } } else { for(; it!=end; ++it) if(!Misc::fExists((*it).path()) || QFile::remove((*it).path())) { // Also remove any AFM or PFM files... QStringList other; Misc::getAssociatedFiles((*it).path(), other); QStringList::ConstIterator oit(other.constBegin()), oend(other.constEnd()); for(; oit!=oend; ++oit) QFile::remove(*oit); theFolders[folder].addModifiedDir(Misc::getDir((*it).path())); (*st).remove(*it); s.add(*it); if(!theFolders[folder].disabledDirty() && Misc::isHidden(Misc::getFile((*it).path()))) theFolders[folder].setDisabledDirty(); } } if(STATUS_OK==result) { if((*st).files().isEmpty()) { (*fam).remove(*st); if((*fam).styles().isEmpty()) theFolders[folder].removeFont(*fam); } else result=STATUS_PARTIAL_DELETE; del.add(s); } emit fontsRemoved(Families(del, FOLDER_SYS==folder)); } KFI_DBUG << "status" << result; emit status(pid, result); itsConnectionsTimer->start(constConnectionsTimeout); itsFontListTimer->start(constFontListTimeout); } void FontInst::uninstall(const QString &name, bool fromSystem, int pid, bool checkConfig) { KFI_DBUG << name << fromSystem << pid << checkConfig; FamilyCont::ConstIterator fam; StyleCont::ConstIterator st; if(findFont(name, fromSystem || isSystem ? FOLDER_SYS : FOLDER_USER, fam, st)) uninstall((*fam).name(), (*st).value(), fromSystem, pid, checkConfig); else emit status(pid, KIO::ERR_DOES_NOT_EXIST); } void FontInst::move(const QString &family, quint32 style, bool toSystem, int pid, bool checkConfig) { KFI_DBUG << family << style << toSystem << pid << checkConfig; itsConnections.insert(pid); if(checkConfig) updateFontList(); if(isSystem) emit status(pid, KIO::ERR_UNSUPPORTED_ACTION); else { FamilyCont::ConstIterator fam; StyleCont::ConstIterator st; EFolder from=toSystem ? FOLDER_USER : FOLDER_SYS, to=toSystem ? FOLDER_SYS : FOLDER_USER; if(findFont(family, style, from, fam, st)) { FileCont::ConstIterator it((*st).files().begin()), end((*st).files().end()); QStringList files; for(; it!=end; ++it) { files.append((*it).path()); theFolders[from].addModifiedDir(Misc::getDir((*it).path())); // Actual 'to' folder does not really matter, as we only want to call fc-cache // ...actual folders only matter for xreating fonts.dir, etc, and we wont be doing this... theFolders[to].addModifiedDir(theFolders[to].location()); } QVariantMap args; args["method"] = "move"; args["files"] = files; args["toSystem"] = toSystem; args["dest"] = theFolders[to].location(); args["uid"] = getuid(); args["gid"] = getgid(); int result=performAction(args); if(STATUS_OK==result) updateFontList(); emit status(pid, result); } else { KFI_DBUG << "does not exist"; emit status(pid, KIO::ERR_DOES_NOT_EXIST); } } itsConnectionsTimer->start(constConnectionsTimeout); itsFontListTimer->start(constFontListTimeout); } 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; } void FontInst::enable(const QString &family, quint32 style, bool inSystem, int pid, bool checkConfig) { KFI_DBUG << family << style << inSystem << pid << checkConfig; toggle(true, family, style, inSystem, pid, checkConfig); } void FontInst::disable(const QString &family, quint32 style, bool inSystem, int pid, bool checkConfig) { KFI_DBUG << family << style << inSystem << pid << checkConfig; toggle(false, family, style, inSystem, pid, checkConfig); } void FontInst::removeFile(const QString &family, quint32 style, const QString &file, bool fromSystem, int pid, bool checkConfig) { KFI_DBUG << family << style << file << fromSystem << pid << checkConfig; itsConnections.insert(pid); if(checkConfig) updateFontList(); // First find the family/style EFolder folder=isSystem || fromSystem ? FOLDER_SYS : FOLDER_USER; FamilyCont::ConstIterator fam; StyleCont::ConstIterator st; int result=findFont(family, style, folder, fam, st) ? (int)STATUS_OK : (int)KIO::ERR_DOES_NOT_EXIST; if(STATUS_OK==result) { // Family/style found - now check that the requested file is *within* the same folder as one // of the files linked to this font... FileCont files((*st).files()); FileCont::ConstIterator it(files.begin()), end(files.end()); QString dir(Misc::getDir(file)); result=KIO::ERR_DOES_NOT_EXIST; for(; it!=end && STATUS_OK!=result; ++it) if(Misc::getDir((*it).path())==dir) result=STATUS_OK; if(STATUS_OK==result) { // OK, found folder - so can now proceed to delete the file... if(fromSystem && !isSystem) { QVariantMap args; args["method"] = "removeFile"; args["file"] = file; result=performAction(args); } else { result=Misc::fExists(file) ? QFile::remove(file) ? (int)STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED : (int)KIO::ERR_DOES_NOT_EXIST; } if(STATUS_OK==result) theFolders[folder].addModifiedDir(dir); } } emit status(pid, result); } void FontInst::reconfigure(int pid, bool force) { KFI_DBUG << pid << force; bool sysModified(theFolders[FOLDER_SYS].isModified()); saveDisabled(); KFI_DBUG << theFolders[FOLDER_USER].isModified() << sysModified; if(!isSystem && (force || theFolders[FOLDER_USER].isModified())) theFolders[FOLDER_USER].configure(force); if(sysModified) { if(isSystem) { theFolders[FOLDER_SYS].configure(); } else { QVariantMap args; args["method"] = "reconfigure"; performAction(args); theFolders[FOLDER_SYS].clearModified(); } } itsConnectionsTimer->start(constConnectionsTimeout); itsFontListTimer->start(constFontListTimeout); updateFontList(); emit status(pid, isSystem ? constSystemReconfigured : STATUS_OK); } QString FontInst::folderName(bool sys) { return theFolders[sys || isSystem ? FOLDER_SYS : FOLDER_USER].location(); } void FontInst::saveDisabled() { if(isSystem) theFolders[FOLDER_SYS].saveDisabled(); else for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) if(FOLDER_SYS==i && !isSystem) { if(theFolders[i].disabledDirty()) { QVariantMap args; args["method"] = "saveDisabled"; performAction(args); theFolders[i].saveDisabled(); } } else theFolders[i].saveDisabled(); } void FontInst::connectionsTimeout() { bool canExit(true); KFI_DBUG << "exiting"; checkConnections(); for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) { if(theFolders[i].disabledDirty()) canExit=false; theFolders[i].saveDisabled(); } if(0==itsConnections.count()) { if(canExit) qApp->exit(0); else // Try again later... itsConnectionsTimer->start(constConnectionsTimeout); } } void FontInst::fontListTimeout() { updateFontList(true); itsFontListTimer->start(constFontListTimeout); } void FontInst::updateFontList(bool emitChanges) { // For some reason just the "!FcConfigUptoDate(0)" check does not always work :-( FcBool fcModified=!FcConfigUptoDate(nullptr); if(fcModified || theFolders[FOLDER_SYS].fonts().isEmpty() || (!isSystem && theFolders[FOLDER_USER].fonts().isEmpty()) || theFolders[FOLDER_SYS].disabledDirty() || (!isSystem && theFolders[FOLDER_USER].disabledDirty())) { KFI_DBUG << "Need to refresh font lists"; if(fcModified) { KFI_DBUG << "Re-init FC"; if(!FcInitReinitialize()) KFI_DBUG << "Re-init failed????"; } Folder::Flat old[FOLDER_COUNT]; if(emitChanges) { KFI_DBUG << "Flatten existing font lists"; for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) old[i]=theFolders[i].flatten(); } saveDisabled(); for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) theFolders[i].clearFonts(); KFI_DBUG << "update list of fonts"; FcPattern *pat = FcPatternCreate(); FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_FAMILY, FC_FAMILYLANG, FC_WEIGHT, FC_LANG, FC_CHARSET, FC_SCALABLE, #ifndef KFI_FC_NO_WIDTHS FC_WIDTH, #endif FC_SLANT, FC_INDEX, FC_FOUNDRY, (void*)nullptr); FcFontSet *list=FcFontList(nullptr, pat, os); FcPatternDestroy(pat); FcObjectSetDestroy(os); theFolders[FOLDER_SYS].loadDisabled(); if(!isSystem) theFolders[FOLDER_USER].loadDisabled(); if(list) { QString home(Misc::dirSyntax(QDir::homePath())); for (int i = 0; i < list->nfont; i++) { QString fileName(Misc::fileSyntax(FC::getFcString(list->fonts[i], FC_FILE))); if(!fileName.isEmpty() && Misc::fExists(fileName)) // && 0!=fileName.indexOf(constDefomaLocation)) { QString family, foundry; quint32 styleVal; int index; qulonglong writingSystems(WritingSystems::instance()->get(list->fonts[i])); FcBool scalable=FcFalse; if(FcResultMatch!=FcPatternGetBool(list->fonts[i], FC_SCALABLE, 0, &scalable)) scalable=FcFalse; FC::getDetails(list->fonts[i], family, styleVal, index, foundry); FamilyCont::ConstIterator fam=theFolders[isSystem || 0!=fileName.indexOf(home) ? FOLDER_SYS : FOLDER_USER].addFont(Family(family)); StyleCont::ConstIterator style=(*fam).add(Style(styleVal)); FileCont::ConstIterator file=(*style).add(File(fileName, foundry, index)); (*style).setWritingSystems((*style).writingSystems()|writingSystems); if(scalable) (*style).setScalable(); } } FcFontSetDestroy(list); } if(emitChanges) { KFI_DBUG << "Look for differences"; for(int i=0; i<(isSystem ? 1 : FOLDER_COUNT); ++i) { KFI_DBUG << "Flatten, and take copies..."; Folder::Flat newList=theFolders[i].flatten(), onlyNew=newList; KFI_DBUG << "Determine differences..."; onlyNew.subtract(old[i]); old[i].subtract(newList); KFI_DBUG << "Emit changes..."; Families families=onlyNew.build(isSystem || i==FOLDER_SYS); if(!families.items.isEmpty()) emit fontsAdded(families); families=old[i].build(isSystem || i==FOLDER_SYS); if(!families.items.isEmpty()) emit fontsRemoved(families); } } KFI_DBUG << "updated list of fonts"; } } void FontInst::toggle(bool enable, const QString &family, quint32 style, bool inSystem, int pid, bool checkConfig) { KFI_DBUG; itsConnections.insert(pid); if(checkConfig) updateFontList(); EFolder folder=isSystem || inSystem ? FOLDER_SYS : FOLDER_USER; FamilyCont::ConstIterator fam; StyleCont::ConstIterator st; int result=findFont(family, style, folder, fam, st) ? (int)STATUS_OK : (int)KIO::ERR_DOES_NOT_EXIST; if(STATUS_OK==result) { FileCont files((*st).files()), toggledFiles; FileCont::ConstIterator it(files.begin()), end(files.end()); QHash movedFonts; QHash movedAssoc; QSet modifiedDirs; for(; it!=end && 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 the font is a system font, and we're not root, then just go through the actions here - so // that we can build the list of changes that would happen... if((inSystem && !isSystem) || 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 && STATUS_OK==result; ++ait) { to=Misc::getDir(*ait)+ QString(enable ? Misc::unhide(Misc::getFile(*ait)) : Misc::hide(Misc::getFile(*ait))); if(to!=*ait) { if((inSystem && !isSystem) || renameFontFile(*ait, to)) { movedAssoc[*ait]=to; } else { result=KIO::ERR_WRITE_ACCESS_DENIED; } } } } else { result=KIO::ERR_WRITE_ACCESS_DENIED; } } } if(inSystem && !isSystem) { Family toggleFam((*fam).name()); toggleFam.add(*st); QVariantMap args; args["method"] = "toggle"; QString xml; QTextStream str(&xml); toggleFam.toXml(false, str); args["xml"] = xml; args["enable"] = enable; result=performAction(args); } if(STATUS_OK==result) { Family addFam((*fam).name()), delFam((*fam).name()); Style addStyle((*st).value(), (*st).scalable(), (*st).writingSystems()), delStyle((*st).value(), (*st).scalable(), (*st).writingSystems()); addStyle.setFiles(toggledFiles); addFam.add(addStyle); delStyle.setFiles(files); delFam.add(delStyle); (*st).setFiles(toggledFiles); theFolders[folder].addModifiedDirs(modifiedDirs); emit fontsAdded(Families(addFam, FOLDER_SYS==folder)); emit fontsRemoved(Families(delFam, FOLDER_SYS==folder)); theFolders[folder].setDisabledDirty(); } else // un-move fonts! { 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()); } } emit status(pid, result); itsConnectionsTimer->start(constConnectionsTimeout); itsFontListTimer->start(constFontListTimeout); } void FontInst::addModifedSysFolders(const Family &family) { StyleCont::ConstIterator style(family.styles().begin()), styleEnd(family.styles().end()); for(; style!=styleEnd; ++style) { FileCont::ConstIterator file((*style).files().begin()), fileEnd((*style).files().end()); for(; file!=fileEnd; ++file) theFolders[FOLDER_SYS].addModifiedDir(Misc::getDir((*file).path())); } } void FontInst::checkConnections() { KFI_DBUG; QSet::ConstIterator it(itsConnections.begin()), end(itsConnections.end()); QSet remove; for(; it!=end; ++it) if(0!=kill(*it, 0)) remove.insert(*it); itsConnections.subtract(remove); } bool FontInst::findFontReal(const QString &family, const QString &style, EFolder folder, FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st) { KFI_DBUG; Family f(family); fam=theFolders[folder].fonts().find(f); if(theFolders[folder].fonts().end()==fam) return false; StyleCont::ConstIterator end((*fam).styles().end()); for(st=(*fam).styles().begin(); st!=end; ++st) if(FC::createStyleName((*st).value())==style) return true; return false; } bool FontInst::findFont(const QString &font, EFolder folder, FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st, bool updateList) { KFI_DBUG; QString family, style; decompose(font, family, style); if(!findFontReal(family, style, folder, fam, st)) { if(updateList) { // Not found, so refresh font list and try again... updateFontList(); return findFontReal(family, style, folder, fam, st); } else { return false; } } return true; } bool FontInst::findFontReal(const QString &family, quint32 style, EFolder folder, FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st) { KFI_DBUG; fam=theFolders[folder].fonts().find(Family(family)); if(theFolders[folder].fonts().end()==fam) return false; else { st=(*fam).styles().find(style); return (*fam).styles().end()!=st; } } bool FontInst::findFont(const QString &family, quint32 style, EFolder folder, FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st, bool updateList) { KFI_DBUG; if(!findFontReal(family, style, folder, fam, st)) { if(updateList) { // Not found, so refresh font list and try again... updateFontList(); return findFontReal(family, style, folder, fam, st); } else { return false; } } return true; } int FontInst::performAction(const QVariantMap &args) { KAuth::Action action("org.kde.fontinst.manage"); action.setHelperId("org.kde.fontinst"); action.setArguments(args); KFI_DBUG << "Call " << args["method"].toString() << " on helper"; itsFontListTimer->stop(); itsConnectionsTimer->stop(); KAuth::ExecuteJob* j = action.execute(); j->exec(); if (j->error()) { qWarning() << "kauth action failed" << j->errorString() << j->errorText(); //error is a KAuth::ActionReply::Error rest of this code expects KIO error codes which are extended by EStatus switch (j->error()) { case KAuth::ActionReply::Error::UserCancelledError: return KIO::ERR_USER_CANCELED; case KAuth::ActionReply::Error::AuthorizationDeniedError: /*fall through*/ case KAuth::ActionReply::Error::NoSuchActionError: return KIO::ERR_COULD_NOT_AUTHENTICATE; default: return KIO::ERR_INTERNAL; } return KIO::ERR_INTERNAL; } KFI_DBUG << "Success!"; return STATUS_OK; } }