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 }