not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kcontrol / kfontinst / kio / KioFonts.cpp
blobbaa3a536d4769e72de7c02b8efc52a98a0e6a864
1 /*
2 * KFontInst - KDE Font Installer
4 * Copyright 2003-2007 Craig Drummond <craig@kde.org>
6 * ----
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
24 #include <config-workspace.h>
25 #include "KioFonts.h"
26 #include <stdlib.h>
27 #ifdef HAVE_STDINT_H
28 #include <stdint.h>
29 #endif
30 #include <pwd.h>
31 #include <grp.h>
32 #include <sys/types.h>
33 #include <sys/errno.h>
34 #include <errno.h>
35 #include <utime.h>
36 #include <sys/time.h>
37 #include <sys/resource.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <kio/ioslave_defaults.h>
41 #include <KDE/KMimeType>
42 #include <KDE/KMessageBox>
43 #include <QtCore/QDir>
44 #include <QtCore/QDataStream>
45 #include <QtCore/QTextStream>
46 #include <QtCore/QRegExp>
47 #include <QtGui/QFontDatabase>
48 #include <KDE/KComponentData>
49 #include <kde_file.h>
50 #include <KDE/KTemporaryFile>
51 #include <KDE/SuProcess>
52 #include <KDE/KDebug>
53 #include <KDE/KStandardDirs>
54 #include <KDE/KMD5>
55 #include <KDE/KZip>
56 #include <KDE/KConfigGroup>
57 #include <kxftconfig.h>
58 #include <fontconfig/fontconfig.h>
59 #include "KfiConstants.h"
60 #include "Fc.h"
61 #include "Misc.h"
62 #if defined USE_POLICYKIT && USE_POLICYKIT==1
63 #include "FontInst.h"
64 #include "PolicyKitAuthenticator.h"
65 #include <QtDBus/QtDBus>
66 #include <QtDBus/QDBusConnection>
67 #include <signal.h>
68 #else
69 #include "SuProc.h"
70 #include "Socket.h"
71 #endif
72 #include <ctype.h>
74 // Enable the following so that all URLs are actually <family>, <style>, e.g.
75 // without #define: fonts:/arial.ttf
76 // with #define: fonts:/Arial, Regular.ttf
78 // Not enabled - as it messes things up a little with fonts/group files.
79 //#define KFI_KIO_ALL_URLS_HAVE_NAME
81 #define KFI_DBUG kDebug(7000) << '(' << time(NULL) << ')'
83 #define MAX_IPC_SIZE (1024*32)
84 #define DEFAULT_TIMEOUT 2 // Time between last mod and writing files...
85 #define FC_CACHE_CMD "fc-cache"
87 using namespace KDESu;
89 static const int constMaxFcCheckTime=10;
91 extern "C"
93 KDE_EXPORT int kdemain(int argc, char **argv);
96 static KFI::CKioFonts *slaveInstance=NULL;
98 void kioFontsExitHandler()
100 if(slaveInstance)
102 slaveInstance->cleanup();
103 slaveInstance=NULL;
107 int kdemain(int argc, char **argv)
109 if (argc != 4)
111 fprintf(stderr, "Usage: kio_" KFI_KIO_FONTS_PROTOCOL
112 " protocol domain-socket1 domain-socket2\n");
113 exit(-1);
116 KLocale::setMainCatalog(KFI_CATALOGUE);
118 KComponentData componentData("kio_" KFI_KIO_FONTS_PROTOCOL);
119 KFI::CKioFonts slave(argv[2], argv[3]);
121 atexit(kioFontsExitHandler);
122 slave.dispatchLoop();
124 return 0;
127 namespace KFI
130 static QString urlString(const KUrl &u)
132 KUrl url(u);
134 if(url.hasUser() && KFI_KIO_FONTS_PROTOCOL==url.protocol() && KFI_SYS_USER==url.user())
135 url.setUser(QString());
136 return url.prettyUrl();
139 static bool addCreateFolderCmd(const QString &folder, QList<CKioFonts::TCommand> &cmd)
141 if(!Misc::dExists(folder))
143 cmd.append(CKioFonts::TCommand(KFI::CMD_CREATE_DIR, folder));
144 return true;
147 return false;
150 inline bool isSysFolder(const QString &sect)
152 return i18n(KFI_KIO_FONTS_SYS)==sect || KFI_KIO_FONTS_SYS==sect;
155 inline bool isUserFolder(const QString &sect)
157 return i18n(KFI_KIO_FONTS_USER)==sect || KFI_KIO_FONTS_USER==sect;
160 inline bool isAllFolder(const QString &sect)
162 return i18n(KFI_KIO_FONTS_ALL)==sect || KFI_KIO_FONTS_ALL==sect;
165 #ifdef KFI_KIO_ALL_URLS_HAVE_NAME
166 static const char *constExtensions[]=
167 {".ttf", KFI_FONTS_PACKAGE, ".otf", ".pfa", ".pfb", ".ttc",
168 ".pcf", ".pcf.gz", ".bdf", ".bdf.gz", NULL };
170 static QString removeKnownExtension(const KUrl &url)
172 QString fname(url.fileName());
173 int pos;
175 for(int i=0; constExtensions[i]; ++i)
176 if(-1!=(pos=fname.lastIndexOf(QString::fromLatin1(constExtensions[i]), -1, Qt::CaseInsensitive)))
177 return fname.left(pos);
178 return fname;
181 #define removeMultipleExtension(A) removeKnownExtension(A)
182 static void addKnownExtension(QString &url, const CDisabledFonts::TFileList &files, const QString &name,
183 bool hidden)
185 if(files.count()>1)
187 if(hidden)
188 url+='.';
189 url+=name+QString::fromLatin1(KFI_FONTS_PACKAGE);
191 else
193 QString fileName(Misc::getFile(files.first()));
194 int pos(0);
196 for(int i=0; constExtensions[i]; ++i)
197 if(-1!=(pos=fileName.lastIndexOf(QString::fromLatin1(constExtensions[i]), -1,
198 Qt::CaseInsensitive)))
200 if(hidden)
201 url+='.';
202 url+=name+constExtensions[i];
203 return;
205 url+=fileName;
208 #else
209 static QString removeMultipleExtension(const KUrl &url)
211 QString fname(url.fileName());
212 int pos;
214 if(-1!=(pos=fname.lastIndexOf(QString::fromLatin1(KFI_FONTS_PACKAGE))))
215 fname=fname.left(pos);
217 return fname;
219 #endif
221 static QString modifyName(const QString &fname, bool toUpper=false)
223 static const char constSymbols[]={ '-', ' ', ':', ';', '/', '~', 0 };
225 QString rv(toUpper ? fname.toUpper() : fname.toLower());
227 for(int s=0; constSymbols[s]; ++s)
228 rv=rv.replace(constSymbols[s], '_');
230 return rv;
233 static bool checkFiles(const CDisabledFonts::TFileList &files)
235 CDisabledFonts::TFileList::ConstIterator it(files.begin()),
236 end(files.end());
238 for(; it!=end; ++it)
239 if(!Misc::fExists(*it))
240 return false;
241 return true;
244 inline int getSize(KIO::UDSEntry &entry)
246 return entry.numberValue(KIO::UDSEntry::UDS_SIZE, 0);
249 static int getSize(const QByteArray &file)
251 KDE_struct_stat buff;
253 if(-1!=KDE_lstat(file, &buff))
255 if (S_ISLNK(buff.st_mode))
257 char buffer2[1000];
258 int n=readlink(file, buffer2, 1000);
259 if(n!= -1)
260 buffer2[n]='\0';
262 if(-1==KDE_stat(file, &buff))
263 return -1;
265 return buff.st_size;
268 return -1;
271 static int getSize(const CDisabledFonts::TFileList &files)
273 CDisabledFonts::TFileList::ConstIterator it,
274 end=files.end();
275 int size=0;
277 for(it=files.begin(); it!=end; ++it)
278 size+=getSize(QFile::encodeName(*it));
280 return size;
284 // Return real filename
285 // if normal file, return file
286 // if link, return dest
287 static QString getReal(const QString &file)
289 QByteArray cPath(QFile::encodeName(file));
290 KDE_struct_stat buff;
292 if(-1!=KDE_lstat(cPath, &buff) && S_ISLNK(buff.st_mode))
294 char buffer2[1000];
295 int n=readlink(cPath, buffer2, 1000);
297 if(n!= -1)
298 buffer2[n]='\0';
301 if('.'==buffer2[0]) // Relative link...
303 QString linkDest(QString::fromLocal8Bit(buffer2));
304 QDir d(Misc::getDir(file)+Misc::getDir(linkDest));
306 return Misc::dirSyntax(d.canonicalPath())+Misc::getFile(linkDest);
308 else
309 return QString::fromLocal8Bit(buffer2);
312 return file;
316 // Get the list of files associated with a list of entries...
317 // 1. get any associated afm or pfms
318 // 2. Resolve any symlinks
319 // 3. Remove duplicates (due to symlik and real being in list)
320 static void getFontFiles(const CDisabledFonts::TFileList &entries, CDisabledFonts::TFileList &files,
321 bool removeSymLinks=true)
323 CDisabledFonts::TFileList::ConstIterator it,
324 end=entries.end();
326 for(it=entries.begin(); it!=end; ++it)
328 QStringList assoc;
329 QString file(removeSymLinks ? getReal(*it) : (*it).path);
331 if(-1==files.indexOf(file) && Misc::fExists(file))
332 files.append(CDisabledFonts::TFile(file, (*it).face, (*it).foundry));
334 Misc::getAssociatedFiles(*it, assoc);
336 if(assoc.count())
338 QStringList::const_iterator fIt,
339 fEnd=assoc.constEnd();
341 for(fIt=assoc.constBegin(); fIt!=fEnd; ++fIt)
343 file=removeSymLinks ? getReal(*fIt) : *fIt;
345 if(!files.contains(file) && Misc::fExists(file))
346 files.append(CDisabledFonts::TFile(file));
352 static bool isScalable(const QString &str)
354 QByteArray cFile(QFile::encodeName(str));
356 return Misc::checkExt(cFile, "ttf") || Misc::checkExt(cFile, "otf") || Misc::checkExt(cFile, "ttc") ||
357 Misc::checkExt(cFile, "pfa") || Misc::checkExt(cFile, "pfb");
360 static bool isATtc(const QByteArray &file)
363 // To speed things up, check the files extension 1st...
364 if(Misc::checkExt(file, "ttc"))
365 return true;
366 else
368 // No '.ttc' exension match, so try querying with FreeType...
369 int count=0;
370 FcPattern *pat=FcFreeTypeQuery((const FcChar8 *)(QFile::encodeName(file).constData()), 0, NULL, &count);
372 if(pat)
374 FcPatternDestroy(pat);
375 return count>1; // Only care if TTC has more than 1 face...
378 return false;
381 // Check if
382 // ...src and dest are the *same* ttc file -> TTC copy
383 // ...or that src does not exist, and a new dest does -> TTC move
384 static bool isSameTtc(const QString &src, const QString &dest)
386 static const int constMaxTimeDiff=20;
388 QByteArray cDest(QFile::encodeName(dest));
390 if(isATtc(cDest))
392 QByteArray cSrc(QFile::encodeName(src));
394 KDE_struct_stat srcStat,
395 destStat;
396 bool srcExists=-1!=KDE_lstat(cSrc, &srcStat),
397 destExists=-1!=KDE_lstat(cDest, &destStat);
399 // Check that file sizes are the same, and that the dest file is recent...
400 if(srcExists && destExists)
402 if(srcStat.st_size==destStat.st_size && abs(destStat.st_atime-time(NULL))<constMaxTimeDiff)
404 // Sizes match, so check md5 sums...
405 QFile srcFile(src),
406 destFile(dest);
408 if(srcFile.open(QIODevice::ReadOnly) && destFile.open(QIODevice::ReadOnly))
410 KMD5 srcMd5,
411 destMd5;
413 return srcMd5.update(srcFile) && destMd5.update(destFile) &&
414 srcMd5.verify(destMd5.rawDigest());
418 else // In case of move, after the 1st, src won't exist, but dest should (and should be recent!)
419 return destExists && abs(destStat.st_atime-time(NULL))<constMaxTimeDiff;
422 return false;
425 enum EUrlStatus
427 BAD_Url,
428 Url_OK,
429 REDIRECT_Url
432 static KUrl getRedirect(const KUrl &u)
434 // Go from fonts:/System to fonts:/
436 KUrl redirect(u);
437 QString path(u.path()),
438 sect(CKioFonts::getSect(path));
440 path.remove(sect);
441 path.replace("//", "/");
442 redirect.setPath(path);
444 KFI_DBUG << "Redirect from " << u.path() << " to " << redirect.path();
445 return redirect;
448 static bool nonRootSys(const KUrl &u)
450 return !Misc::root() && isSysFolder(CKioFonts::getSect(u.path()));
453 static bool writeAll(int fd, const char *buf, size_t len)
455 while(len>0)
457 ssize_t written=write(fd, buf, len);
458 if (written<0 && EINTR!=errno)
459 return false;
460 buf+=written;
461 len-=written;
463 return true;
466 static bool isAAfm(const QString &fname)
468 if(Misc::checkExt(QFile::encodeName(fname), "afm")) // CPD? Is this a necessary check?
470 QFile file(fname);
472 if(file.open(QIODevice::ReadOnly))
474 QTextStream stream(&file);
475 QString line;
477 for(int lc=0; lc<30 && !stream.atEnd(); ++lc)
479 line=stream.readLine();
481 if(line.contains("StartFontMetrics"))
483 file.close();
484 return true;
488 file.close();
492 return false;
495 static bool isAPfm(const QString &fname)
497 bool ok=false;
499 // I know extension checking is bad, but Ghostscript's pf2afm requires the pfm file to
500 // have the .pfm extension...
501 QByteArray name(QFile::encodeName(fname));
503 if(Misc::checkExt(name, "pfm"))
506 // OK, the extension matches, so perform a little contents checking...
507 FILE *f=fopen(name.constData(), "r");
509 if(f)
511 static const unsigned long constCopyrightLen = 60;
512 static const unsigned long constTypeToExt = 49;
513 static const unsigned long constExtToFname = 20;
514 static const unsigned long constExtLen = 30;
515 static const unsigned long constFontnameMin = 75;
516 static const unsigned long constFontnameMax = 512;
518 unsigned short version=0,
519 type=0,
520 extlen=0;
521 unsigned long length=0,
522 fontname=0,
523 fLength=0;
525 fseek(f, 0, SEEK_END);
526 fLength=ftell(f);
527 fseek(f, 0, SEEK_SET);
529 if(2==fread(&version, 1, 2, f) && // Read version
530 4==fread(&length, 1, 4, f) && // length...
531 length==fLength &&
532 0==fseek(f, constCopyrightLen, SEEK_CUR) && // Skip copyright notice...
533 2==fread(&type, 1, 2, f) &&
534 0==fseek(f, constTypeToExt, SEEK_CUR) &&
535 2==fread(&extlen, 1, 2, f) &&
536 extlen==constExtLen &&
537 0==fseek(f, constExtToFname, SEEK_CUR) &&
538 4==fread(&fontname, 1, 4, f) &&
539 fontname>constFontnameMin && fontname<constFontnameMax)
540 ok=true;
541 fclose(f);
545 return ok;
548 // This function is *only* used for the generation of AFMs from PFMs.
549 static bool isAType1(const QString &fname)
551 static const char * constStr="%!PS-AdobeFont-";
552 static const unsigned int constStrLen=15;
553 static const unsigned int constPfbOffset=6;
554 static const unsigned int constPfbLen=constStrLen+constPfbOffset;
556 QByteArray name(QFile::encodeName(fname));
557 char buffer[constPfbLen];
558 bool match=false;
560 if(Misc::checkExt(name, "pfa"))
562 FILE *f=fopen(name.constData(), "r");
564 if(f)
566 if(constStrLen==fread(buffer, 1, constStrLen, f))
567 match=0==memcmp(buffer, constStr, constStrLen);
568 fclose(f);
571 else if(Misc::checkExt(name, "pfb"))
573 static const char constPfbMarker = static_cast<char>(0x80);
575 FILE *f=fopen(name.constData(), "r");
577 if(f)
579 if(constPfbLen==fread(buffer, 1, constPfbLen, f))
580 match=buffer[0]==constPfbMarker && 0==memcmp(&buffer[constPfbOffset], constStr,
581 constStrLen);
582 fclose(f);
586 return match;
589 static QString getMatch(const QString &file, const char *extension)
591 QString f(Misc::changeExt(file, extension));
593 return Misc::fExists(f) ? f : QString();
596 struct KfiFont
598 struct Path
600 Path(const QString &p=QString()) : orig(p) { }
602 QString orig,
603 modified;
605 bool operator==(const Path &p) const { return p.orig==orig; }
608 KfiFont(const QString &n=QString(), const QString &p=QString()) : name(n)
609 { if(!p.isEmpty()) paths.append(Path(p)); }
611 QString name;
612 QList<Path> paths;
614 bool operator==(const KfiFont &f) const { return f.name==name; }
617 struct KfiFontList : public QList<KfiFont>
619 Iterator locate(const KfiFont &t) { int i = indexOf(t); return (-1==i ? end() : (begin()+i)); }
622 static void setName(const QString &orig, QString &modified, int pos, bool hidden)
624 modified=orig.mid(pos);
625 modified=modified.replace('/', '_');
626 if(hidden && '.'!=modified[0])
627 modified='.'+modified;
631 // This function returns a set of maping of from -> to for copy/move operations
632 static bool getFontList(const CDisabledFonts::TFileList &files, QMap<QString, QString> &map)
634 // First of all create a list of font files, and their paths
635 CDisabledFonts::TFileList::ConstIterator it=files.begin(),
636 end=files.end();
637 KfiFontList list;
639 for(;it!=end; ++it)
641 QString name(Misc::getFile(*it)),
642 path(Misc::getDir(*it));
643 KfiFontList::Iterator entry=list.locate(KfiFont(name));
645 if(entry!=list.end())
647 if(!(*entry).paths.contains(path))
648 (*entry).paths.append(path);
650 else
651 list.append(KfiFont(name, path));
654 KfiFontList::Iterator fIt(list.begin()),
655 fEnd(list.end());
657 for(; fIt!=fEnd; ++fIt)
659 bool hidden='.'==(*fIt).name[0];
660 QList<KfiFont::Path>::Iterator pBegin((*fIt).paths.begin()),
661 pIt(++pBegin),
662 pEnd((*fIt).paths.end());
663 --pBegin;
665 if((*fIt).paths.count()>1)
667 // There's more than 1 file with the same name, but in a different location
668 // therefore, take the unique part of the path, and replace / with _
669 // e.g.
670 // /usr/X11R6/lib/X11/fonts/75dpi/times.pcf.gz
671 // /usr/X11R6/lib/X11/fonts/100dpi/times.pcf.gz
673 // Will produce:
674 // 75dpi_times.pcf.gz
675 // 100dpi_times.pcf.gz
676 int beginLen((*pBegin).orig.length());
678 for(; pIt!=pEnd; ++pIt)
680 unsigned int len=qMin((*pIt).orig.length(), beginLen);
681 bool modified=false;
683 for(unsigned int i=0; i<len; ++i)
684 if((*pIt).orig[i]!=(*pBegin).orig[i])
686 setName((*pIt).orig, (*pIt).modified, i, hidden);
687 if((*pBegin).modified.isEmpty())
688 setName((*pBegin).orig, (*pBegin).modified, i, hidden);
689 modified=true;
690 break;
692 if(!modified)
694 if(beginLen>(*pIt).orig.length())
696 if((*pBegin).modified.isEmpty())
697 setName((*pBegin).orig, (*pBegin).modified, (*pIt).orig.length(), hidden);
699 else
700 setName((*pIt).orig, (*pIt).modified, beginLen, hidden);
704 for(pIt=(*fIt).paths.begin(); pIt!=pEnd; ++pIt)
705 if(hidden && '.'==(*pIt).modified[0] && '.'==(*fIt).name[0])
706 map[(*pIt).orig+(*fIt).name]=(*pIt).modified+(*fIt).name.mid(1);
707 else
708 map[(*pIt).orig+(*fIt).name]=(*pIt).modified+(*fIt).name;
711 return list.count() ? true : false;
714 inline QString getDestFolder(const QString &folder, const QString &file)
716 return folder+file[0].toLower()+'/';
719 // Extract just the family from a font name
720 static QString getFamily(const QString &font)
722 int commaPos=font.lastIndexOf(',');
723 return -1==commaPos ? font : font.left(commaPos);
726 inline qulonglong toBit(QFontDatabase::WritingSystem ws)
728 return ((qulonglong)1) << (int)ws;
731 //.........the following section is inspired by qfontdatabase_x11.cpp / loadFontConfig
734 // Unfortunately FontConfig doesn't know about some languages. We have to test these through the
735 // charset. The lists below contain the systems where we need to do this.
736 static const struct
738 QFontDatabase::WritingSystem ws;
739 ushort ch;
740 } sampleCharForWritingSystem[] =
742 { QFontDatabase::Telugu, 0xc15 },
743 { QFontDatabase::Kannada, 0xc95 },
744 { QFontDatabase::Malayalam, 0xd15 },
745 { QFontDatabase::Sinhala, 0xd9a },
746 { QFontDatabase::Myanmar, 0x1000 },
747 { QFontDatabase::Ogham, 0x1681 },
748 { QFontDatabase::Runic, 0x16a0 },
749 { QFontDatabase::Any, 0x0 }
752 static qulonglong getWritingSystems(FcPattern *pat)
754 qulonglong ws(0);
756 FcLangSet *langset(0);
757 const CDisabledFonts::LangWritingSystemMap *langForWritingSystem(CDisabledFonts::languageForWritingSystemMap());
759 if (FcResultMatch==FcPatternGetLangSet(pat, FC_LANG, 0, &langset))
761 for (int i = 0; langForWritingSystem[i].lang; ++i)
762 if (FcLangDifferentLang!=FcLangSetHasLang(langset, langForWritingSystem[i].lang))
763 ws|=toBit(langForWritingSystem[i].ws);
765 else
766 ws|=toBit(QFontDatabase::Other);
768 FcCharSet *cs(0);
770 if (FcResultMatch == FcPatternGetCharSet(pat, FC_CHARSET, 0, &cs))
772 // some languages are not supported by FontConfig, we rather check the
773 // charset to detect these
774 for (int i = 0; QFontDatabase::Any!=sampleCharForWritingSystem[i].ws; ++i)
775 if (FcCharSetHasChar(cs, sampleCharForWritingSystem[i].ch))
776 ws|=toBit(sampleCharForWritingSystem[i].ws);
779 return ws;
782 static QString obtainMimeType(const QString &file)
784 if(Misc::checkExt(file, "ttf") || Misc::checkExt(file, "ttc"))
785 return "application/x-font-ttf";
786 if(Misc::checkExt(file, "otf"))
787 return "application/x-font-otf";
788 if(Misc::checkExt(file, "pfa") || Misc::checkExt(file, "pfb"))
789 return "application/x-font-type1";
790 if(Misc::checkExt(file, "pcf.gz") || Misc::checkExt(file, "pcf"))
791 return "application/x-font-pcf";
792 if(Misc::checkExt(file, "bdf.gz") || Misc::checkExt(file, "bdf"))
793 return "application/x-font-bdf";
795 // File extension check failed, use kmimetype to read contents...
796 return KMimeType::findByPath(file)->name();
799 //.........
801 CKioFonts::CKioFonts(const QByteArray &pool, const QByteArray &app)
802 : KIO::SlaveBase(KFI_KIO_FONTS_PROTOCOL, pool, app),
803 itsRoot(Misc::root()),
804 itsAddToSysFc(false),
805 itsLastFcCheckTime(0),
806 itsFontList(NULL),
807 #if defined USE_POLICYKIT && USE_POLICYKIT==1
808 itsFontInstIface(0L),
809 itsPid(0)
810 #else
811 itsSocket(NULL),
812 itsSuProc(NULL)
813 #endif
815 KFI_DBUG;
817 slaveInstance=this;
819 #if !(defined USE_POLICYKIT && USE_POLICYKIT==1)
820 if(!itsRoot)
822 // Set core dump size to 0 because we will have
823 // root's password in memory.
824 struct rlimit rlim;
825 rlim.rlim_cur=rlim.rlim_max=0;
826 setrlimit(RLIMIT_CORE, &rlim);
828 #endif
830 // Check with fontconfig for folder locations...
832 // 1. Get list of fontconfig dirs
833 // 2. For user, look for any starting with $HOME - but prefer $HOME/.fonts
834 // 3. For system, look for any starting with /usr/local/share - but prefer /usr/local/share/fonts
835 // 4. If either are not found, then add to local.conf / .fonts.conf
837 FcStrList *list=FcConfigGetFontDirs(FcInitLoadConfigAndFonts());
838 QStringList dirs;
839 FcChar8 *dir;
841 while((dir=FcStrListNext(list)))
842 dirs.append(Misc::dirSyntax((const char *)dir));
844 EFolder mainFolder=FOLDER_SYS;
846 if(!itsRoot)
848 QString home(Misc::dirSyntax(QDir::homePath())),
849 defaultDir(Misc::dirSyntax(QDir::homePath()+"/.fonts/")),
850 dir(Misc::getFolder(defaultDir, home, dirs));
852 if(dir.isEmpty()) // Then no $HOME/ was found in fontconfigs dirs!
854 KXftConfig xft(KXftConfig::Dirs, false);
855 xft.addDir(defaultDir);
856 xft.apply();
857 dir=defaultDir;
859 mainFolder=FOLDER_USER;
860 itsFolders[FOLDER_USER].setLocation(dir, false);
863 QString sysDefault(KFI_DEFAULT_SYS_FONTS_FOLDER),
864 sysDir(Misc::getFolder(sysDefault, "/usr/local/share/", dirs));
866 if(sysDir.isEmpty())
868 if(itsRoot)
870 KXftConfig xft(KXftConfig::Dirs, true);
871 xft.addDir(sysDefault);
872 xft.apply();
874 else
875 itsAddToSysFc=true;
877 sysDir=sysDefault;
880 itsFolders[FOLDER_SYS].setLocation(sysDir, true);
883 // Ensure exists
884 if(!Misc::dExists(itsFolders[mainFolder].location))
885 Misc::createDir(itsFolders[mainFolder].location);
887 updateFontList();
890 void CKioFonts::cleanup()
892 slaveInstance=NULL;
894 KFI_DBUG;
895 itsFolders[FOLDER_USER].disabled->save();
896 doModified();
897 #if defined USE_POLICYKIT && USE_POLICYKIT==1
898 delete itsFontInstIface;
899 #else
900 quitHelper();
902 delete itsSuProc;
903 delete itsSocket;
904 #endif
907 void CKioFonts::listDir(const KUrl &url)
909 KFI_DBUG << url.path() << " query:" << url.query();
911 clearFontList(); // Always refresh font list when listing...
912 if(updateFontList() && checkUrl(url, true))
914 KIO::UDSEntry entry;
915 int size=0,
916 sections=url.path().split('/', QString::SkipEmptyParts).count();
917 if(itsRoot || sections!=0)
919 if(!itsRoot && isAllFolder(getSect(url.path())))
921 totalSize(itsFolders[FOLDER_SYS].fontMap.count()+itsFolders[FOLDER_SYS].disabled->items().count()+
922 itsFolders[FOLDER_USER].fontMap.count()+itsFolders[FOLDER_USER].disabled->items().count());
923 listDir(FOLDER_SYS, entry);
924 listDir(FOLDER_USER, entry);
926 else
928 EFolder folder=getFolder(url);
930 totalSize(itsFolders[folder].fontMap.count()+itsFolders[folder].disabled->items().count());
931 listDir(folder, entry);
934 else
936 size=2;
937 totalSize(size);
938 createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_USER), itsFolders[FOLDER_USER].location,
939 false);
940 listEntry(entry, false);
941 createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_SYS), itsFolders[FOLDER_SYS].location,
942 true);
943 listEntry(entry, false);
946 listEntry(size ? entry : KIO::UDSEntry(), true);
947 finished();
950 KFI_DBUG << "finished!";
953 void CKioFonts::listDir(EFolder folder, KIO::UDSEntry &entry)
955 if(itsFolders[folder].fontMap.count())
957 TFontMap::Iterator it=itsFolders[folder].fontMap.begin(),
958 end=itsFolders[folder].fontMap.end();
960 for ( ; it != end; ++it)
962 entry.clear();
963 if(createFontUDSEntry(entry, it.key(), it.value().files, it.value().styleVal,
964 it.value().writingSystems, FOLDER_SYS==folder))
965 listEntry(entry, false);
969 CDisabledFonts::TFontList::Iterator dIt(itsFolders[folder].disabled->items().begin()),
970 dEnd(itsFolders[folder].disabled->items().end());
972 for(; dIt!=dEnd; ++dIt)
973 if(createFontUDSEntry(entry, (*dIt).getName(), (*dIt).files,
974 (*dIt).styleInfo, (*dIt).writingSystems, FOLDER_SYS==folder, true))
975 listEntry(entry, false);
978 void CKioFonts::stat(const KUrl &url)
980 KFI_DBUG << url.prettyUrl() << " query:" << url.query();
982 KIO::UDSEntry entry;
983 bool err=false;
985 if(checkUrl(url, true, false))
987 QString path(url.path(KUrl::RemoveTrailingSlash));
989 if(path.isEmpty())
991 error(KIO::ERR_COULD_NOT_STAT, urlString(url));
992 return;
995 QStringList pathList(path.split('/', QString::SkipEmptyParts));
997 switch(pathList.count())
999 case 0:
1000 err=!createFolderUDSEntry(entry, i18n("Fonts"),
1001 itsFolders[itsRoot ? FOLDER_SYS : FOLDER_USER].location,
1002 false);
1003 break;
1004 case 1:
1005 if(itsRoot)
1006 err=!createStatEntry(entry, url, FOLDER_SYS);
1007 else
1008 if(isUserFolder(pathList[0]))
1009 err=!createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_USER),
1010 itsFolders[FOLDER_USER].location, false);
1011 else if(isSysFolder(pathList[0]))
1012 err=!createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_SYS),
1013 itsFolders[FOLDER_SYS].location, true);
1014 else if(isAllFolder(pathList[0]))
1015 err=!createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_ALL),
1016 itsFolders[FOLDER_SYS].location, true);
1017 else
1019 error(KIO::ERR_SLAVE_DEFINED,
1020 i18n("Please specify \"%1\" or \"%2\".",
1021 i18n(KFI_KIO_FONTS_USER), i18n(KFI_KIO_FONTS_SYS)));
1022 return;
1024 break;
1025 default:
1026 err=!createStatEntry(entry, url, getFolder(url));
1029 else if(!itsRoot && 1==url.path(KUrl::RemoveTrailingSlash)
1030 .split('/', QString::SkipEmptyParts).count())
1033 // If a user (non-root) copies a font to fonts:/, kio_fonts will redirect to fonts:/Personal
1034 // If the font already exists in fonts:/Personal, konqueror will then do a stat on
1035 // fonts:/<filename> to get its file size in the "overwrite" dialog.
1038 // But, the font will not exist in fonts:/<filename> - so we need to see if it exists
1039 // in fonts:/Personal/<filename> in order to get the correct size. Otherwise konqueror, etc,
1040 // display 0 bytes!
1042 KUrl modUrl(url);
1044 modUrl.setPath(QChar('/')+i18n(KFI_KIO_FONTS_USER)+QChar('/')+url.fileName());
1045 err=!createStatEntry(entry, modUrl, FOLDER_USER);
1047 else
1049 error(KIO::ERR_SLAVE_DEFINED, i18n("Please specify \"%1\" or \"%2\".",
1050 i18n(KFI_KIO_FONTS_USER), i18n(KFI_KIO_FONTS_SYS)));
1051 return;
1054 if(err)
1056 error(KIO::ERR_DOES_NOT_EXIST, urlString(url));
1057 return;
1060 statEntry(entry);
1061 finished();
1064 bool CKioFonts::createStatEntry(KIO::UDSEntry &entry, const KUrl &url, EFolder folder)
1066 KFI_DBUG << url.path();
1068 // First try to create stat entry without refreshing lists...
1069 bool ok=createStatEntryReal(entry, url, folder) && getSize(entry)>0;
1071 // Hmm... well that failed, so refresh lists and try again!
1072 if(!ok)
1074 KFI_DBUG << "refresh font list";
1075 entry.clear();
1076 clearFontList();
1077 updateFontList();
1078 ok=createStatEntryReal(entry, url, folder) && getSize(entry)>0;
1081 // Perhaps its not a valid font? Try to stat on location+name
1082 if(!ok)
1084 KFI_DBUG << "could not find";
1086 QStringList folders;
1088 folders.append(itsFolders[folder].location);
1089 folders.append(getDestFolder(itsFolders[folder].location, url.fileName()));
1091 QStringList::const_iterator it(folders.begin()),
1092 end(folders.end());
1094 for(; it!=end; ++it)
1095 for(int t=0; t<3 && !ok; ++t)
1097 CDisabledFonts::TFileList files;
1098 QString fileName=0==t
1099 ? url.fileName()
1100 : 1==t ? modifyName(url.fileName()) // lowercase
1101 : modifyName(url.fileName(), true); // uppercase
1103 files.append(CDisabledFonts::TFile((*it)+fileName));
1104 entry.clear();
1105 ok=createFontUDSEntry(entry, i18n("Invalid Font"), files,
1106 KFI_NO_STYLE_INFO, 0, FOLDER_SYS==folder,
1107 Misc::isHidden(url)) && getSize(entry)>0;
1111 if(!ok)
1112 entry.clear();
1114 return ok;
1117 bool CKioFonts::createStatEntryReal(KIO::UDSEntry &entry, const KUrl &url, EFolder folder)
1119 KFI_DBUG << url.path();
1121 TFontMap::Iterator it=getMap(url);
1123 if(it!=itsFolders[folder].fontMap.end())
1125 KFI_DBUG << "its a normal font";
1126 return createFontUDSEntry(entry, it.key(), it.value().files, it.value().styleVal,
1127 it.value().writingSystems, FOLDER_SYS==folder);
1130 KFI_DBUG << "try looking in disabled fonts";
1132 QString name=Misc::getFile(removeMultipleExtension(url));
1133 CDisabledFonts::TFontList::Iterator dIt=itsFolders[folder].disabled->find(name,
1134 Misc::getIntQueryVal(url, KFI_KIO_FACE, 0));
1136 if(dIt!=itsFolders[folder].disabled->items().end())
1138 KFI_DBUG << "its a disabled font";
1139 return createFontUDSEntry(entry, (*dIt).getName(), (*dIt).files, (*dIt).styleInfo,
1140 (*dIt).writingSystems, FOLDER_SYS==folder, true);
1143 return false;
1146 void CKioFonts::get(const KUrl &url)
1148 KFI_DBUG << url.path() << " query:" << url.query();
1150 bool thumb="1"==metaData("thumbnail");
1151 CDisabledFonts::TFileList srcFiles;
1153 // Any error will be logged in getSourceFiles
1154 if(updateFontList() && checkUrl(url) && getSourceFiles(url, srcFiles))
1157 // The thumbnail job always donwloads non-local files to /tmp/... and passes this file name to
1158 // the thumbnail creator. However, in the case of fonts which are split among many files, this
1159 // wont work. Therefore, when the thumbnail code asks for the font to donwload, just return
1160 // the family and style info for enabled fonts, and the filename for disabled fonts. This way
1161 // the font-thumbnail creator can read this and just ask Xft/fontconfig for the font data.
1162 if(thumb)
1164 QByteArray array;
1165 QTextStream stream(&array, QIODevice::WriteOnly);
1166 EFolder folder(getFolder(url));
1167 TFontMap::Iterator it(getMap(url)),
1168 end(itsFolders[folder].fontMap.end());
1170 emit mimeType("text/plain");
1172 if(it==end)
1175 // OK, its a disabled font - if possible try to return the location of the font file
1176 // itself.
1177 QString name(Misc::getFile(removeMultipleExtension(url)));
1178 CDisabledFonts::TFontList::Iterator dIt(itsFolders[folder].disabled
1179 ->find(name, Misc::getIntQueryVal(url,
1180 KFI_KIO_FACE, 0))),
1181 dEnd(itsFolders[folder].disabled->items().end());
1182 bool found=false;
1184 if(dIt!=dEnd)
1186 CDisabledFonts::TFileList::ConstIterator fIt((*dIt).files.begin()),
1187 fEnd((*dIt).files.end());
1189 for(; fIt!=fEnd; ++fIt)
1190 if(isScalable((*fIt).path))
1192 KFI_DBUG << "hasMetaData(\"thumbnail\"), so return FILE: "
1193 << (*fIt).path << " / " << (*fIt).face;
1194 stream << KFI_PATH_KEY << (*fIt).path << endl
1195 << KFI_FACE_KEY << (*fIt).face << endl;
1196 found=true;
1197 break;
1201 if(!found)
1203 KFI_DBUG << "hasMetaData(\"thumbnail\"), so return Url: " << url;
1204 stream << url.prettyUrl();
1207 else
1209 KFI_DBUG << "hasMetaData(\"thumbnail\"), so return DETAILS: " << it.key() << " / "
1210 << (*it).styleVal;
1212 stream << KFI_NAME_KEY << it.key() << endl
1213 << KFI_STYLE_KEY << (*it).styleVal << endl;
1215 if(1==(*it).files.count())
1217 CDisabledFonts::TFileList::ConstIterator fIt((*it).files.begin());
1219 stream << KFI_PATH_KEY << (*fIt).path << endl
1220 << KFI_FACE_KEY << (*fIt).face << endl;
1224 totalSize(array.size());
1225 data(array);
1226 processedSize(array.size());
1227 data(QByteArray());
1228 processedSize(array.size());
1229 finished();
1230 KFI_DBUG << "Finished thumbnail...";
1231 return;
1234 QString realPath;
1235 KDE_struct_stat buff;
1236 bool multiple=false;
1238 if(1==srcFiles.count())
1239 realPath=srcFiles.first().path;
1240 else // Font is made up of multiple files - so create .zip of them all!
1242 KTemporaryFile tmpFile;
1244 if(tmpFile.open())
1246 KZip zip(tmpFile.fileName());
1248 tmpFile.setAutoRemove(false);
1249 realPath=tmpFile.fileName();
1251 if(zip.open(QIODevice::WriteOnly))
1253 QMap<QString, QString> map;
1255 getFontList(srcFiles, map);
1257 QMap<QString, QString>::Iterator fIt(map.begin()),
1258 fEnd(map.end());
1261 // Iterate through created list, and add to zip archive
1262 // ...unhide any hidden files
1263 for(; fIt!=fEnd; ++fIt)
1264 zip.addLocalFile(fIt.key(), Misc::unhide(fIt.value()));
1266 multiple=true;
1267 zip.close();
1272 QByteArray realPathC(QFile::encodeName(realPath));
1273 KFI_DBUG << "real: " << realPathC;
1275 if (-2==KDE_stat(realPathC.constData(), &buff))
1276 error(EACCES==errno ? KIO::ERR_ACCESS_DENIED : KIO::ERR_DOES_NOT_EXIST, urlString(url));
1277 else if (S_ISDIR(buff.st_mode))
1278 error(KIO::ERR_IS_DIRECTORY, urlString(url));
1279 else if (!S_ISREG(buff.st_mode))
1280 error(KIO::ERR_CANNOT_OPEN_FOR_READING, urlString(url));
1281 else
1283 int fd = KDE_open(realPathC.constData(), O_RDONLY);
1285 if (fd < 0)
1286 error(KIO::ERR_CANNOT_OPEN_FOR_READING, urlString(url));
1287 else
1289 // Determine the mimetype of the file to be retrieved, and emit it.
1290 // This is mandatory in all slaves (for KRun/BrowserRun to work).
1291 emit mimeType(KMimeType::findByPath(realPathC, buff.st_mode)->name());
1293 totalSize(buff.st_size);
1295 KIO::filesize_t processed=0;
1296 char buffer[MAX_IPC_SIZE];
1297 QByteArray array;
1299 while(1)
1301 int n=::read(fd, buffer, MAX_IPC_SIZE);
1303 if (-1==n)
1305 if (EINTR==errno)
1306 continue;
1308 error(KIO::ERR_COULD_NOT_READ, urlString(url));
1309 ::close(fd);
1310 if(multiple)
1311 ::unlink(realPathC);
1312 return;
1314 if (0==n)
1315 break; // Finished
1317 array=array.fromRawData(buffer, n);
1318 data(array);
1319 array.clear();
1321 processed+=n;
1322 processedSize(processed);
1325 data(QByteArray());
1326 ::close(fd);
1327 processedSize(buff.st_size);
1328 finished();
1331 if(multiple)
1332 ::unlink(realPathC);
1336 void CKioFonts::put(const KUrl &u, int mode, KIO::JobFlags flags)
1338 KFI_DBUG << u.path() << " query:" << u.query();
1340 if(Misc::isHidden(u))
1342 error(KIO::ERR_SLAVE_DEFINED, i18n("Cannot install %1\nHidden fonts cannot be "
1343 "installed.", urlString(u)));
1344 return;
1347 // updateFontList(); // CPD: don't update font list upon a put - it's too slow. Just stat on
1348 // // filename!
1349 //checkUrl(u) // CPD: Don't need to check Url, as the call to "confirmUrl()" below will sort out
1350 // // any probs!
1352 KUrl url(u);
1354 correctUrl(url);
1356 bool nrs(nonRootSys(url)),
1357 clearList(!hasMetaData(KFI_KIO_NO_CLEAR));
1358 EFolder destFolder(getFolder(url));
1359 QString destFolderReal(getDestFolder(itsFolders[destFolder].location, url.fileName())),
1360 dest(destFolderReal+modifyName(url.fileName()));
1361 QByteArray destC(QFile::encodeName(dest));
1362 KDE_struct_stat buffDest;
1363 bool destExists(KDE_lstat(destC.constData(), &buffDest)!= -1);
1365 if (destExists && !(flags & KIO::Overwrite) && !(flags & KIO::Resume))
1367 error(KIO::ERR_FILE_ALREADY_EXIST, urlString(url));
1368 return;
1371 if(nrs && !getRootPasswd(u)) // Need to check can get root passwd before start download...
1373 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
1374 return;
1378 // As we don't get passed a mime-type the following needs to happen:
1380 // 1. Download to a temporary file
1381 // 2. Check with FreeType that the file is a font, or that it is
1382 // an AFM or PFM file
1383 KTemporaryFile tmpFile;
1385 tmpFile.setAutoRemove(true);
1387 if(putReal(tmpFile))
1389 EFileType type(checkFile(tmpFile.fileName(), u)); // error logged in checkFile
1391 if(FILE_UNKNOWN==type)
1392 return;
1394 int timeout(reconfigTimeout());
1396 if(nrs) // Ask root to move the tmp font...
1398 QList<TCommand> cmd;
1399 TCommand c(KFI::CMD_MOVE_FILE);
1401 addCreateFolderCmd(itsFolders[FOLDER_SYS].location, cmd);
1402 if(destFolderReal!=itsFolders[FOLDER_SYS].location)
1403 addCreateFolderCmd(destFolderReal, cmd);
1405 c.args.append(tmpFile.fileName());
1406 c.args.append(dest);
1407 c.args.append(0);
1408 c.args.append(0);
1410 cmd.append(c);
1412 // Get root to move this to fonts folder...
1413 if(doRootCmd(u, cmd, false)) // Already asked for passwd...
1415 tmpFile.setAutoRemove(false);
1416 if(FILE_FONT==type)
1417 modified(timeout, FOLDER_SYS, clearList, destFolderReal);
1418 createAfm(dest, true);
1420 else
1422 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
1423 return;
1426 else // Move it to our font folder...
1428 tmpFile.setAutoRemove(false);
1429 if(!Misc::dExists(destFolderReal))
1430 Misc::createDir(destFolderReal);
1431 if(0==::rename(QFile::encodeName(tmpFile.fileName()).constData(), destC.constData()))
1433 Misc::setFilePerms(destC);
1434 if(FILE_FONT==type)
1435 modified(timeout, FOLDER_USER, clearList, destFolderReal);
1436 createAfm(dest);
1438 else
1440 error(KIO::ERR_SLAVE_DEFINED, i18n("Could not access \"%1\" folder.",
1441 i18n(KFI_KIO_FONTS_USER)));
1442 return;
1446 finished();
1450 QString CKioFonts::getUserName(uid_t uid)
1452 if (!itsUserCache.contains(uid))
1454 struct passwd *user = getpwuid(uid);
1455 if(user)
1456 itsUserCache.insert(uid, QString::fromLatin1(user->pw_name));
1457 else
1458 return QString::number(uid);
1460 return itsUserCache[uid];
1463 QString CKioFonts::getGroupName(gid_t gid)
1465 if (!itsGroupCache.contains(gid))
1467 struct group *grp = getgrgid(gid);
1468 if(grp)
1469 itsGroupCache.insert(gid, QString::fromLatin1(grp->gr_name));
1470 else
1471 return QString::number(gid);
1473 return itsGroupCache[gid];
1476 bool CKioFonts::createFontUDSEntry(KIO::UDSEntry &entry, const QString &name,
1477 const CDisabledFonts::TFileList &patterns,
1478 quint32 styleVal, qulonglong writingSystems,
1479 bool sys, bool hidden)
1481 //KFI_DBUG << "name:" << name << " style:" << styleVal << " #"
1482 // << patterns.count();
1485 // First of all get list of real files - i.e. remove any duplicates due to symlinks
1486 CDisabledFonts::TFileList files;
1488 getFontFiles(patterns, files);
1490 CDisabledFonts::TFileList::ConstIterator it(files.begin()),
1491 end(files.end());
1493 if(files.count()>1)
1496 // Sort list of files - placing scalable ones first. This is because, when determening the
1497 // mimetype, the 1st valid file will be chosen. In case of mixed bitmap/scalable - prefer
1498 // scalable
1499 CDisabledFonts::TFileList sorted;
1501 for(; it!=end; ++it)
1502 if(isScalable(*it))
1503 sorted.prepend(*it);
1504 else
1505 sorted.append(*it);
1506 files=sorted;
1507 it=files.constBegin();
1508 end=files.constEnd();
1511 entry.clear();
1512 entry.insert(KIO::UDSEntry::UDS_SIZE, getSize(files));
1514 for(; it!=end; ++it)
1516 QByteArray cPath(QFile::encodeName(*it));
1517 KDE_struct_stat buff;
1519 if(-1!=KDE_lstat(cPath, &buff))
1521 if(0==writingSystems)
1522 writingSystems=toBit(QFontDatabase::Other);
1524 entry.insert(KIO::UDSEntry::UDS_NAME, name);
1525 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, buff.st_mode&S_IFMT);
1526 entry.insert(KIO::UDSEntry::UDS_ACCESS, buff.st_mode&07777);
1527 entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.st_mtime);
1528 entry.insert(KIO::UDSEntry::UDS_USER, getUserName(buff.st_uid));
1529 entry.insert(KIO::UDSEntry::UDS_GROUP, getGroupName(buff.st_gid));
1530 entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, buff.st_atime);
1531 entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, obtainMimeType(*it));
1532 entry.insert(UDS_EXTRA_FC_STYLE, styleVal);
1533 entry.insert(UDS_EXTRA_WRITING_SYSTEMS, writingSystems);
1535 if(hidden)
1536 entry.insert(KIO::UDSEntry::UDS_HIDDEN, 1);
1538 entry.insert(UDS_EXTRA_FILE_NAME, (*it).path);
1539 entry.insert(UDS_EXTRA_FOUNDRY, (*it).foundry);
1540 if(files.count()>1)
1541 entry.insert(UDS_EXTRA_FILE_LIST, files.toString(true));
1543 QString url(KFI_KIO_FONTS_PROTOCOL+QString::fromLatin1(":/"));
1545 if(!Misc::root())
1547 url+=sys ? i18n(KFI_KIO_FONTS_SYS) : i18n(KFI_KIO_FONTS_USER);
1548 url+=QString::fromLatin1("/");
1551 #ifdef KFI_KIO_ALL_URLS_HAVE_NAME
1552 addKnownExtension(url, files, name, hidden);
1553 #else
1554 if(files.count()>1)
1556 if(hidden)
1557 url+='.';
1558 url+=name+QString::fromLatin1(KFI_FONTS_PACKAGE);
1560 else
1561 url+=Misc::getFile(*it);
1562 #endif
1564 if(files.count()==1 && (*it).face>0)
1566 KUrl kUrl(url);
1568 kUrl.setQuery("?"KFI_KIO_FACE"="+QString::number((*it).face));
1569 entry.insert(KIO::UDSEntry::UDS_URL, kUrl.url());
1571 else
1572 entry.insert(KIO::UDSEntry::UDS_URL, url);
1574 return true; // This file was OK, so use its values...
1577 return false;
1580 bool CKioFonts::createFolderUDSEntry(KIO::UDSEntry &entry, const QString &name,
1581 const QString &path, bool sys)
1583 KFI_DBUG << "name:" << name << " path:" << path << " sys?:" << sys;
1585 KDE_struct_stat buff;
1586 QByteArray cPath(QFile::encodeName(path));
1588 entry.clear();
1590 if(-1!=KDE_lstat(cPath, &buff))
1592 entry.insert(KIO::UDSEntry::UDS_NAME, name);
1594 if (S_ISLNK(buff.st_mode))
1596 KFI_DBUG << path << " is a link";
1598 char buffer2[1000];
1599 int n=readlink(cPath, buffer2, 1000);
1600 if(n!= -1)
1601 buffer2[n]='\0';
1603 if(-1==KDE_stat(cPath, &buff))
1605 // It is a link pointing to nowhere
1606 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFMT - 1);
1607 entry.insert(KIO::UDSEntry::UDS_ACCESS, S_IRWXU | S_IRWXG | S_IRWXO);
1608 entry.insert(KIO::UDSEntry::UDS_SIZE, 0);
1609 goto notype;
1611 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); // CPD Treat links as regular folder...
1613 else
1614 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, buff.st_mode&S_IFMT);
1615 entry.insert(KIO::UDSEntry::UDS_ACCESS, buff.st_mode&07777);
1616 entry.insert(KIO::UDSEntry::UDS_SIZE, buff.st_size);
1618 notype:
1619 entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.st_mtime);
1620 entry.insert(KIO::UDSEntry::UDS_USER, getUserName(buff.st_uid));
1621 entry.insert(KIO::UDSEntry::UDS_GROUP, getGroupName(buff.st_gid));
1622 entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, buff.st_atime);
1623 entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/directory"));
1624 return true;
1626 else if (sys && !Misc::root()) // Default system fonts folder does not actually exist yet!
1628 KFI_DBUG << "Default system folder (" << path
1629 << ") does not yet exist, so create dummy entry";
1630 entry.insert(KIO::UDSEntry::UDS_NAME, name);
1631 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
1632 entry.insert(KIO::UDSEntry::UDS_ACCESS, 0744);
1633 entry.insert(KIO::UDSEntry::UDS_USER, QString::fromLatin1("root"));
1634 entry.insert(KIO::UDSEntry::UDS_GROUP, QString::fromLatin1("root"));
1635 entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/directory"));
1636 return true;
1639 return false;
1642 bool CKioFonts::putReal(KTemporaryFile &dest)
1644 if (!dest.open())
1646 error(EACCES==errno ? KIO::ERR_WRITE_ACCESS_DENIED : KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest.fileName());
1647 return false;
1650 int result;
1651 // Loop until we got 0 (end of data)
1654 QByteArray buffer;
1656 dataReq(); // Request for data
1657 result = readData(buffer);
1658 if(result > 0 && !writeAll(dest.handle(), buffer.constData(), buffer.size()))
1660 if(ENOSPC==errno) // disk full
1662 error(KIO::ERR_DISK_FULL, dest.fileName());
1663 result = -2; // means: remove dest file
1665 else
1667 error(KIO::ERR_COULD_NOT_WRITE, dest.fileName());
1668 result = -1;
1672 while(result>0);
1674 if (result<0)
1676 dest.close();
1677 ::exit(255);
1680 return true;
1683 void CKioFonts::copy(const KUrl &src, const KUrl &d, int mode, KIO::JobFlags flags)
1686 // Support:
1687 // Copying to fonts:/
1688 // Copying from fonts:/ and file:/
1690 KFI_DBUG << src.prettyUrl() << " query:" << src.query() << " - "
1691 << d.prettyUrl() << " query:" << d.query();
1693 if(Misc::isHidden(d))
1695 error(KIO::ERR_SLAVE_DEFINED,
1696 i18n("Cannot copy %1 to %2\nHidden/disabled fonts cannot be installed.",
1697 urlString(src), urlString(d)));
1698 return;
1701 bool fromFonts=KFI_KIO_FONTS_PROTOCOL==src.protocol();
1703 // CPD: don't update font list upon a copy from file - it's too slow. Just stat on filename!
1704 if(!fromFonts || updateFontList() && checkUrl(src) && checkAllowed(src))
1706 //checkUrl(u) // CPD as per comment in ::put()
1708 CDisabledFonts::TFileList srcFiles;
1709 int timeout(reconfigTimeout());
1711 if(getSourceFiles(src, srcFiles)) // Any error will be logged in getSourceFiles
1713 KUrl dest(d);
1715 correctUrl(dest);
1717 bool metrics(fromFonts ? false : Misc::isMetrics(src.fileName())),
1718 clearList(!hasMetaData(KFI_KIO_NO_CLEAR));
1719 EFolder destFolder(getFolder(dest));
1720 QMap<QString, QString> map;
1722 if(!fromFonts)
1723 map[src.path()]=src.fileName();
1725 // As above, if copying from file, then only stat on dest filename, but if from fonts to
1726 // fonts need to get the list of possible source files, etc.
1727 if(fromFonts ? confirmMultiple(src, srcFiles,
1728 FOLDER_SYS==destFolder ? FOLDER_USER : FOLDER_SYS, OP_COPY) &&
1729 getFontList(srcFiles, map) &&
1730 checkDestFiles(src, map, dest, destFolder, flags)
1731 : checkDestFile(src, dest, destFolder, flags) )
1733 if(nonRootSys(dest))
1735 QList<TCommand> cmd;
1736 int size=0;
1737 CDirList addedFolders;
1738 QMap<QString, QString>::Iterator fIt(map.begin()),
1739 fEnd(map.end());
1741 for(; fIt!=fEnd; ++fIt)
1743 TCommand c(KFI::CMD_COPY_FILE);
1744 QString destFolderReal(getDestFolder(itsFolders[destFolder].location,
1745 Misc::getFile(*fIt)));
1747 if(!addedFolders.contains(itsFolders[FOLDER_SYS].location) &&
1748 addCreateFolderCmd(itsFolders[FOLDER_SYS].location, cmd))
1749 addedFolders.add(itsFolders[FOLDER_SYS].location);
1751 if(!addedFolders.contains(destFolderReal) &&
1752 addCreateFolderCmd(destFolderReal, cmd))
1753 addedFolders.add(destFolderReal);
1755 c.args.append(fIt.key());
1756 c.args.append(destFolderReal+modifyName(fIt.value()));
1757 cmd.append(c);
1759 int s=getSize(QFile::encodeName(fIt.key()));
1760 if(s>0)
1761 size+=s;
1764 totalSize(size);
1766 if(doRootCmd(d, cmd, true))
1768 if(!metrics)
1769 modified(timeout, destFolder, clearList, addedFolders);
1770 processedSize(size);
1771 if(src.isLocalFile() && 1==srcFiles.count())
1772 createAfm(itsFolders[destFolder].location+modifyName(map.begin().value()), true);
1774 else
1776 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
1777 return;
1780 else
1782 QMap<QString, QString>::Iterator fIt(map.begin()),
1783 fEnd(map.end());
1784 QString destFolderReal;
1786 for(; fIt!=fEnd; ++fIt)
1788 destFolderReal=getDestFolder(itsFolders[destFolder].location,
1789 Misc::getFile(*fIt));
1791 QByteArray realSrc(QFile::encodeName(fIt.key())),
1792 realDest(QFile::encodeName(destFolderReal+
1793 modifyName(fIt.value())));
1794 KDE_struct_stat buffSrc;
1796 if(-1==KDE_stat(realSrc.constData(), &buffSrc))
1798 error(EACCES==errno ? KIO::ERR_ACCESS_DENIED : KIO::ERR_DOES_NOT_EXIST,
1799 urlString(src));
1800 return;
1803 int srcFd=KDE_open(realSrc.constData(), O_RDONLY);
1805 if (srcFd<0)
1807 error(KIO::ERR_CANNOT_OPEN_FOR_READING, urlString(src));
1808 return;
1811 if(!Misc::dExists(destFolderReal))
1812 Misc::createDir(destFolderReal);
1814 // WABA: Make sure that we keep writing permissions ourselves,
1815 // otherwise we can be in for a surprise on NFS.
1816 int destFd=KDE_open(realDest.constData(), O_CREAT | O_TRUNC | O_WRONLY,
1817 -1==mode ? 0666 : mode | S_IWUSR);
1819 if (destFd<0)
1821 error(EACCES==errno
1822 ? KIO::ERR_WRITE_ACCESS_DENIED
1823 : KIO::ERR_CANNOT_OPEN_FOR_WRITING, urlString(dest));
1824 ::close(srcFd);
1825 return;
1828 totalSize(buffSrc.st_size);
1830 KIO::filesize_t processed = 0;
1831 char buffer[MAX_IPC_SIZE];
1832 QByteArray array;
1834 while(1)
1836 int n=::read(srcFd, buffer, MAX_IPC_SIZE);
1838 if(-1==n)
1840 error(KIO::ERR_COULD_NOT_READ, urlString(src));
1841 ::close(srcFd);
1842 ::close(destFd);
1843 return;
1845 if(0==n)
1846 break; // Finished
1848 if(!writeAll(destFd, buffer, n))
1850 ::close(srcFd);
1851 ::close(destFd);
1852 if (ENOSPC==errno) // disk full
1854 error(KIO::ERR_DISK_FULL, urlString(dest));
1855 remove(realDest.constData());
1857 else
1858 error(KIO::ERR_COULD_NOT_WRITE, urlString(dest));
1859 return;
1862 processed += n;
1863 processedSize(processed);
1866 ::close(srcFd);
1868 if(::close(destFd))
1870 error(KIO::ERR_COULD_NOT_WRITE, urlString(dest));
1871 return;
1874 Misc::setFilePerms(realDest);
1876 // copy access and modification time
1877 struct utimbuf ut;
1879 ut.actime = buffSrc.st_atime;
1880 ut.modtime = buffSrc.st_mtime;
1881 ::utime(realDest.constData(), &ut);
1883 processedSize(buffSrc.st_size);
1884 if(!metrics)
1885 modified(timeout, destFolder, clearList, destFolderReal);
1888 if(src.isLocalFile() && 1==srcFiles.count())
1889 createAfm(destFolderReal+modifyName(map.begin().value()));
1892 finished();
1898 void CKioFonts::rename(const KUrl &src, const KUrl &d, KIO::JobFlags flags)
1900 KFI_DBUG << src.prettyUrl() << " query:" << src.query() << " - "
1901 << d.prettyUrl() << " query:" << d.query() << ", " << (flags & KIO::Overwrite);
1903 int timeout(reconfigTimeout());
1905 if(src.directory()==d.directory())
1907 if(!itsRoot && "/"==src.directory())
1909 error(KIO::ERR_SLAVE_DEFINED, i18n("You cannot rename font folders"));
1910 return;
1913 CDisabledFonts::TFontList::Iterator disabledIt;
1914 TFontMap::Iterator enabledIt;
1915 const CDisabledFonts::TFileList *entries(getEntries(src, enabledIt, disabledIt));
1917 if(!entries)
1919 KFI_DBUG << "No entries found, updating font list antry again...";
1920 updateFontList();
1921 entries=getEntries(src, enabledIt, disabledIt);
1924 if(entries)
1926 QString destFile(Misc::getFile(d.path())),
1927 srcFile(Misc::getFile(src.path())),
1928 destEn(destFile.mid(1)),
1929 srcEn(srcFile.mid(1));
1930 EFolder folder(getFolder(d));
1931 QString srcName(Misc::getFile(removeMultipleExtension(src)));
1932 bool clearList(!hasMetaData(KFI_KIO_NO_CLEAR)),
1933 nrs(nonRootSys(src)),
1934 enable(Misc::isHidden(srcFile) && !Misc::isHidden(destFile) && srcEn==destFile),
1935 disable(!Misc::isHidden(srcFile) && Misc::isHidden(destFile) &&
1936 destEn==srcFile);
1938 if(enable && disabledIt!=itsFolders[folder].disabled->items().end())
1940 if(confirmMultiple(src, (*disabledIt).files, folder, OP_ENABLE))
1942 CDirList folders;
1943 CDisabledFonts::TFileList::ConstIterator it((*disabledIt).files.begin()),
1944 end((*disabledIt).files.end());
1945 bool ok(false);
1947 for(; it!=end; ++it)
1948 folders.add(Misc::getDir(*it));
1950 if(nrs)
1952 TCommand c(KFI::CMD_ENABLE_FONT);
1954 c.args.append((*disabledIt).family);
1955 c.args.append((int)(*disabledIt).styleInfo);
1956 ok=doRootCmd(d, c);
1958 else
1959 ok=itsFolders[folder].disabled->enable(disabledIt);
1961 if(ok)
1963 modified(timeout, folder, clearList, folders);
1964 finished();
1966 else
1967 if(nrs)
1968 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
1969 else
1970 error(KIO::ERR_DOES_NOT_EXIST, urlString(src));
1972 return;
1974 else if (disable && disabledIt==itsFolders[folder].disabled->items().end())
1976 if(confirmMultiple(src, (*enabledIt).files, folder, OP_DISABLE))
1978 CDirList folders;
1979 QMap<int, QString> names;
1980 CDisabledFonts::TFileList::ConstIterator it((*enabledIt).files.begin()),
1981 end((*enabledIt).files.end());
1982 bool ok=false;
1984 for(; it!=end; ++it)
1985 folders.add(Misc::getDir(*it));
1988 // If there is only 1 file mapped to this fontname, see if this file maps
1989 // to multiple font names - as would be the case in a TTC file...
1990 if(1==(*enabledIt).files.count())
1991 names=getFontIndexToNameEntries(folder, (*((*enabledIt).files.begin())).path);
1993 if(0==names.count())
1994 names[0]=enabledIt.key(); // Multiple files -> cant use index :-(
1996 QMap<int, QString>::ConstIterator nameIt(names.begin()),
1997 nameEnd(names.end());
1999 for(; nameIt!=nameEnd; ++nameIt)
2000 if(nrs)
2002 TCommand c(KFI::CMD_DISABLE_FONT);
2004 c.args.append(getFamily(enabledIt.key()));
2005 c.args.append((int)(*enabledIt).styleVal);
2006 c.args.append((*enabledIt).writingSystems);
2007 c.args.append((int)(nameIt.key()));
2008 c.args.append((*enabledIt).files.count());
2009 for(it=(*enabledIt).files.constBegin(); it!=end; ++it)
2011 c.args.append((*it).path);
2012 c.args.append((*it).foundry);
2014 ok=doRootCmd(d, c);
2016 else
2018 QString fontStr(*nameIt);
2019 int commaPos=fontStr.indexOf(',');
2020 CDisabledFonts::TFont font(-1==commaPos
2021 ? fontStr
2022 : fontStr.left(commaPos),
2023 (*enabledIt).styleVal,
2024 (*enabledIt).writingSystems);
2026 font.files=(*enabledIt).files;
2027 if(1==font.files.count())
2028 (*(font.files.begin())).face=nameIt.key();
2029 ok=itsFolders[folder].disabled->disable(font);
2032 if(ok)
2034 modified(timeout, folder, clearList, folders);
2035 finished();
2037 else
2038 if(nrs)
2039 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
2040 else
2041 error(KIO::ERR_DOES_NOT_EXIST, urlString(src));
2042 return;
2044 error(KIO::ERR_SLAVE_DEFINED, i18n("Internal error - could not find font."));
2045 return;
2047 else if(enable || disable)
2048 error(KIO::ERR_SLAVE_DEFINED, enable
2049 ? i18n("Could not enable %1\n"
2050 "An enabled font already exists, please delete the disabled one.",
2051 urlString(src))
2052 : i18n("Could not disable %1\n"
2053 "A disabled font already exists, please delete the enabled one.",
2054 urlString(src)));
2055 else
2056 error(KIO::ERR_SLAVE_DEFINED, i18n("Sorry, fonts cannot be renamed."));
2058 return;
2060 error(KIO::ERR_DOES_NOT_EXIST, urlString(src));
2061 return;
2063 else if(itsRoot) // Should never happen...
2065 error(KIO::ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, KIO::CMD_RENAME));
2066 return;
2068 else if(Misc::isHidden(src) || Misc::isHidden(d))
2070 error(KIO::ERR_SLAVE_DEFINED, i18n("Cannot move %1 to %2\nDisabled fonts cannot be moved.",
2071 urlString(src), urlString(d)));
2072 return;
2074 else
2077 // Can't rename from/to file:/ -> therefore rename can only be from fonts:/System to
2078 // fonts:/Personal, or vice versa.
2079 CDisabledFonts::TFileList srcFiles;
2081 if(getSourceFiles(src, srcFiles)) // Any error will be logged in getSourceFiles
2083 KUrl dest(d);
2085 correctUrl(dest);
2087 EFolder destFolder(getFolder(dest));
2088 QMap<QString, QString> map;
2090 if(confirmMultiple(src, srcFiles, FOLDER_SYS==destFolder ? FOLDER_USER : FOLDER_SYS, OP_MOVE) &&
2091 getFontList(srcFiles, map) && checkDestFiles(src, map, dest, destFolder, flags))
2093 QMap<QString, QString>::Iterator fIt(map.begin()),
2094 fEnd(map.end());
2095 bool askPasswd=true,
2096 toSys=FOLDER_SYS==destFolder;
2098 for(; fIt!=fEnd; ++fIt)
2100 QString destFolderReal(getDestFolder(itsFolders[destFolder].location, fIt.value()));
2101 QList<TCommand> cmd;
2102 TCommand c(KFI::CMD_MOVE_FILE);
2104 if(toSys)
2106 addCreateFolderCmd(itsFolders[FOLDER_SYS].location, cmd);
2107 if(destFolderReal!=itsFolders[FOLDER_SYS].location)
2108 addCreateFolderCmd(destFolderReal, cmd);
2111 c.args.append(fIt.key());
2112 c.args.append(destFolderReal+fIt.value());
2113 c.args.append((int)(toSys ? 0 : getuid()));
2114 c.args.append((int)(toSys ? 0 : getgid()));
2115 cmd.append(c);
2117 QString sysDir,
2118 userDir;
2120 if(FOLDER_SYS==destFolder)
2122 sysDir=destFolderReal;
2123 userDir=Misc::getDir(fIt.key());
2125 else
2127 userDir=destFolderReal;
2128 sysDir=Misc::getDir(fIt.key());
2131 if(doRootCmd(toSys ? d : src, cmd, askPasswd))
2133 modified(timeout, FOLDER_SYS, true, sysDir);
2134 modified(timeout, FOLDER_USER, true, userDir);
2135 askPasswd=false; // Don't keep on asking for password...
2137 else
2139 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
2140 return;
2146 finished();
2149 void CKioFonts::del(const KUrl &url, bool)
2151 KFI_DBUG << url.path() << " query:" << url.query();
2153 const CDisabledFonts::TFileList *entries;
2154 CDisabledFonts::TFontList::Iterator disabledIt;
2155 TFontMap::Iterator enabledIt;
2157 if(checkUrl(url) && checkAllowed(url) &&
2158 (( (entries=getEntries(url, enabledIt, disabledIt)) && entries->count() && checkFiles(*entries)) ||
2159 ( updateFontList() && (entries=getEntries(url, enabledIt, disabledIt)) && entries->count() &&
2160 checkFiles(*entries))) && confirmMultiple(url, entries, getFolder(url), OP_DELETE))
2162 CDisabledFonts::TFileList::ConstIterator it,
2163 end(entries->end());
2164 CDirList modifiedDirs;
2165 bool clearList(!hasMetaData(KFI_KIO_NO_CLEAR));
2166 int timeout(reconfigTimeout());
2168 if(nonRootSys(url))
2170 if(disabledIt!=itsFolders[FOLDER_SYS].disabled->items().end())
2172 TCommand c(KFI::CMD_DELETE_DISABLED_FONT);
2174 c.args.append((*disabledIt).family);
2175 c.args.append((int)(*disabledIt).styleInfo);
2177 if(doRootCmd(url, c))
2178 itsFolders[FOLDER_SYS].disabled->refresh();
2179 else
2181 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
2182 return;
2185 else
2187 QList<TCommand> cmd;
2189 for(it=entries->begin(); it!=end; ++it)
2191 modifiedDirs.add(Misc::getDir(*it));
2192 cmd.append(TCommand(KFI::CMD_DELETE_FILE, (*it).path));
2194 QStringList files;
2196 Misc::getAssociatedFiles(*it, files);
2198 if(files.count())
2200 QStringList::const_iterator fIt,
2201 fEnd=files.constEnd();
2203 for(fIt=files.constBegin(); fIt!=fEnd; ++fIt)
2204 cmd.append(TCommand(KFI::CMD_DELETE_FILE, *fIt));
2208 if(doRootCmd(url, cmd))
2209 modified(timeout, FOLDER_SYS, clearList, modifiedDirs);
2210 else
2212 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
2213 return;
2217 else
2219 for(it=entries->begin(); it!=end; ++it)
2221 if (0!=unlink(QFile::encodeName(*it).constData()))
2222 error(EACCES==errno || EPERM==errno
2223 ? KIO::ERR_ACCESS_DENIED
2224 : EISDIR==errno
2225 ? KIO::ERR_IS_DIRECTORY
2226 : KIO::ERR_CANNOT_DELETE,
2227 *it);
2228 else
2230 modifiedDirs.add(Misc::getDir(*it));
2232 QStringList files;
2234 Misc::getAssociatedFiles(*it, files);
2236 if(files.count())
2238 QStringList::const_iterator fIt,
2239 fEnd=files.constEnd();
2241 for(fIt=files.constBegin(); fIt!=fEnd; ++fIt)
2242 unlink(QFile::encodeName(*fIt).constData());
2247 if(disabledIt!=itsFolders[itsRoot ? FOLDER_SYS : FOLDER_USER].disabled->items().end())
2249 itsFolders[itsRoot ? FOLDER_SYS : FOLDER_USER].disabled->remove(disabledIt);
2250 itsFolders[itsRoot ? FOLDER_SYS : FOLDER_USER].disabled->refresh();
2252 else
2253 modified(timeout, itsRoot ? FOLDER_SYS : FOLDER_USER, clearList, modifiedDirs);
2255 finished();
2257 else if(isATtc(QFile::encodeName(url.fileName())))
2258 finished();
2259 else
2260 error(KIO::ERR_SLAVE_DEFINED, i18n("Could not access \"%1\".", urlString(url)));
2263 void CKioFonts::modified(int timeout, EFolder folder, bool clearList, const CDirList &dirs)
2265 KFI_DBUG << "timout:" << timeout << " folder:" << (int)folder << " clearList:" << clearList;
2267 if(dirs.count())
2269 CDirList::ConstIterator it(dirs.begin()),
2270 end(dirs.end());
2272 for(; it!=end; ++it)
2273 itsFolders[folder].modified.add(*it);
2275 else
2276 itsFolders[folder].modified.add(itsFolders[folder].location);
2277 setTimeoutSpecialCommand(timeout ? timeout : -1);
2279 if(clearList)
2280 clearFontList(); // List of fonts has changed.../
2283 void CKioFonts::special(const QByteArray &a)
2285 KFI_DBUG;
2287 if(a.size())
2289 QDataStream stream(a);
2290 int cmd;
2292 stream >> cmd;
2294 switch (cmd)
2296 case SPECIAL_RESCAN:
2297 clearFontList();
2298 updateFontList();
2299 finished();
2300 break;
2301 case SPECIAL_CONFIGURE:
2303 if(itsRoot)
2305 KUrl url;
2307 stream >> url;
2309 if(url.isValid())
2310 itsFolders[FOLDER_SYS].disabled->reload();
2311 if(0==itsFolders[FOLDER_SYS].modified.count())
2312 configure(FOLDER_SYS);
2314 else
2315 for(;;)
2317 KUrl url;
2319 stream >> url;
2321 if(url.isEmpty() || !url.isValid())
2322 break;
2324 QString sect(getSect(url.path()));
2326 if(isSysFolder(sect))
2328 itsFolders[FOLDER_SYS].disabled->reload();
2329 if(0==itsFolders[FOLDER_SYS].modified.count())
2330 configure(FOLDER_SYS);
2332 else if(isUserFolder(sect))
2334 itsFolders[FOLDER_USER].disabled->reload();
2335 if(0==itsFolders[FOLDER_USER].modified.count())
2336 configure(FOLDER_USER);
2340 if(itsFolders[FOLDER_USER].disabled->modified())
2341 itsFolders[FOLDER_USER].disabled->reload();
2342 doModified();
2343 clearFontList();
2344 updateFontList();
2345 finished();
2346 break;
2348 default:
2349 error(KIO::ERR_UNSUPPORTED_ACTION, QString::number(cmd));
2352 else
2353 doModified();
2356 bool CKioFonts::configure(EFolder folder)
2358 bool refreshX(false);
2360 if(!itsRoot && FOLDER_SYS==folder)
2362 QList<TCommand> cmd;
2364 if(itsAddToSysFc)
2366 itsAddToSysFc=false;
2367 cmd.append(TCommand(KFI::CMD_ADD_DIR_TO_FONTCONFIG, itsFolders[FOLDER_SYS].location));
2370 if(itsFolders[FOLDER_SYS].modified.count())
2372 CDirList::ConstIterator it(itsFolders[FOLDER_SYS].modified.begin()),
2373 end(itsFolders[FOLDER_SYS].modified.end());
2375 for(; it!=end; ++it)
2376 if(Misc::fExists((*it)+"fonts.dir"))
2378 cmd.append(TCommand(KFI::CMD_CFG_DIR_FOR_X, *it));
2379 refreshX=true;
2383 cmd.append(TCommand(KFI::CMD_FC_CACHE));
2384 doRootCmd(KUrl(), cmd, false);
2386 else
2388 Misc::doCmd(FC_CACHE_CMD);
2389 KFI_DBUG << "RUN: " << FC_CACHE_CMD;
2391 itsFolders[folder].disabled->save();
2393 CDirList::ConstIterator it(itsFolders[folder].modified.begin()),
2394 end(itsFolders[folder].modified.end());
2396 for(; it!=end; ++it)
2397 if(Misc::fExists((*it)+"fonts.dir"))
2399 Misc::configureForX11(*it);
2400 refreshX=true;
2403 return refreshX;
2406 void CKioFonts::doModified()
2408 KFI_DBUG;
2409 bool refreshX(false),
2410 clear(false);
2412 infoMessage(i18n("Configuring installed fonts..."));
2413 setTimeoutSpecialCommand(-1); // Cancel timer
2415 if(itsFolders[FOLDER_SYS].modified.count())
2417 refreshX=configure(FOLDER_SYS);
2418 itsFolders[FOLDER_SYS].modified.clear();
2419 clear=true;
2422 if(!itsRoot && itsFolders[FOLDER_USER].modified.count())
2424 refreshX=configure(FOLDER_USER);
2425 itsFolders[FOLDER_USER].modified.clear();
2426 clear=true;
2429 if(clear)
2430 clearFontList();
2431 if(refreshX)
2432 Misc::doCmd("xset", "fp", "rehash");
2433 infoMessage(QString());
2434 KFI_DBUG << "finished";
2437 bool CKioFonts::getRootPasswd(const KUrl &url, bool askPasswd)
2439 KFI_DBUG;
2441 #if defined USE_POLICYKIT && USE_POLICYKIT==1
2442 if(url.hasUser() && !url.pass().isEmpty() && KFI_SYS_USER==url.user())
2444 bool ok;
2445 int v=url.pass().toInt(&ok);
2447 if(ok)
2448 itsPid=v;
2451 KFI_DBUG << "askPasswd:" << askPasswd << " itsPid:" << itsPid;
2453 // If we have a pid, check that its still alive!
2454 if(0!=itsPid && 0!=kill(itsPid, 0))
2455 itsPid=getpid();
2457 if(!askPasswd)
2458 return 0!=itsPid;
2460 uint winId=hasMetaData("window-id") ? metaData("window-id").toInt() : 0,
2461 pid=0==itsPid ? getpid() : itsPid;
2463 KFI_DBUG << "ObtainAuthorization pid:" << pid << " winId:" << winId;
2465 if(PolicyKitAuthenticator::instance()->authenticate(KFI_IFACE, winId, pid, false))
2467 KFI_DBUG << "Authorization obtained";
2468 itsPid=pid;
2469 return true;
2472 // If pids do not match, its possible (probable!) that the calling app has already
2473 // authenticated, if so ObtainAuthorization returns false on sucessive attempts!
2474 return itsPid!=getpid();
2475 #else
2476 if(url.hasUser() && !url.pass().isEmpty() && KFI_SYS_USER==url.user())
2478 itsPasswd=url.pass();
2479 return !itsPasswd.isEmpty();
2482 if(!askPasswd)
2483 return !itsPasswd.isEmpty();
2485 KIO::AuthInfo authInfo;
2486 SuProcess proc(KFI_SYS_USER);
2487 bool error=false;
2488 int attempts=0;
2489 QString errorMsg;
2491 authInfo.url=KUrl(KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS"/");
2492 authInfo.keepPassword=false;
2493 authInfo.caption=i18n("Authorization Required");
2494 authInfo.username=i18n(KFI_AUTHINF_USER);
2496 if(proc.useUsersOwnPassword())
2497 authInfo.prompt=i18n("The requested action requires administrator privileges.\n"
2498 "If you have these privileges then please enter your password.");
2499 else
2500 authInfo.prompt=i18n("The requested action requires administrator privileges.\n"
2501 "Please enter the system administrator's password.");
2503 if(!checkCachedAuthentication(authInfo) && !askPasswd)
2504 authInfo.password=itsPasswd;
2506 if(askPasswd)
2508 while(!error && 0!=proc.checkInstall(authInfo.password.toLocal8Bit()))
2510 KFI_DBUG << "ATTEMPT : " << attempts;
2511 if(1==attempts++)
2512 errorMsg=i18n("Incorrect password.\n");
2513 if(attempts>2 || !openPasswordDialog(authInfo, errorMsg))
2514 error=true;
2516 if(!error && authInfo.keepPassword)
2517 cacheAuthentication(authInfo);
2519 else
2520 error=proc.checkInstall(authInfo.password.toLocal8Bit()) ? true : false;
2522 itsPasswd= error ? QString() : authInfo.password;
2523 return !itsPasswd.isEmpty();
2524 #endif
2527 #if !(defined USE_POLICYKIT && USE_POLICYKIT==1)
2528 void CKioFonts::quitHelper()
2530 if(itsServer.isOpen() && itsSuProc && itsSocket && itsSuProc->isRunning())
2532 KFI_DBUG;
2533 if(itsSocket->write(QVariant((int)KFI::CMD_QUIT)))
2535 bool res;
2536 if(itsSocket->read(res, 10) && res)
2538 itsSuProc->terminate();
2539 itsSuProc->wait(100);
2544 #endif
2546 bool CKioFonts::doRootCmd(const KUrl &url, QList<TCommand> &cmd, bool askPasswd)
2548 KFI_DBUG;
2550 if(cmd.count() && getRootPasswd(url, askPasswd))
2552 #if defined USE_POLICYKIT && USE_POLICYKIT==1
2553 QList<TCommand>::ConstIterator it(cmd.begin()),
2554 end(cmd.end());
2555 bool error(false);
2557 if(!itsFontInstIface)
2558 itsFontInstIface=new org::kde::fontinst(KFI_IFACE, "/FontInst", QDBusConnection::systemBus());
2560 for(; it!=end && !error; ++it)
2562 int response(-1);
2564 KFI_DBUG << "do cmd " << (int)((*it).cmd);
2566 switch((*it).cmd)
2568 case CMD_ENABLE_FONT:
2569 if(2==(*it).args.count())
2570 response=itsFontInstIface->enableFont(itsPid, (*it).args[0].toString(),
2571 (*it).args[1].toUInt()).value();
2572 else
2573 error=true;
2574 break;
2575 case CMD_DISABLE_FONT:
2576 if((*it).args.count()>=7 && (*it).args.count()%2)
2578 QStringList fileData;
2579 QList<QVariant>::ConstIterator fit((*it).args.begin()),
2580 fend((*it).args.end());
2582 for(int i=0; i<5; ++i)
2583 ++fit;
2585 for(; fit!=fend; ++fit)
2586 fileData.append((*fit).toString());
2588 response=itsFontInstIface->disableFont(itsPid, (*it).args[0].toString(),
2589 (*it).args[1].toUInt(), (*it).args[2].toULongLong(),
2590 (*it).args[3].toUInt(), fileData).value();
2592 else
2593 error=true;
2594 break;
2595 case CMD_DELETE_DISABLED_FONT:
2596 if(2==(*it).args.count())
2597 response=itsFontInstIface->deleteDisabledFont(itsPid, (*it).args[0].toString(),
2598 (*it).args[1].toUInt()).value();
2599 else
2600 error=true;
2601 break;
2602 case CMD_RELOAD_DISABLED_LIST:
2603 if(0==(*it).args.count())
2604 response=itsFontInstIface->reloadDisabledList(itsPid);
2605 else
2606 error=true;
2607 break;
2608 case CMD_COPY_FILE:
2609 if(2==(*it).args.count())
2610 response=itsFontInstIface->copyFont(itsPid, (*it).args[0].toString(),
2611 (*it).args[1].toString()).value();
2612 else
2613 error=true;
2614 break;
2615 case CMD_MOVE_FILE:
2616 if(4==(*it).args.count())
2617 response=itsFontInstIface->moveFont(itsPid, (*it).args[0].toString(),
2618 (*it).args[1].toString(), (*it).args[2].toUInt(),
2619 (*it).args[3].toUInt()).value();
2620 else
2621 error=true;
2622 break;
2623 case CMD_DELETE_FILE:
2624 if(1==(*it).args.count())
2625 response=itsFontInstIface->deleteFont(itsPid, (*it).args[0].toString()).value();
2626 else
2627 error=true;
2628 break;
2629 case CMD_CREATE_DIR:
2630 if(1==(*it).args.count())
2632 KFI_DBUG << "Create dir " << (*it).args[0].toString();
2633 response=itsFontInstIface->createDir(itsPid, (*it).args[0].toString()).value();
2635 else
2636 error=true;
2637 break;
2638 case CMD_CREATE_AFM:
2639 if(1==(*it).args.count())
2640 response=itsFontInstIface->createAfm(itsPid, (*it).args[0].toString()).value();
2641 else
2642 error=true;
2643 break;
2644 case CMD_FC_CACHE:
2645 if(0==(*it).args.count())
2646 response=doLongRootCmd("fcCache");
2647 else
2648 error=true;
2649 break;
2650 case CMD_ADD_DIR_TO_FONTCONFIG:
2651 if(1==(*it).args.count())
2652 response=itsFontInstIface->addToFc(itsPid, (*it).args[0].toString()).value();
2653 else
2654 error=true;
2655 break;
2656 case CMD_CFG_DIR_FOR_X:
2657 if(1==(*it).args.count())
2658 response=doLongRootCmd("configureX", (*it).args[0].toString());
2659 else
2660 error=true;
2661 break;
2662 default:
2663 error=true;
2664 break;
2667 if(!error)
2669 if(FontInst::CommandOk!=response)
2671 error=true;
2672 KFI_DBUG << "Failed";
2674 else
2675 KFI_DBUG << "Success";
2677 else
2678 KFI_DBUG << "Parameter error";
2681 return !error;
2682 #else
2683 if(!itsServer.isOpen())
2685 KFI_DBUG << "Open server socket";
2686 // Open socket for communication with helper app...
2687 itsServer.open();
2690 if(itsServer.isOpen())
2692 if(itsSuProc && !itsSuProc->isRunning())
2694 KFI_DBUG << "Delete client socket";
2695 delete itsSocket;
2696 itsSocket=NULL;
2697 delete itsSuProc;
2698 itsSuProc=NULL;
2701 if(!itsSuProc)
2703 // Start helper app...
2704 KFI_DBUG << "Start helper...";
2705 itsSuProc=new CSuProc(itsServer.name(), itsPasswd);
2706 itsSuProc->start();
2709 if(!itsSocket)
2711 // Wait for helper app to connect...
2712 KFI_DBUG << "Wait for client...";
2713 itsSocket=itsServer.waitForClient();
2716 if(itsSocket)
2718 // Write commands to helper, and wait for replies...
2719 QList<TCommand>::ConstIterator it(cmd.begin()),
2720 end(cmd.end());
2721 bool commsError(false);
2723 for(; it!=end && !commsError; ++it)
2725 KFI_DBUG << "Send command #" << (*it).cmd;
2727 if(itsSocket->write(QVariant((int)(*it).cmd)))
2729 QList<QVariant>::ConstIterator argIt((*it).args.begin()),
2730 argEnd((*it).args.end());
2732 for(; argIt!=argEnd && !commsError; ++argIt)
2733 if(!itsSocket->write(*argIt))
2735 KFI_DBUG << "Failed to write arg";
2736 commsError=true;
2739 if(!commsError) // Wait for response!
2741 bool res;
2743 if(itsSocket->read(res, CMD_FC_CACHE==(*it).cmd ||
2744 CMD_CFG_DIR_FOR_X==(*it).cmd
2745 ? 600 // fc-cache can take a *long* time...
2746 : 10))
2748 if(!res &&
2749 CMD_ADD_DIR_TO_FONTCONFIG!=(*it).cmd &&
2750 CMD_CFG_DIR_FOR_X!=(*it).cmd)
2752 KFI_DBUG << "Command failed :-(";
2753 return false;
2756 else
2758 KFI_DBUG << "Failed to read response";
2759 commsError=true;
2763 else
2765 KFI_DBUG << "Failed to write command id";
2766 commsError=true;
2770 if(!commsError)
2771 return true;
2772 delete itsSocket;
2773 itsSocket=NULL;
2775 else
2776 KFI_DBUG << "No socket connection :-(";
2778 #endif
2781 return false;
2784 #if defined USE_POLICYKIT && USE_POLICYKIT==1
2785 int CKioFonts::doLongRootCmd(const QString &method, const QString &param)
2787 static const int constDBusTimeout=20*60*1000; // 20mins
2789 KFI_DBUG << method << param;
2791 QList<QVariant> args;
2792 QDBusMessage message=QDBusMessage::createMethodCall(itsFontInstIface->service(),
2793 itsFontInstIface->path(),
2794 itsFontInstIface->interface(),
2795 method);
2797 args << qVariantFromValue(itsPid);
2799 if(!param.isEmpty())
2800 args << qVariantFromValue(param);
2802 message.setArguments(args);
2804 QDBusMessage reply=itsFontInstIface->connection().call(message, QDBus::Block, constDBusTimeout);
2806 KFI_DBUG << "Got reply";
2808 if (QDBusMessage::ReplyMessage==reply.type())
2810 QDBusReply<int> r(reply);
2812 if (r.isValid())
2813 return r.value();
2816 return FontInst::CommandFailed;
2818 #endif
2820 bool CKioFonts::doRootCmd(const KUrl &url, const TCommand &cmd, bool askPasswd)
2822 QList<TCommand> cmds;
2824 cmds.append(cmd);
2825 return doRootCmd(url, cmds, askPasswd);
2828 void CKioFonts::correctUrl(KUrl &url)
2830 KFI_DBUG << url.path();
2831 if(!itsRoot)
2833 QString sect(getSect(url.path()));
2835 if(!isSysFolder(sect) && !isUserFolder(sect))
2837 url.setPath(QChar('/')+i18n(KFI_KIO_FONTS_USER)+QChar('/')+url.fileName());
2838 KFI_DBUG << "Changed URL to:" << url.path();
2843 void CKioFonts::clearFontList()
2845 KFI_DBUG;
2847 if(itsFontList)
2848 FcFontSetDestroy(itsFontList);
2850 itsFontList=NULL;
2851 itsFolders[FOLDER_SYS].fontMap.clear();
2852 if(!itsRoot)
2853 itsFolders[FOLDER_USER].fontMap.clear();
2856 bool CKioFonts::updateFontList()
2858 KFI_DBUG;
2860 // For some reason just the "!FcConfigUptoDate(0)" check does not always work :-(
2861 if(0!=itsLastFcCheckTime &&
2862 (!itsFontList || !FcConfigUptoDate(0) ||
2863 (abs(time(NULL)-itsLastFcCheckTime)>constMaxFcCheckTime)))
2865 KFI_DBUG << "itsFontList:" << (intptr_t)itsFontList
2866 << " FcConfigUptoDate:" << (int)FcConfigUptoDate(0)
2867 << " time diff:" << abs(time(NULL)-itsLastFcCheckTime)
2868 << " max:" << constMaxFcCheckTime;
2869 FcInitReinitialize();
2870 clearFontList();
2873 if(!itsRoot)
2875 #if defined USE_POLICYKIT && USE_POLICYKIT==1
2876 if(0!=itsPid)
2877 #else
2878 if(itsServer.isOpen() && itsSuProc && itsSocket)
2879 #endif
2880 doRootCmd(KUrl(), TCommand(KFI::CMD_RELOAD_DISABLED_LIST), false);
2881 itsFolders[FOLDER_USER].disabled->refresh();
2883 itsFolders[FOLDER_SYS].disabled->refresh();
2885 if(!itsFontList)
2887 KFI_DBUG << "update list of fonts";
2889 itsLastFcCheckTime=time(NULL);
2891 FcPattern *pat = FcPatternCreate();
2892 FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_FAMILY,
2893 #ifdef KFI_USE_TRANSLATED_FAMILY_NAME
2894 FC_FAMILYLANG,
2895 #endif
2896 FC_WEIGHT, FC_LANG, FC_CHARSET,
2897 //FC_SCALABLE,
2898 #ifndef KFI_FC_NO_WIDTHS
2899 FC_WIDTH,
2900 #endif
2901 FC_SLANT, FC_INDEX, FC_FOUNDRY, (void*)0);
2903 itsFontList=FcFontList(0, pat, os);
2905 FcPatternDestroy(pat);
2906 FcObjectSetDestroy(os);
2908 if (itsFontList)
2911 // defoma (DEbian FOnt MAnanager) installs sym links into /var/lib/defoma/fontconfig.d, but also
2912 // places this folder into fontconfigs search path. Leading to duplicate font files. Therefore, just
2913 // ignore defoma's sym links...
2914 // -> Can't just ignore these, as if a font is disabled fontconfig will still list it, as it sees the symlink
2915 //static const char * constDefomaLocation="/var/lib/defoma/fontconfig.d";
2917 QString home(Misc::dirSyntax(QDir::homePath()));
2919 for (int i = 0; i < itsFontList->nfont; i++)
2921 EFolder folder=FOLDER_SYS;
2922 QString fileName(Misc::fileSyntax(FC::getFcString(itsFontList->fonts[i], FC_FILE)));
2924 if(!fileName.isEmpty()) // && 0!=fileName.indexOf(constDefomaLocation))
2926 QString name,
2927 foundry(FC::getFcString(itsFontList->fonts[i], FC_FOUNDRY));
2928 quint32 styleVal;
2929 int index;
2931 if(!itsRoot && 0==fileName.indexOf(home))
2932 folder=FOLDER_USER;
2934 FC::getDetails(itsFontList->fonts[i], name, styleVal, index);
2936 TFontDetails &details=itsFolders[folder].fontMap[name];
2937 bool use=true;
2939 details.styleVal=styleVal;
2940 details.writingSystems|=getWritingSystems(itsFontList->fonts[i]);
2942 if(details.files.count()) // Check for duplicates...
2944 CDisabledFonts::TFileList::Iterator it,
2945 end=details.files.end();
2947 for(it=details.files.begin(); use && it!=end; ++it)
2948 if(fileName==*it)
2949 use=false;
2951 if(use)
2952 details.files.append(CDisabledFonts::TFile(fileName, index, foundry));
2957 KFI_DBUG << "updated list of fonts";
2960 if(NULL==itsFontList)
2962 error(KIO::ERR_SLAVE_DEFINED, i18n("Internal fontconfig error."));
2963 return false;
2966 return true;
2969 CKioFonts::EFolder CKioFonts::getFolder(const KUrl &url)
2971 return itsRoot || isSysFolder(getSect(url.path())) ? FOLDER_SYS : FOLDER_USER;
2974 CKioFonts::TFontMap::Iterator CKioFonts::getMap(const KUrl &url)
2976 KFI_DBUG << url.prettyUrl();
2978 int face(Misc::getIntQueryVal(url, KFI_KIO_FACE, 0));
2979 EFolder folder(getFolder(url));
2980 TFontMap::Iterator it=itsFolders[folder].fontMap.find(removeMultipleExtension(url)),
2981 end(itsFolders[folder].fontMap.end());
2983 if(it==end) // Perhaps it was fonts:/System/times.ttf ???
2985 QString fName(Misc::getFile(url.path()));
2987 for(int t=0; t<3; ++t)
2989 QString fileName=0==t
2990 ? fName
2991 : 1==t ? modifyName(fName) // lowercase
2992 : modifyName(fName, true); // uppercase
2994 KFI_DBUG << "look for " << fileName;
2996 for(it=itsFolders[folder].fontMap.begin(); it!=end; ++it)
2998 CDisabledFonts::TFileList::Iterator sIt((*it).files.begin()),
2999 sEnd((*it).files.end());
3001 for(;sIt!=sEnd; ++sIt)
3002 if(Misc::getFile(*sIt)==fileName && (*sIt).face==face)
3003 return it;
3008 return it;
3011 const CDisabledFonts::TFileList * CKioFonts::getEntries(const KUrl &url,
3012 TFontMap::Iterator &enabledIt,
3013 CDisabledFonts::TFontList::Iterator &disabledIt)
3015 KFI_DBUG << url.prettyUrl();
3017 EFolder folder=getFolder(url);
3018 TFontMap::Iterator it(getMap(url)),
3019 end(itsFolders[folder].fontMap.end());
3020 QString name=Misc::getFile(removeMultipleExtension(url));
3021 CDisabledFonts::TFontList::Iterator dIt(itsFolders[folder].disabled->find(name,
3022 Misc::getIntQueryVal(url, KFI_KIO_FACE, 0))),
3023 dEnd(itsFolders[folder].disabled->items().end());
3025 enabledIt=end;
3026 disabledIt=dEnd;
3028 if(it!=end && dIt==dEnd)
3030 KFI_DBUG << "found enabled";
3031 enabledIt=it;
3032 return &(it.value().files);
3034 else if (it==end && dIt!=dEnd)
3036 disabledIt=dIt;
3037 KFI_DBUG << "found disabled";
3038 return &((*dIt).files);
3040 else if(it!=end && dIt!=dEnd)
3042 KFI_DBUG << "found both!";
3044 // Oops... we have a match for both a hidden, and non-hidden font! Have to ask which one...
3045 // This should never really happen, as hidden fonts will start with a period.
3046 if(KMessageBox::Yes==messageBox(QuestionYesNo,
3047 i18n("The selected URL (%1) matches both an enabled, and disabled "
3048 "font. Which one do you wish to access?", url.prettyUrl()),
3049 i18n("Duplicate Font"), i18n("Enabled Font"),
3050 i18n("Disabled Font")))
3052 enabledIt=it;
3053 return &(it.value().files);
3055 else
3057 disabledIt=dIt;
3058 return &((*dIt).files);
3062 KFI_DBUG << "found none";
3063 return NULL;
3066 QStringList CKioFonts::getFontNameEntries(EFolder folder, const QString &file, bool disabledFonts)
3068 QStringList rv;
3070 if(disabledFonts)
3072 CDisabledFonts::TFontList::Iterator it(itsFolders[folder].disabled->items().begin()),
3073 end(itsFolders[folder].disabled->items().end());
3075 for(; it!=end; ++it)
3077 CDisabledFonts::TFileList::ConstIterator patIt,
3078 patEnd=(*it).files.end();
3080 for(patIt=(*it).files.begin(); patIt!=patEnd; ++patIt)
3081 if((*patIt).path==file)
3083 rv.append((*it).name);
3084 break;
3088 else
3090 TFontMap::Iterator it,
3091 end=itsFolders[folder].fontMap.end();
3093 for(it=itsFolders[folder].fontMap.begin(); it!=end; ++it)
3095 CDisabledFonts::TFileList::ConstIterator patIt,
3096 patEnd=it.value().files.constEnd();
3098 for(patIt=it.value().files.constBegin(); patIt!=patEnd; ++patIt)
3099 if((*patIt).path==file)
3101 rv.append(it.key());
3102 break;
3106 return rv;
3109 QMap<int, QString> CKioFonts::getFontIndexToNameEntries(EFolder folder, const QString &file)
3111 QMap<int, QString> rv;
3112 TFontMap::Iterator it,
3113 end=itsFolders[folder].fontMap.end();
3115 for(it=itsFolders[folder].fontMap.begin(); it!=end; ++it)
3117 CDisabledFonts::TFileList::Iterator patIt,
3118 patEnd=it.value().files.end();
3120 for(patIt=it.value().files.begin(); patIt!=patEnd; ++patIt)
3121 if((*patIt).path==file)
3123 rv[(*patIt).face]=it.key();
3124 break;
3128 return rv;
3131 QString * CKioFonts::getEntry(EFolder folder, const QString &file, bool full)
3133 TFontMap::Iterator it,
3134 end=itsFolders[folder].fontMap.end();
3136 for(it=itsFolders[folder].fontMap.begin(); it!=end; ++it)
3138 CDisabledFonts::TFileList::Iterator patIt,
3139 patEnd=it.value().files.end();
3141 for(patIt=it.value().files.begin(); patIt!=patEnd; ++patIt)
3142 if( (full && (*patIt).path==file) ||
3143 (!full && Misc::getFile(*patIt)==file))
3144 return &((*patIt).path);
3147 return NULL;
3150 CKioFonts::EFileType CKioFonts::checkFile(const QString &file, const KUrl &url)
3153 // To speed things up, check the files extension 1st...
3154 if(Misc::checkExt(file, "bdf") || Misc::checkExt(file, "bdf.gz") ||
3155 Misc::checkExt(file, "pcf") || Misc::checkExt(file, "pcf.gz"))
3157 // Need to check whether bitmaps have been hidden from from fontconfig - as happens on KUbuntu...
3158 if(FC::bitmapsEnabled())
3159 return FILE_FONT;
3160 else
3161 error(KIO::ERR_SLAVE_DEFINED, i18n("You cannot install bitmap fonts, as these have been "
3162 "disabled on your system."));
3164 else if(isAAfm(file) || isAPfm(file))
3165 return FILE_METRICS;
3166 else if(Misc::isPackage(file))
3167 error(KIO::ERR_SLAVE_DEFINED, i18n("You cannot install a fonts package directly.\n"
3168 "Please extract %1, and install the components individually.",
3169 urlString(url)));
3170 else
3173 // Check that file is a font via FreeType...
3174 int count=0;
3175 FcPattern *pat=FcFreeTypeQuery((const FcChar8 *)(QFile::encodeName(file).constData()), 0, NULL,
3176 &count);
3178 if(pat)
3180 FcBool scalable;
3182 if(FcResultMatch==FcPatternGetBool(pat, FC_SCALABLE, 0, &scalable) && scalable)
3184 // check too see whether font is already installed!
3185 int weight(KFI_NULL_SETTING), slant(KFI_NULL_SETTING), width(KFI_NULL_SETTING);
3187 FcPatternGetInteger(pat, FC_WEIGHT, 0, &weight);
3188 FcPatternGetInteger(pat, FC_SLANT, 0, &slant);
3189 #ifndef KFI_FC_NO_WIDTHS
3190 FcPatternGetInteger(pat, FC_WIDTH, 0, &width);
3191 #endif
3193 QString name(FC::createName(pat, weight, width, slant));
3195 KFI_DBUG << "Check for name:" << name;
3197 // TODO: CDisabledFonts need to find on family & style info? Also need a find() that does
3198 // not take into account face! Perhaps use -1?
3199 if(itsFolders[FOLDER_SYS].fontMap.contains(name) ||
3200 itsFolders[FOLDER_SYS].disabled->items().end()!=
3201 itsFolders[FOLDER_SYS].disabled->find(name, 1) ||
3202 (!itsRoot && (itsFolders[FOLDER_USER].fontMap.contains(name) ||
3203 itsFolders[FOLDER_USER].disabled->items().end()!=
3204 itsFolders[FOLDER_USER].disabled->find(name, 1))))
3206 FcPatternDestroy(pat);
3207 error(KIO::ERR_SLAVE_DEFINED, i18n("File %1 contains the font:\n%2\n"
3208 "A font with this name is already installed.\n",
3209 urlString(url), name));
3210 return FILE_UNKNOWN;
3213 FcPatternDestroy(pat);
3214 return FILE_FONT;
3217 error(KIO::ERR_SLAVE_DEFINED, i18n("Could not determine file type for: %1\n"
3218 "Only fonts may be installed.", urlString(url)));
3220 return FILE_UNKNOWN;
3223 bool CKioFonts::getSourceFiles(const KUrl &src, CDisabledFonts::TFileList &files, bool removeSymLinks)
3225 if(KFI_KIO_FONTS_PROTOCOL==src.protocol())
3227 CDisabledFonts::TFontList::Iterator disabledIt;
3228 TFontMap::Iterator enabledIt;
3229 const CDisabledFonts::TFileList *entries=getEntries(src, enabledIt, disabledIt);
3231 if(entries)
3232 getFontFiles(*entries, files, removeSymLinks);
3234 else
3235 if(src.isLocalFile())
3236 if(FILE_UNKNOWN!=checkFile(src.path(), src))
3237 files.append(CDisabledFonts::TFile(src.path()));
3238 else
3239 return false; // error logged in checkFile...
3241 if(files.count())
3243 CDisabledFonts::TFileList::Iterator it,
3244 end=files.end();
3246 for(it=files.begin(); it!=end; ++it)
3248 QByteArray realSrc=QFile::encodeName(*it);
3249 KDE_struct_stat buffSrc;
3251 if (-1==KDE_stat(realSrc.constData(), &buffSrc))
3253 error(EACCES==errno ? KIO::ERR_ACCESS_DENIED : KIO::ERR_DOES_NOT_EXIST,
3254 urlString(src));
3255 return false;
3257 if(S_ISDIR(buffSrc.st_mode))
3259 error(KIO::ERR_IS_DIRECTORY, urlString(src));
3260 return false;
3262 if(S_ISFIFO(buffSrc.st_mode) || S_ISSOCK(buffSrc.st_mode))
3264 error(KIO::ERR_CANNOT_OPEN_FOR_READING, urlString(src));
3265 return false;
3269 else
3271 error(KIO::ERR_DOES_NOT_EXIST, urlString(src));
3272 return false;
3275 return true;
3278 bool CKioFonts::checkDestFile(const KUrl &src, const KUrl &dest, EFolder destFolder, KIO::JobFlags flags)
3280 QStringList folders;
3282 folders.append(itsFolders[destFolder].location);
3283 folders.append(getDestFolder(itsFolders[destFolder].location, src.fileName()));
3285 QStringList::const_iterator it(folders.begin()),
3286 end(folders.end());
3287 QString destFile;
3289 for(; it!=end; ++it)
3291 if(!(flags & KIO::Overwrite) && (Misc::fExists(destFile=(*it)+src.fileName()) ||
3292 Misc::fExists(destFile=(*it)+modifyName(src.fileName())) ||
3293 Misc::fExists(destFile=(*it)+modifyName(src.fileName(), true)) ) )
3295 // If copying / moving a TTC and it is the *same* file, then don't log an error, but
3296 // don't continue the transaction...
3298 // Reason being that fonts:/ lists the font names (not filenames) so for a TTC there'll
3299 // be multiple entries...
3300 if(isSameTtc(src.path(), destFile))
3301 finished();
3302 else
3303 error(KIO::ERR_FILE_ALREADY_EXIST, urlString(dest));
3304 return false;
3307 bool isHidden=Misc::isHidden(src);
3308 QString other(isHidden ? src.fileName().mid(1)
3309 : QChar('.')+src.fileName());
3311 if(Misc::fExists((*it)+other) ||
3312 Misc::fExists((*it)+modifyName(other)) ||
3313 Misc::fExists((*it)+modifyName(other, true)))
3315 error(KIO::ERR_SLAVE_DEFINED,
3316 isHidden
3317 ? i18n("Could not install %1\nA matching enabled font already exists. "
3318 "Please disable that.", urlString(src))
3319 : i18n("Could not install %1\nA matching disabled font already exists. "
3320 "Please enable that.", urlString(src)));
3321 return false;
3324 return true;
3327 bool CKioFonts::checkDestFiles(const KUrl &src, QMap<QString, QString> &map, const KUrl &dest,
3328 EFolder destFolder, KIO::JobFlags flags)
3331 // Check whether files exist at destination...
3333 if(dest.protocol()==src.protocol() &&
3334 dest.directory()==src.directory()) // Check whether confirmUrl changed a "cp fonts:/System
3335 // fonts:/" to "cp fonts:/System fonts:/System"
3337 error(KIO::ERR_FILE_ALREADY_EXIST, urlString(dest));
3338 return false;
3341 if(!(flags & KIO::Overwrite))
3343 QMap<QString, QString>::Iterator fIt(map.begin()),
3344 fEnd(map.end());
3345 QString *destEntry;
3347 for(; fIt!=fEnd; ++fIt)
3348 if(NULL!=(destEntry=getEntry(destFolder, fIt.value())) ||
3349 NULL!=(destEntry=getEntry(destFolder, modifyName(fIt.value()))) || // lowercase
3350 NULL!=(destEntry=getEntry(destFolder, modifyName(fIt.value()), true))) // uppercase
3352 // If copying / moving a TTC and it is the *same* file, then don't log an error, but
3353 // don't continue the transaction...
3355 // Reason being that fonts:/ lists the font names (not filenames) so for a TTC there'll
3356 // be multiple entries for a TTC...
3357 if(isSameTtc(src.path(), *destEntry))
3358 finished();
3359 else
3360 error(KIO::ERR_FILE_ALREADY_EXIST, urlString(dest));
3361 return false;
3365 return true;
3369 // Gather the number and names of the font faces located in "files". If there is more than 1 face
3370 // (such as there would be for a TTC font), then ask the user for confirmation of the action.
3371 bool CKioFonts::confirmMultiple(const KUrl &url, const CDisabledFonts::TFileList &files, EFolder folder,
3372 EOp op)
3374 if(KFI_KIO_FONTS_PROTOCOL!=url.protocol())
3375 return true;
3377 CDisabledFonts::TFileList::ConstIterator it,
3378 end=files.end();
3379 QStringList fonts;
3381 for(it=files.begin(); it!=files.end(); ++it)
3383 QStringList fn(getFontNameEntries(folder, *it, OP_ENABLE==op));
3384 QStringList::const_iterator fnIt(fn.begin()),
3385 fnEnd(fn.end());
3387 for(; fnIt!=fnEnd; ++fnIt)
3388 if(-1==fonts.indexOf(*fnIt))
3389 fonts.append(*fnIt);
3392 if(fonts.count()>1)
3394 QString out,
3395 question;
3396 QStringList::const_iterator it,
3397 end=fonts.constEnd();
3399 for(it=fonts.constBegin(); it!=end; ++it)
3400 out+=QString("<li>")+*it+QString("</li>");
3402 switch(op)
3404 case OP_MOVE:
3405 question=i18n("<p>You are attempting to move a font that is located in a file alongside "
3406 "other fonts; in order to proceed with the move they will "
3407 "all have to be moved. The affected fonts are:</p>"
3408 "<ul>%1</ul><p>\n Do you wish to move all of these?</p>", out);
3409 break;
3410 case OP_COPY:
3411 question=i18n("<p>You are attempting to copy a font that is located in a file alongside "
3412 "other fonts; in order to proceed with the copy they will "
3413 "all have to be copied. The affected fonts are:</p>"
3414 "<ul>%1</ul><p>\n Do you wish to copy all of these?</p>", out);
3415 break;
3416 case OP_DELETE:
3417 question=i18n("<p>You are attempting to delete a font that is located in a file alongside "
3418 "other fonts; in order to proceed with the deletion they will "
3419 "all have to be deleted. The affected fonts are:</p>"
3420 "<ul>%1</ul><p>\n Do you wish to delete all of these?</p>", out);
3421 break;
3422 case OP_ENABLE:
3423 question=i18n("<p>You are attempting to enable a font that is located in a file alongside "
3424 "other fonts; in order to proceed with the enabling they will "
3425 "all have to be enabled. The affected fonts are:</p>"
3426 "<ul>%1</ul><p>\n Do you wish to enable all of these?</p>", out);
3427 break;
3428 case OP_DISABLE:
3429 question=i18n("<p>You are attempting to disable a font that is located in a file alongside "
3430 "other fonts; in order to proceed with the disabling they will "
3431 "all have to be disabled. The affected fonts are:</p>"
3432 "<ul>%1</ul><p>\n Do you wish to disable all of these?</p>", out);
3433 break;
3436 if(KMessageBox::No==messageBox(question, QuestionYesNo))
3438 error(KIO::ERR_USER_CANCELED, urlString(url));
3439 return false;
3443 return true;
3446 bool CKioFonts::confirmMultiple(const KUrl &url, const CDisabledFonts::TFileList *patterns,
3447 EFolder folder, EOp op)
3449 if(KFI_KIO_FONTS_PROTOCOL!=url.protocol())
3450 return true;
3452 return patterns ? confirmMultiple(url, *patterns, folder, op) : false;
3455 bool CKioFonts::checkUrl(const KUrl &u, bool rootOk, bool logError)
3457 if(KFI_KIO_FONTS_PROTOCOL==u.protocol() && (!rootOk || (rootOk && "/"!=u.path())))
3459 QString sect(getSect(u.path()));
3461 if(itsRoot)
3463 if((isSysFolder(sect) || isUserFolder(sect)) &&
3464 (itsFolders[FOLDER_SYS].fontMap.end()==itsFolders[FOLDER_SYS].fontMap.find(sect)))
3465 //CPD: TODO: || it has a font specified! e.g. fonts:/System/Times -> even in have a
3466 // fonts:/System font, redirect should still happen
3468 redirection(getRedirect(u));
3469 finished();
3470 return false;
3473 else
3474 if(!isSysFolder(sect) && !isUserFolder(sect) && !isAllFolder(sect) )
3476 if(logError)
3477 error(KIO::ERR_SLAVE_DEFINED, i18n("Please specify \"%1\" or \"%2\".",
3478 i18n(KFI_KIO_FONTS_USER), i18n(KFI_KIO_FONTS_SYS)));
3479 return false;
3483 return true;
3486 bool CKioFonts::checkAllowed(const KUrl &u)
3488 if (KFI_KIO_FONTS_PROTOCOL==u.protocol())
3490 QString ds(Misc::dirSyntax(u.path()));
3492 if(ds==QString(QChar('/')+i18n(KFI_KIO_FONTS_USER)+QChar('/')) ||
3493 ds==QString(QChar('/')+i18n(KFI_KIO_FONTS_SYS)+QChar('/')) ||
3494 ds==QString(QChar('/')+QString::fromLatin1(KFI_KIO_FONTS_USER)+QChar('/')) ||
3495 ds==QString(QChar('/')+QString::fromLatin1(KFI_KIO_FONTS_SYS)+QChar('/')))
3497 error(KIO::ERR_SLAVE_DEFINED,
3498 i18n("You cannot rename, move, copy, or delete either \"%1\" or \"%2\".",
3499 i18n(KFI_KIO_FONTS_USER), i18n(KFI_KIO_FONTS_SYS))); \
3500 return false;
3504 return true;
3508 // Create an AFM from a Type 1 (pfa/pfb) font and its PFM file...
3509 void CKioFonts::createAfm(const QString &file, bool nrs)
3511 #if defined USE_POLICYKIT && USE_POLICYKIT==1
3512 if(nrs && 0==itsPid)
3513 return;
3514 #else
3515 if(nrs && itsPasswd.isEmpty())
3516 return;
3517 #endif
3519 bool type1=isAType1(file),
3520 pfm=!type1 && isAPfm(file); // No point checking if is pfm if its a type1
3522 if(type1 || pfm)
3524 // pf2afm wants files with lowercase extension, so just check for lowercase!
3525 // -- when a font is installed, the extension is converted to lowercase anyway...
3526 QString afm=getMatch(file, "afm");
3528 if(afm.isEmpty()) // No point creating if AFM already exists!
3530 QString pfm,
3533 if(type1) // Its a Type1, so look for existing PFM
3535 pfm=getMatch(file, "pfm");
3536 t1=file;
3538 else // Its a PFM, so look for existing Type1
3540 t1=getMatch(file, "pfa");
3541 if(t1.isEmpty())
3542 t1=getMatch(file, "pfb");
3543 pfm=file;
3546 if(!t1.isEmpty() && !pfm.isEmpty()) // Do we have both Type1 and PFM?
3548 QString name(t1.left(t1.length()-4)); // pf2afm wants name without extension...
3550 if(nrs)
3551 doRootCmd(KUrl(), TCommand(KFI::CMD_CREATE_AFM, name));
3552 else
3553 Misc::doCmd("pf2afm", QFile::encodeName(name));
3559 int CKioFonts::reconfigTimeout()
3561 return hasMetaData(KFI_KIO_TIMEOUT)
3562 ? metaData(KFI_KIO_TIMEOUT).toInt()
3563 : DEFAULT_TIMEOUT;
3566 void CKioFonts::TFolder::setLocation(const QString &l, bool sys)
3568 location=l;
3569 delete disabled;
3570 disabled=new CDisabledFonts(sys);