File indexing completed on 2025-01-26 05:08:06

0001 /*
0002     SPDX-FileCopyrightText: 2003-2009 Craig Drummond <craig@kde.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "FontInst.h"
0007 #include "Fc.h"
0008 #include "Misc.h"
0009 #include "Utils.h"
0010 #include "WritingSystems.h"
0011 #include "fontinstadaptor.h"
0012 #include "kfontinst_debug.h"
0013 
0014 #include <KAuth/Action>
0015 #include <KAuth/ActionReply>
0016 #include <KAuth/ExecuteJob>
0017 #include <KAuth/HelperSupport>
0018 #include <QDebug>
0019 #include <QTimer>
0020 #include <fontconfig/fontconfig.h>
0021 #include <kio/global.h>
0022 #include <signal.h>
0023 #include <sys/types.h>
0024 #include <unistd.h>
0025 
0026 namespace KFI
0027 {
0028 static void decompose(const QString &name, QString &family, QString &style)
0029 {
0030     int commaPos = name.lastIndexOf(',');
0031 
0032     family = -1 == commaPos ? name : name.left(commaPos);
0033     style = -1 == commaPos ? KFI_WEIGHT_REGULAR.untranslatedText() : name.mid(commaPos + 2);
0034 }
0035 
0036 static bool isSystem = false;
0037 static Folder theFolders[FontInst::FOLDER_COUNT];
0038 static const int constSystemReconfigured = -1;
0039 static const int constConnectionsTimeout = 30 * 1000;
0040 static const int constFontListTimeout = 10 * 1000;
0041 
0042 typedef void (*SignalHandler)(int);
0043 
0044 static void registerSignalHandler(SignalHandler handler)
0045 {
0046     if (!handler) {
0047         handler = SIG_DFL;
0048     }
0049 
0050     sigset_t mask;
0051     sigemptyset(&mask);
0052 
0053 #ifdef SIGSEGV
0054     signal(SIGSEGV, handler);
0055     sigaddset(&mask, SIGSEGV);
0056 #endif
0057 #ifdef SIGFPE
0058     signal(SIGFPE, handler);
0059     sigaddset(&mask, SIGFPE);
0060 #endif
0061 #ifdef SIGILL
0062     signal(SIGILL, handler);
0063     sigaddset(&mask, SIGILL);
0064 #endif
0065 #ifdef SIGABRT
0066     signal(SIGABRT, handler);
0067     sigaddset(&mask, SIGABRT);
0068 #endif
0069 
0070     sigprocmask(SIG_UNBLOCK, &mask, nullptr);
0071 }
0072 
0073 void signalHander(int)
0074 {
0075     static bool inHandler = false;
0076 
0077     if (!inHandler) {
0078         inHandler = true;
0079         theFolders[isSystem ? FontInst::FOLDER_SYS : FontInst::FOLDER_USER].saveDisabled();
0080         inHandler = false;
0081     }
0082 }
0083 
0084 FontInst::FontInst()
0085 {
0086     isSystem = Misc::root();
0087     registerTypes();
0088 
0089     new FontinstAdaptor(this);
0090     QDBusConnection bus = QDBusConnection::sessionBus();
0091 
0092     // qDebug() << "Connecting to session bus";
0093     if (!bus.registerService(OrgKdeFontinstInterface::staticInterfaceName())) {
0094         // qDebug() << "Failed to register service!";
0095         ::exit(-1);
0096     }
0097     if (!bus.registerObject(FONTINST_PATH, this)) {
0098         // qDebug() << "Failed to register object!";
0099         ::exit(-1);
0100     }
0101 
0102     registerSignalHandler(signalHander);
0103     m_connectionsTimer = new QTimer(this);
0104     m_fontListTimer = new QTimer(this);
0105     connect(m_connectionsTimer, &QTimer::timeout, this, &FontInst::connectionsTimeout);
0106     connect(m_fontListTimer, &QTimer::timeout, this, &FontInst::fontListTimeout);
0107     m_connectionsTimer->start(constConnectionsTimeout);
0108     m_fontListTimer->start(constFontListTimeout);
0109 
0110     for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
0111         theFolders[i].init(FOLDER_SYS == i, isSystem);
0112     }
0113 
0114     updateFontList(false);
0115 }
0116 
0117 FontInst::~FontInst()
0118 {
0119     for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
0120         theFolders[i].saveDisabled();
0121     }
0122 }
0123 
0124 void FontInst::list(int folders, int pid)
0125 {
0126     // qDebug() << folders << pid;
0127 
0128     m_connections.insert(pid);
0129     updateFontList(false);
0130     QList<KFI::Families> fonts;
0131 
0132     for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
0133         if (0 == folders || folders & (1 << i)) {
0134             fonts += theFolders[i].list();
0135         }
0136     }
0137 
0138     m_connectionsTimer->start(constConnectionsTimeout);
0139     m_fontListTimer->start(constFontListTimeout);
0140     Q_EMIT fontList(pid, fonts);
0141 }
0142 
0143 void FontInst::statFont(const QString &name, int folders, int pid)
0144 {
0145     // qDebug() << name << folders << pid;
0146 
0147     bool checkSystem = 0 == folders || folders & SYS_MASK || isSystem, checkUser = 0 == folders || (folders & USR_MASK && !isSystem);
0148     FamilyCont::ConstIterator fam;
0149     StyleCont::ConstIterator st;
0150 
0151     m_connections.insert(pid);
0152     if ((checkSystem && findFont(name, FOLDER_SYS, fam, st)) || (checkUser && findFont(name, FOLDER_USER, fam, st, !checkSystem))) {
0153         Family rv((*fam).name());
0154         rv.add(*st);
0155         // qDebug() << "Found font, Q_EMIT details...";
0156         Q_EMIT fontStat(pid, rv);
0157     } else {
0158         // qDebug() << "Font not found, Q_EMIT empty details...";
0159         Q_EMIT fontStat(pid, Family(name));
0160     }
0161 }
0162 
0163 void FontInst::install(const QString &file, bool createAfm, bool toSystem, int pid, bool checkConfig)
0164 {
0165     // qDebug() << file << createAfm << toSystem << pid << checkConfig;
0166 
0167     m_connections.insert(pid);
0168 
0169     if (checkConfig) {
0170         updateFontList();
0171     }
0172 
0173     EFolder folder = isSystem || toSystem ? FOLDER_SYS : FOLDER_USER;
0174     Family font;
0175     Utils::EFileType type = Utils::check(file, font);
0176 
0177     int result = Utils::FILE_BITMAP == type && !FC::bitmapsEnabled() ? STATUS_BITMAPS_DISABLED : Utils::FILE_INVALID == type ? STATUS_NOT_FONT_FILE : STATUS_OK;
0178 
0179     if (STATUS_OK == result) {
0180         if (Utils::FILE_AFM != type && Utils::FILE_PFM != type) {
0181             for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT) && STATUS_OK == result; ++i) {
0182                 if (theFolders[i].contains(font.name(), (*font.styles().begin()).value())) {
0183                     result = STATUS_ALREADY_INSTALLED;
0184                 }
0185             }
0186         }
0187 
0188         if (STATUS_OK == result) {
0189             QString name(Misc::modifyName(Misc::getFile(file))), destFolder(Misc::getDestFolder(theFolders[folder].location(), name));
0190 
0191             result = Utils::FILE_AFM != type && Utils::FILE_PFM != type && Misc::fExists(destFolder + name) ? (int)KIO::ERR_FILE_ALREADY_EXIST : (int)STATUS_OK;
0192             if (STATUS_OK == result) {
0193                 if (toSystem && !isSystem) {
0194                     // qDebug() << "Send request to system helper" << file << destFolder << name;
0195                     QVariantMap args;
0196                     args["method"] = "install";
0197                     args["file"] = file;
0198                     args["name"] = name;
0199                     args["destFolder"] = destFolder;
0200                     args["createAfm"] = createAfm;
0201                     args["type"] = (int)type;
0202                     result = performAction(args);
0203                 } else {
0204                     if (!Misc::dExists(destFolder)) {
0205                         result = Misc::createDir(destFolder) ? (int)STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED;
0206                     }
0207 
0208                     if (STATUS_OK == result) {
0209                         result = QFile::copy(file, destFolder + name) ? (int)STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED;
0210                     }
0211 
0212                     if (STATUS_OK == result) {
0213                         Misc::setFilePerms(QFile::encodeName(destFolder + name));
0214                         if ((Utils::FILE_SCALABLE == type || Utils::FILE_PFM == type) && createAfm) {
0215                             Utils::createAfm(destFolder + name, type);
0216                         }
0217                     }
0218                 }
0219 
0220                 if (STATUS_OK == result && Utils::FILE_AFM != type && Utils::FILE_PFM != type) {
0221                     StyleCont::ConstIterator st(font.styles().begin());
0222                     FileCont::ConstIterator f((*st).files().begin());
0223                     File df(destFolder + name, (*f).foundry(), (*f).index());
0224 
0225                     (*st).clearFiles();
0226                     (*st).add(df);
0227                     theFolders[folder].add(font);
0228                     theFolders[folder].addModifiedDir(destFolder);
0229                     Q_EMIT fontsAdded(Families(font, FOLDER_SYS == folder));
0230                 }
0231             }
0232         }
0233     }
0234 
0235     Q_EMIT status(pid, result);
0236     m_connectionsTimer->start(constConnectionsTimeout);
0237     m_fontListTimer->start(constFontListTimeout);
0238 }
0239 
0240 void FontInst::uninstall(const QString &family, quint32 style, bool fromSystem, int pid, bool checkConfig)
0241 {
0242     // qDebug() << family << style << fromSystem << pid << checkConfig;
0243 
0244     m_connections.insert(pid);
0245 
0246     if (checkConfig) {
0247         updateFontList();
0248     }
0249 
0250     EFolder folder = isSystem || fromSystem ? FOLDER_SYS : FOLDER_USER;
0251     FamilyCont::ConstIterator fam;
0252     StyleCont::ConstIterator st;
0253     int result = findFont(family, style, folder, fam, st) ? (int)STATUS_OK : (int)KIO::ERR_DOES_NOT_EXIST;
0254 
0255     if (STATUS_OK == result) {
0256         Family del((*fam).name());
0257         Style s((*st).value(), (*st).scalable(), (*st).writingSystems());
0258         FileCont files((*st).files());
0259         FileCont::ConstIterator it(files.begin()), end(files.end());
0260 
0261         if (fromSystem && !isSystem) {
0262             QStringList fileList;
0263             bool wasDisabled(false);
0264 
0265             for (; it != end; ++it) {
0266                 fileList.append((*it).path());
0267                 theFolders[FOLDER_SYS].addModifiedDir(Misc::getDir((*it).path()));
0268                 if (!wasDisabled && Misc::isHidden(Misc::getFile((*it).path()))) {
0269                     wasDisabled = true;
0270                 }
0271             }
0272             QVariantMap args;
0273             args["method"] = "uninstall";
0274             args["files"] = fileList;
0275             result = performAction(args);
0276 
0277             if (STATUS_OK == result) {
0278                 FileCont empty;
0279                 s.setFiles(files);
0280                 (*st).setFiles(empty);
0281                 if (wasDisabled) {
0282                     theFolders[FOLDER_SYS].setDisabledDirty();
0283                 }
0284             }
0285         } else {
0286             for (; it != end; ++it) {
0287                 if (!Misc::fExists((*it).path()) || QFile::remove((*it).path())) {
0288                     // Also remove any AFM or PFM files...
0289                     QStringList other;
0290                     Misc::getAssociatedFiles((*it).path(), other);
0291                     QStringList::ConstIterator oit(other.constBegin()), oend(other.constEnd());
0292                     for (; oit != oend; ++oit) {
0293                         QFile::remove(*oit);
0294                     }
0295 
0296                     theFolders[folder].addModifiedDir(Misc::getDir((*it).path()));
0297                     (*st).remove(*it);
0298                     s.add(*it);
0299                     if (!theFolders[folder].disabledDirty() && Misc::isHidden(Misc::getFile((*it).path()))) {
0300                         theFolders[folder].setDisabledDirty();
0301                     }
0302                 }
0303             }
0304         }
0305 
0306         if (STATUS_OK == result) {
0307             if ((*st).files().isEmpty()) {
0308                 (*fam).remove(*st);
0309                 if ((*fam).styles().isEmpty()) {
0310                     theFolders[folder].removeFont(*fam);
0311                 }
0312             } else {
0313                 result = STATUS_PARTIAL_DELETE;
0314             }
0315             del.add(s);
0316         }
0317         Q_EMIT fontsRemoved(Families(del, FOLDER_SYS == folder));
0318     }
0319     // qDebug() << "status" << result;
0320     Q_EMIT status(pid, result);
0321 
0322     m_connectionsTimer->start(constConnectionsTimeout);
0323     m_fontListTimer->start(constFontListTimeout);
0324 }
0325 
0326 void FontInst::uninstall(const QString &name, bool fromSystem, int pid, bool checkConfig)
0327 {
0328     // qDebug() << name << fromSystem << pid << checkConfig;
0329 
0330     FamilyCont::ConstIterator fam;
0331     StyleCont::ConstIterator st;
0332     if (findFont(name, fromSystem || isSystem ? FOLDER_SYS : FOLDER_USER, fam, st)) {
0333         uninstall((*fam).name(), (*st).value(), fromSystem, pid, checkConfig);
0334     } else {
0335         Q_EMIT status(pid, KIO::ERR_DOES_NOT_EXIST);
0336     }
0337 }
0338 
0339 void FontInst::move(const QString &family, quint32 style, bool toSystem, int pid, bool checkConfig)
0340 {
0341     // qDebug() << family << style << toSystem << pid << checkConfig;
0342 
0343     m_connections.insert(pid);
0344     if (checkConfig) {
0345         updateFontList();
0346     }
0347 
0348     if (isSystem) {
0349         Q_EMIT status(pid, KIO::ERR_UNSUPPORTED_ACTION);
0350     } else {
0351         FamilyCont::ConstIterator fam;
0352         StyleCont::ConstIterator st;
0353         EFolder from = toSystem ? FOLDER_USER : FOLDER_SYS, to = toSystem ? FOLDER_SYS : FOLDER_USER;
0354 
0355         if (findFont(family, style, from, fam, st)) {
0356             FileCont::ConstIterator it((*st).files().begin()), end((*st).files().end());
0357             QStringList files;
0358 
0359             for (; it != end; ++it) {
0360                 files.append((*it).path());
0361                 theFolders[from].addModifiedDir(Misc::getDir((*it).path()));
0362                 // Actual 'to' folder does not really matter, as we only want to call fc-cache
0363                 // ...actual folders only matter for xreating fonts.dir, etc, and we wont be doing this...
0364                 theFolders[to].addModifiedDir(theFolders[to].location());
0365             }
0366 
0367             QVariantMap args;
0368             args["method"] = "move";
0369             args["files"] = files;
0370             args["toSystem"] = toSystem;
0371             args["dest"] = theFolders[to].location();
0372             args["uid"] = getuid();
0373             args["gid"] = getgid();
0374             int result = performAction(args);
0375 
0376             if (STATUS_OK == result) {
0377                 updateFontList();
0378             }
0379             Q_EMIT status(pid, result);
0380         } else {
0381             // qDebug() << "does not exist";
0382             Q_EMIT status(pid, KIO::ERR_DOES_NOT_EXIST);
0383         }
0384     }
0385 
0386     m_connectionsTimer->start(constConnectionsTimeout);
0387     m_fontListTimer->start(constFontListTimeout);
0388 }
0389 
0390 static bool renameFontFile(const QString &from, const QString &to, int uid = -1, int gid = -1)
0391 {
0392     if (!QFile::rename(from, to)) {
0393         return false;
0394     }
0395 
0396     QByteArray dest(QFile::encodeName(to));
0397     Misc::setFilePerms(dest);
0398     if (-1 != uid && -1 != gid) {
0399         ::chown(dest.data(), uid, gid);
0400     }
0401     return true;
0402 }
0403 
0404 void FontInst::enable(const QString &family, quint32 style, bool inSystem, int pid, bool checkConfig)
0405 {
0406     // qDebug() << family << style << inSystem << pid << checkConfig;
0407     toggle(true, family, style, inSystem, pid, checkConfig);
0408 }
0409 
0410 void FontInst::disable(const QString &family, quint32 style, bool inSystem, int pid, bool checkConfig)
0411 {
0412     // qDebug() << family << style << inSystem << pid << checkConfig;
0413     toggle(false, family, style, inSystem, pid, checkConfig);
0414 }
0415 
0416 void FontInst::removeFile(const QString &family, quint32 style, const QString &file, bool fromSystem, int pid, bool checkConfig)
0417 {
0418     // qDebug() << family << style << file << fromSystem << pid << checkConfig;
0419 
0420     m_connections.insert(pid);
0421 
0422     if (checkConfig) {
0423         updateFontList();
0424     }
0425 
0426     // First find the family/style
0427     EFolder folder = isSystem || fromSystem ? FOLDER_SYS : FOLDER_USER;
0428     FamilyCont::ConstIterator fam;
0429     StyleCont::ConstIterator st;
0430     int result = findFont(family, style, folder, fam, st) ? (int)STATUS_OK : (int)KIO::ERR_DOES_NOT_EXIST;
0431 
0432     if (STATUS_OK == result) {
0433         // Family/style found - now check that the requested file is *within* the same folder as one
0434         // of the files linked to this font...
0435         FileCont files((*st).files());
0436         FileCont::ConstIterator it(files.begin()), end(files.end());
0437         QString dir(Misc::getDir(file));
0438 
0439         result = KIO::ERR_DOES_NOT_EXIST;
0440         for (; it != end && STATUS_OK != result; ++it) {
0441             if (Misc::getDir((*it).path()) == dir) {
0442                 result = STATUS_OK;
0443             }
0444         }
0445 
0446         if (STATUS_OK == result) {
0447             // OK, found folder - so can now proceed to delete the file...
0448             if (fromSystem && !isSystem) {
0449                 QVariantMap args;
0450                 args["method"] = "removeFile";
0451                 args["file"] = file;
0452                 result = performAction(args);
0453             } else {
0454                 result = Misc::fExists(file) ? QFile::remove(file) ? (int)STATUS_OK : (int)KIO::ERR_WRITE_ACCESS_DENIED : (int)KIO::ERR_DOES_NOT_EXIST;
0455             }
0456 
0457             if (STATUS_OK == result) {
0458                 theFolders[folder].addModifiedDir(dir);
0459             }
0460         }
0461     }
0462 
0463     Q_EMIT status(pid, result);
0464 }
0465 
0466 void FontInst::reconfigure(int pid, bool force)
0467 {
0468     // qDebug() << pid << force;
0469     bool sysModified(theFolders[FOLDER_SYS].isModified());
0470 
0471     saveDisabled();
0472 
0473     // qDebug() << theFolders[FOLDER_USER].isModified() << sysModified;
0474     if (!isSystem && (force || theFolders[FOLDER_USER].isModified())) {
0475         theFolders[FOLDER_USER].configure(force);
0476     }
0477 
0478     if (sysModified) {
0479         if (isSystem) {
0480             theFolders[FOLDER_SYS].configure();
0481         } else {
0482             QVariantMap args;
0483             args["method"] = "reconfigure";
0484             performAction(args);
0485             theFolders[FOLDER_SYS].clearModified();
0486         }
0487     }
0488 
0489     m_connectionsTimer->start(constConnectionsTimeout);
0490     m_fontListTimer->start(constFontListTimeout);
0491 
0492     updateFontList();
0493     Q_EMIT status(pid, isSystem ? constSystemReconfigured : STATUS_OK);
0494 }
0495 
0496 QString FontInst::folderName(bool sys)
0497 {
0498     return theFolders[sys || isSystem ? FOLDER_SYS : FOLDER_USER].location();
0499 }
0500 
0501 void FontInst::saveDisabled()
0502 {
0503     if (isSystem) {
0504         theFolders[FOLDER_SYS].saveDisabled();
0505     } else {
0506         for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
0507             if (FOLDER_SYS == i && !isSystem) {
0508                 if (theFolders[i].disabledDirty()) {
0509                     QVariantMap args;
0510                     args["method"] = "saveDisabled";
0511                     performAction(args);
0512                     theFolders[i].saveDisabled();
0513                 }
0514             } else {
0515                 theFolders[i].saveDisabled();
0516             }
0517         }
0518     }
0519 }
0520 
0521 void FontInst::connectionsTimeout()
0522 {
0523     bool canExit(true);
0524 
0525     // qDebug() << "exiting";
0526     checkConnections();
0527 
0528     for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
0529         if (theFolders[i].disabledDirty()) {
0530             canExit = false;
0531         }
0532         theFolders[i].saveDisabled();
0533     }
0534 
0535     if (0 == m_connections.count()) {
0536         if (canExit) {
0537             qApp->exit(0);
0538         } else { // Try again later...
0539             m_connectionsTimer->start(constConnectionsTimeout);
0540         }
0541     }
0542 }
0543 
0544 void FontInst::fontListTimeout()
0545 {
0546     updateFontList(true);
0547     m_fontListTimer->start(constFontListTimeout);
0548 }
0549 
0550 void FontInst::updateFontList(bool emitChanges)
0551 {
0552     // For some reason just the "!FcConfigUptoDate(0)" check does not always work :-(
0553     FcBool fcModified = !FcConfigUptoDate(nullptr);
0554 
0555     if (fcModified || theFolders[FOLDER_SYS].fonts().isEmpty() || (!isSystem && theFolders[FOLDER_USER].fonts().isEmpty())
0556         || theFolders[FOLDER_SYS].disabledDirty() || (!isSystem && theFolders[FOLDER_USER].disabledDirty())) {
0557         // qDebug() << "Need to refresh font lists";
0558         if (fcModified) {
0559             // qDebug() << "Re-init FC";
0560             if (!FcInitReinitialize()) {
0561                 // qDebug() << "Re-init failed????";
0562             }
0563         }
0564 
0565         Folder::Flat old[FOLDER_COUNT];
0566 
0567         if (emitChanges) {
0568             // qDebug() << "Flatten existing font lists";
0569             for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
0570                 old[i] = theFolders[i].flatten();
0571             }
0572         }
0573 
0574         saveDisabled();
0575 
0576         for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
0577             theFolders[i].clearFonts();
0578         }
0579 
0580         // qDebug() << "update list of fonts";
0581 
0582         FcPattern *pat = FcPatternCreate();
0583         FcObjectSet *os = FcObjectSetBuild(FC_FILE,
0584                                            FC_FAMILY,
0585                                            FC_FAMILYLANG,
0586                                            FC_WEIGHT,
0587                                            FC_LANG,
0588                                            FC_CHARSET,
0589                                            FC_SCALABLE,
0590 #ifndef KFI_FC_NO_WIDTHS
0591                                            FC_WIDTH,
0592 #endif
0593                                            FC_SLANT,
0594                                            FC_INDEX,
0595                                            FC_FOUNDRY,
0596                                            (void *)nullptr);
0597 
0598         FcFontSet *list = FcFontList(nullptr, pat, os);
0599 
0600         FcPatternDestroy(pat);
0601         FcObjectSetDestroy(os);
0602 
0603         theFolders[FOLDER_SYS].loadDisabled();
0604         if (!isSystem) {
0605             theFolders[FOLDER_USER].loadDisabled();
0606         }
0607 
0608         if (list) {
0609             QString home(Misc::dirSyntax(QDir::homePath()));
0610 
0611             for (int i = 0; i < list->nfont; i++) {
0612                 QString fileName(Misc::fileSyntax(FC::getFcString(list->fonts[i], FC_FILE)));
0613 
0614                 if (!fileName.isEmpty() && Misc::fExists(fileName)) // && 0!=fileName.indexOf(constDefomaLocation))
0615                 {
0616                     QString family, foundry;
0617                     quint32 styleVal;
0618                     int index;
0619                     qulonglong writingSystems(WritingSystems::instance()->get(list->fonts[i]));
0620                     FcBool scalable = FcFalse;
0621 
0622                     if (FcResultMatch != FcPatternGetBool(list->fonts[i], FC_SCALABLE, 0, &scalable)) {
0623                         scalable = FcFalse;
0624                     }
0625 
0626                     FC::getDetails(list->fonts[i], family, styleVal, index, foundry);
0627                     FamilyCont::ConstIterator fam = theFolders[isSystem || 0 != fileName.indexOf(home) ? FOLDER_SYS : FOLDER_USER].addFont(Family(family));
0628                     StyleCont::ConstIterator style = (*fam).add(Style(styleVal));
0629                     FileCont::ConstIterator file = (*style).add(File(fileName, foundry, index));
0630                     Q_UNUSED(file);
0631 
0632                     (*style).setWritingSystems((*style).writingSystems() | writingSystems);
0633                     if (scalable) {
0634                         (*style).setScalable();
0635                     }
0636                 }
0637             }
0638 
0639             FcFontSetDestroy(list);
0640         }
0641 
0642         if (emitChanges) {
0643             // qDebug() << "Look for differences";
0644             for (int i = 0; i < (isSystem ? 1 : FOLDER_COUNT); ++i) {
0645                 // qDebug() << "Flatten, and take copies...";
0646                 Folder::Flat newList = theFolders[i].flatten(), onlyNew = newList;
0647 
0648                 // qDebug() << "Determine differences...";
0649                 onlyNew.subtract(old[i]);
0650                 old[i].subtract(newList);
0651 
0652                 // qDebug() << "Emit changes...";
0653                 Families families = onlyNew.build(isSystem || i == FOLDER_SYS);
0654 
0655                 if (!families.items.isEmpty()) {
0656                     Q_EMIT fontsAdded(families);
0657                 }
0658 
0659                 families = old[i].build(isSystem || i == FOLDER_SYS);
0660                 if (!families.items.isEmpty()) {
0661                     Q_EMIT fontsRemoved(families);
0662                 }
0663             }
0664         }
0665         // qDebug() << "updated list of fonts";
0666     }
0667 }
0668 
0669 void FontInst::toggle(bool enable, const QString &family, quint32 style, bool inSystem, int pid, bool checkConfig)
0670 {
0671     m_connections.insert(pid);
0672 
0673     if (checkConfig) {
0674         updateFontList();
0675     }
0676 
0677     EFolder folder = isSystem || inSystem ? FOLDER_SYS : FOLDER_USER;
0678     FamilyCont::ConstIterator fam;
0679     StyleCont::ConstIterator st;
0680     int result = findFont(family, style, folder, fam, st) ? (int)STATUS_OK : (int)KIO::ERR_DOES_NOT_EXIST;
0681 
0682     if (STATUS_OK == result) {
0683         FileCont files((*st).files()), toggledFiles;
0684         FileCont::ConstIterator it(files.begin()), end(files.end());
0685         QHash<File, QString> movedFonts;
0686         QHash<QString, QString> movedAssoc;
0687         QSet<QString> modifiedDirs;
0688 
0689         for (; it != end && STATUS_OK == result; ++it) {
0690             QString to = Misc::getDir((*it).path()) + QString(enable ? Misc::unhide(Misc::getFile((*it).path())) : Misc::hide(Misc::getFile((*it).path())));
0691             if (to != (*it).path()) {
0692                 // qDebug() << "MOVE:" << (*it).path() << " to " << to;
0693                 // If the font is a system font, and we're not root, then just go through the actions here - so
0694                 // that we can build the list of changes that would happen...
0695                 if ((inSystem && !isSystem) || renameFontFile((*it).path(), to)) {
0696                     modifiedDirs.insert(Misc::getDir(enable ? to : (*it).path()));
0697                     toggledFiles.insert(File(to, (*it).foundry(), (*it).index()));
0698                     // Now try to move an associated AFM or PFM files...
0699                     QStringList assoc;
0700 
0701                     movedFonts[*it] = to;
0702                     Misc::getAssociatedFiles((*it).path(), assoc);
0703 
0704                     QStringList::ConstIterator ait(assoc.constBegin()), aend(assoc.constEnd());
0705 
0706                     for (; ait != aend && STATUS_OK == result; ++ait) {
0707                         to = Misc::getDir(*ait) + QString(enable ? Misc::unhide(Misc::getFile(*ait)) : Misc::hide(Misc::getFile(*ait)));
0708 
0709                         if (to != *ait) {
0710                             if ((inSystem && !isSystem) || renameFontFile(*ait, to)) {
0711                                 movedAssoc[*ait] = to;
0712                             } else {
0713                                 result = KIO::ERR_WRITE_ACCESS_DENIED;
0714                             }
0715                         }
0716                     }
0717                 } else {
0718                     result = KIO::ERR_WRITE_ACCESS_DENIED;
0719                 }
0720             }
0721         }
0722 
0723         if (inSystem && !isSystem) {
0724             Family toggleFam((*fam).name());
0725 
0726             toggleFam.add(*st);
0727             QVariantMap args;
0728             args["method"] = "toggle";
0729             QString xml;
0730             QTextStream str(&xml);
0731             toggleFam.toXml(false, str);
0732             args["xml"] = xml;
0733             args["enable"] = enable;
0734             result = performAction(args);
0735         }
0736 
0737         if (STATUS_OK == result) {
0738             Family addFam((*fam).name()), delFam((*fam).name());
0739             Style addStyle((*st).value(), (*st).scalable(), (*st).writingSystems()), delStyle((*st).value(), (*st).scalable(), (*st).writingSystems());
0740 
0741             addStyle.setFiles(toggledFiles);
0742             addFam.add(addStyle);
0743             delStyle.setFiles(files);
0744             delFam.add(delStyle);
0745             (*st).setFiles(toggledFiles);
0746 
0747             theFolders[folder].addModifiedDirs(modifiedDirs);
0748             Q_EMIT fontsAdded(Families(addFam, FOLDER_SYS == folder));
0749             Q_EMIT fontsRemoved(Families(delFam, FOLDER_SYS == folder));
0750 
0751             theFolders[folder].setDisabledDirty();
0752         } else // un-move fonts!
0753         {
0754             QHash<File, QString>::ConstIterator fit(movedFonts.constBegin()), fend(movedFonts.constEnd());
0755             QHash<QString, QString>::ConstIterator ait(movedAssoc.constBegin()), aend(movedAssoc.constEnd());
0756 
0757             for (; fit != fend; ++fit) {
0758                 renameFontFile(fit.value(), fit.key().path());
0759             }
0760             for (; ait != aend; ++ait) {
0761                 renameFontFile(ait.value(), ait.key());
0762             }
0763         }
0764     }
0765     Q_EMIT status(pid, result);
0766 
0767     m_connectionsTimer->start(constConnectionsTimeout);
0768     m_fontListTimer->start(constFontListTimeout);
0769 }
0770 
0771 void FontInst::addModifedSysFolders(const Family &family)
0772 {
0773     StyleCont::ConstIterator style(family.styles().begin()), styleEnd(family.styles().end());
0774 
0775     for (; style != styleEnd; ++style) {
0776         FileCont::ConstIterator file((*style).files().begin()), fileEnd((*style).files().end());
0777 
0778         for (; file != fileEnd; ++file) {
0779             theFolders[FOLDER_SYS].addModifiedDir(Misc::getDir((*file).path()));
0780         }
0781     }
0782 }
0783 
0784 void FontInst::checkConnections()
0785 {
0786     QSet<int>::ConstIterator it(m_connections.begin()), end(m_connections.end());
0787     QSet<int> remove;
0788 
0789     for (; it != end; ++it) {
0790         if (0 != kill(*it, 0)) {
0791             remove.insert(*it);
0792         }
0793     }
0794     m_connections.subtract(remove);
0795 }
0796 
0797 bool FontInst::findFontReal(const QString &family, const QString &style, EFolder folder, FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st)
0798 {
0799     Family f(family);
0800     fam = theFolders[folder].fonts().find(f);
0801     if (theFolders[folder].fonts().end() == fam) {
0802         return false;
0803     }
0804 
0805     StyleCont::ConstIterator end((*fam).styles().end());
0806     for (st = (*fam).styles().begin(); st != end; ++st) {
0807         if (FC::createStyleName((*st).value()) == style) {
0808             return true;
0809         }
0810     }
0811 
0812     return false;
0813 }
0814 
0815 bool FontInst::findFont(const QString &font, EFolder folder, FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st, bool updateList)
0816 {
0817     QString family, style;
0818 
0819     decompose(font, family, style);
0820 
0821     if (!findFontReal(family, style, folder, fam, st)) {
0822         if (updateList) {
0823             // Not found, so refresh font list and try again...
0824             updateFontList();
0825             return findFontReal(family, style, folder, fam, st);
0826         } else {
0827             return false;
0828         }
0829     }
0830 
0831     return true;
0832 }
0833 
0834 bool FontInst::findFontReal(const QString &family, quint32 style, EFolder folder, FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st)
0835 {
0836     fam = theFolders[folder].fonts().find(Family(family));
0837 
0838     if (theFolders[folder].fonts().end() == fam) {
0839         return false;
0840     } else {
0841         st = (*fam).styles().find(style);
0842 
0843         return (*fam).styles().end() != st;
0844     }
0845 }
0846 
0847 bool FontInst::findFont(const QString &family, quint32 style, EFolder folder, FamilyCont::ConstIterator &fam, StyleCont::ConstIterator &st, bool updateList)
0848 {
0849     if (!findFontReal(family, style, folder, fam, st)) {
0850         if (updateList) {
0851             // Not found, so refresh font list and try again...
0852             updateFontList();
0853             return findFontReal(family, style, folder, fam, st);
0854         } else {
0855             return false;
0856         }
0857     }
0858     return true;
0859 }
0860 
0861 int FontInst::performAction(const QVariantMap &args)
0862 {
0863     KAuth::Action action("org.kde.fontinst.manage");
0864 
0865     action.setHelperId("org.kde.fontinst");
0866     action.setArguments(args);
0867     // qDebug() << "Call " << args["method"].toString() << " on helper";
0868     m_fontListTimer->stop();
0869     m_connectionsTimer->stop();
0870 
0871     KAuth::ExecuteJob *j = action.execute();
0872     j->exec();
0873     if (j->error()) {
0874         qCWarning(KFONTINST_DEBUG) << "kauth action failed" << j->errorString() << j->errorText();
0875         // error is a KAuth::ActionReply::Error rest of this code expects KIO error codes which are extended by EStatus
0876         switch (j->error()) {
0877         case KAuth::ActionReply::Error::UserCancelledError:
0878             return KIO::ERR_USER_CANCELED;
0879         case KAuth::ActionReply::Error::AuthorizationDeniedError:
0880             /*fall through*/
0881         case KAuth::ActionReply::Error::NoSuchActionError:
0882             return KIO::ERR_CANNOT_AUTHENTICATE;
0883         default:
0884             return KIO::ERR_INTERNAL;
0885         }
0886         return KIO::ERR_INTERNAL;
0887     }
0888     // qDebug() << "Success!";
0889     return STATUS_OK;
0890 }
0891 
0892 }