Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / kcontrol / kfontinst / kio / KioFonts.cpp
blob8ecfff0686d802b76b6bd6bdb7bfecffb3bffa63
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 #include "SuProc.h"
63 #include "Socket.h"
64 #include <ctype.h>
66 // Enable the following so that all URLs are actually <family>, <style>, e.g.
67 // without #define: fonts:/arial.ttf
68 // with #define: fonts:/Arial, Regular.ttf
70 // Not enabled - as it messes things up a little with fonts/group files.
71 //#define KFI_KIO_ALL_URLS_HAVE_NAME
73 #define KFI_DBUG kDebug(7000) << '(' << time(NULL) << ')'
75 #define MAX_IPC_SIZE (1024*32)
76 #define DEFAULT_TIMEOUT 2 // Time between last mod and writing files...
77 #define FC_CACHE_CMD "fc-cache"
79 using namespace KDESu;
81 static const int constMaxFcCheckTime=10;
83 extern "C"
85 KDE_EXPORT int kdemain(int argc, char **argv);
88 static KFI::CKioFonts *slaveInstance=NULL;
90 void kioFontsExitHandler()
92 if(slaveInstance)
94 slaveInstance->cleanup();
95 slaveInstance=NULL;
99 int kdemain(int argc, char **argv)
101 if (argc != 4)
103 fprintf(stderr, "Usage: kio_" KFI_KIO_FONTS_PROTOCOL
104 " protocol domain-socket1 domain-socket2\n");
105 exit(-1);
108 KLocale::setMainCatalog(KFI_CATALOGUE);
110 KComponentData componentData("kio_" KFI_KIO_FONTS_PROTOCOL);
111 KFI::CKioFonts slave(argv[2], argv[3]);
113 atexit(kioFontsExitHandler);
114 slave.dispatchLoop();
116 return 0;
119 namespace KFI
122 static bool addCreateFolderCmd(const QString &folder, QList<CKioFonts::TCommand> &cmd)
124 if(!Misc::dExists(folder))
126 cmd.append(CKioFonts::TCommand(KFI::CMD_CREATE_DIR, folder));
127 return true;
130 return false;
133 inline bool isSysFolder(const QString &sect)
135 return i18n(KFI_KIO_FONTS_SYS)==sect || KFI_KIO_FONTS_SYS==sect;
138 inline bool isUserFolder(const QString &sect)
140 return i18n(KFI_KIO_FONTS_USER)==sect || KFI_KIO_FONTS_USER==sect;
143 inline bool isAllFolder(const QString &sect)
145 return i18n(KFI_KIO_FONTS_ALL)==sect || KFI_KIO_FONTS_ALL==sect;
148 #ifdef KFI_KIO_ALL_URLS_HAVE_NAME
149 static const char *constExtensions[]=
150 {".ttf", KFI_FONTS_PACKAGE, ".otf", ".pfa", ".pfb", ".ttc",
151 ".pcf", ".pcf.gz", ".bdf", ".bdf.gz", NULL };
153 static QString removeKnownExtension(const KUrl &url)
155 QString fname(url.fileName());
156 int pos;
158 for(int i=0; constExtensions[i]; ++i)
159 if(-1!=(pos=fname.lastIndexOf(QString::fromLatin1(constExtensions[i]), -1, Qt::CaseInsensitive)))
160 return fname.left(pos);
161 return fname;
164 #define removeMultipleExtension(A) removeKnownExtension(A)
165 static void addKnownExtension(QString &url, const CDisabledFonts::TFileList &files, const QString &name,
166 bool hidden)
168 if(files.count()>1)
170 if(hidden)
171 url+='.';
172 url+=name+QString::fromLatin1(KFI_FONTS_PACKAGE);
174 else
176 QString fileName(Misc::getFile(files.first()));
177 int pos(0);
179 for(int i=0; constExtensions[i]; ++i)
180 if(-1!=(pos=fileName.lastIndexOf(QString::fromLatin1(constExtensions[i]), -1,
181 Qt::CaseInsensitive)))
183 if(hidden)
184 url+='.';
185 url+=name+constExtensions[i];
186 return;
188 url+=fileName;
191 #else
192 static QString removeMultipleExtension(const KUrl &url)
194 QString fname(url.fileName());
195 int pos;
197 if(-1!=(pos=fname.lastIndexOf(QString::fromLatin1(KFI_FONTS_PACKAGE))))
198 fname=fname.left(pos);
200 return fname;
202 #endif
204 static QString modifyName(const QString &fname, bool toUpper=false)
206 static const char constSymbols[]={ '-', ' ', ':', ';', '/', '~', 0 };
208 QString rv(toUpper ? fname.toUpper() : fname.toLower());
210 for(int s=0; constSymbols[s]; ++s)
211 rv=rv.replace(constSymbols[s], '_');
213 return rv;
216 static bool checkFiles(const CDisabledFonts::TFileList &files)
218 CDisabledFonts::TFileList::ConstIterator it(files.begin()),
219 end(files.end());
221 for(; it!=end; ++it)
222 if(!Misc::fExists(*it))
223 return false;
224 return true;
227 inline int getSize(KIO::UDSEntry &entry)
229 return entry.numberValue(KIO::UDSEntry::UDS_SIZE, 0);
232 static int getSize(const QByteArray &file)
234 KDE_struct_stat buff;
236 if(-1!=KDE_lstat(file, &buff))
238 if (S_ISLNK(buff.st_mode))
240 char buffer2[1000];
241 int n=readlink(file, buffer2, 1000);
242 if(n!= -1)
243 buffer2[n]='\0';
245 if(-1==KDE_stat(file, &buff))
246 return -1;
248 return buff.st_size;
251 return -1;
254 static int getSize(const CDisabledFonts::TFileList &files)
256 CDisabledFonts::TFileList::ConstIterator it,
257 end=files.end();
258 int size=0;
260 for(it=files.begin(); it!=end; ++it)
261 size+=getSize(QFile::encodeName(*it));
263 return size;
267 // Return real filename
268 // if normal file, return file
269 // if link, return dest
270 static QString getReal(const QString &file)
272 QByteArray cPath(QFile::encodeName(file));
273 KDE_struct_stat buff;
275 if(-1!=KDE_lstat(cPath, &buff) && S_ISLNK(buff.st_mode))
277 char buffer2[1000];
278 int n=readlink(cPath, buffer2, 1000);
280 if(n!= -1)
281 buffer2[n]='\0';
284 if('.'==buffer2[0]) // Relative link...
286 QString linkDest(QString::fromLocal8Bit(buffer2));
287 QDir d(Misc::getDir(file)+Misc::getDir(linkDest));
289 return Misc::dirSyntax(d.canonicalPath())+Misc::getFile(linkDest);
291 else
292 return QString::fromLocal8Bit(buffer2);
295 return file;
299 // Get the list of files associated with a list of entries...
300 // 1. get any associated afm or pfms
301 // 2. Resolve any symlinks
302 // 3. Remove duplicates (due to symlik and real being in list)
303 static void getFontFiles(const CDisabledFonts::TFileList &entries, CDisabledFonts::TFileList &files,
304 bool removeSymLinks=true)
306 CDisabledFonts::TFileList::ConstIterator it,
307 end=entries.end();
309 for(it=entries.begin(); it!=end; ++it)
311 QStringList assoc;
312 QString file(removeSymLinks ? getReal(*it) : (*it).path);
314 if(-1==files.indexOf(file) && Misc::fExists(file))
315 files.append(*it);
317 Misc::getAssociatedFiles(*it, assoc);
319 if(assoc.count())
321 QStringList::Iterator fIt,
322 fEnd=assoc.end();
324 for(fIt=assoc.begin(); fIt!=fEnd; ++fIt)
326 file=removeSymLinks ? getReal(*fIt) : *fIt;
328 if(!files.contains(file) && Misc::fExists(file))
329 files.append(CDisabledFonts::TFile(file));
335 static bool isScalable(const QString &str)
337 QByteArray cFile(QFile::encodeName(str));
339 return Misc::checkExt(cFile, "ttf") || Misc::checkExt(cFile, "otf") || Misc::checkExt(cFile, "ttc") ||
340 Misc::checkExt(cFile, "pfa") || Misc::checkExt(cFile, "pfb");
343 static bool isATtc(const QByteArray &file)
346 // To speed things up, check the files extension 1st...
347 if(Misc::checkExt(file, "ttc"))
348 return true;
349 else
351 // No '.ttc' exension match, so try querying with FreeType...
352 int count=0;
353 FcPattern *pat=FcFreeTypeQuery((const FcChar8 *)(QFile::encodeName(file).constData()), 0, NULL, &count);
355 if(pat)
357 FcPatternDestroy(pat);
358 return count>1; // Only care if TTC has more than 1 face...
361 return false;
364 // Check if
365 // ...src and dest are the *same* ttc file -> TTC copy
366 // ...or that src does not exist, and a new dest does -> TTC move
367 static bool isSameTtc(const QString &src, const QString &dest)
369 static const int constMaxTimeDiff=20;
371 QByteArray cDest(QFile::encodeName(dest));
373 if(isATtc(cDest))
375 QByteArray cSrc(QFile::encodeName(src));
377 KDE_struct_stat srcStat,
378 destStat;
379 bool srcExists=-1!=KDE_lstat(cSrc, &srcStat),
380 destExists=-1!=KDE_lstat(cDest, &destStat);
382 // Check that file sizes are the same, and that the dest file is recent...
383 if(srcExists && destExists)
385 if(srcStat.st_size==destStat.st_size && abs(destStat.st_atime-time(NULL))<constMaxTimeDiff)
387 // Sizes match, so check md5 sums...
388 QFile srcFile(src),
389 destFile(dest);
391 if(srcFile.open(QIODevice::ReadOnly) && destFile.open(QIODevice::ReadOnly))
393 KMD5 srcMd5,
394 destMd5;
396 return srcMd5.update(srcFile) && destMd5.update(destFile) &&
397 srcMd5.verify(destMd5.rawDigest());
401 else // In case of move, after the 1st, src won't exist, but dest should (and should be recent!)
402 return destExists && abs(destStat.st_atime-time(NULL))<constMaxTimeDiff;
405 return false;
408 enum EUrlStatus
410 BAD_Url,
411 Url_OK,
412 REDIRECT_Url
415 static KUrl getRedirect(const KUrl &u)
417 // Go from fonts:/System to fonts:/
419 KUrl redirect(u);
420 QString path(u.path()),
421 sect(CKioFonts::getSect(path));
423 path.remove(sect);
424 path.replace("//", "/");
425 redirect.setPath(path);
427 KFI_DBUG << "Redirect from " << u.path() << " to " << redirect.path();
428 return redirect;
431 static bool nonRootSys(const KUrl &u)
433 return !Misc::root() && isSysFolder(CKioFonts::getSect(u.path()));
436 static bool writeAll(int fd, const char *buf, size_t len)
438 while(len>0)
440 ssize_t written=write(fd, buf, len);
441 if (written<0 && EINTR!=errno)
442 return false;
443 buf+=written;
444 len-=written;
446 return true;
449 static bool isAAfm(const QString &fname)
451 if(Misc::checkExt(QFile::encodeName(fname), "afm")) // CPD? Is this a necessary check?
453 QFile file(fname);
455 if(file.open(QIODevice::ReadOnly))
457 QTextStream stream(&file);
458 QString line;
460 for(int lc=0; lc<30 && !stream.atEnd(); ++lc)
462 line=stream.readLine();
464 if(line.contains("StartFontMetrics"))
466 file.close();
467 return true;
471 file.close();
475 return false;
478 static bool isAPfm(const QString &fname)
480 bool ok=false;
482 // I know extension checking is bad, but Ghostscript's pf2afm requires the pfm file to
483 // have the .pfm extension...
484 QByteArray name(QFile::encodeName(fname));
486 if(Misc::checkExt(name, "pfm"))
489 // OK, the extension matches, so perform a little contents checking...
490 FILE *f=fopen(name.constData(), "r");
492 if(f)
494 static const unsigned long constCopyrightLen = 60;
495 static const unsigned long constTypeToExt = 49;
496 static const unsigned long constExtToFname = 20;
497 static const unsigned long constExtLen = 30;
498 static const unsigned long constFontnameMin = 75;
499 static const unsigned long constFontnameMax = 512;
501 unsigned short version=0,
502 type=0,
503 extlen=0;
504 unsigned long length=0,
505 fontname=0,
506 fLength=0;
508 fseek(f, 0, SEEK_END);
509 fLength=ftell(f);
510 fseek(f, 0, SEEK_SET);
512 if(2==fread(&version, 1, 2, f) && // Read version
513 4==fread(&length, 1, 4, f) && // length...
514 length==fLength &&
515 0==fseek(f, constCopyrightLen, SEEK_CUR) && // Skip copyright notice...
516 2==fread(&type, 1, 2, f) &&
517 0==fseek(f, constTypeToExt, SEEK_CUR) &&
518 2==fread(&extlen, 1, 2, f) &&
519 extlen==constExtLen &&
520 0==fseek(f, constExtToFname, SEEK_CUR) &&
521 4==fread(&fontname, 1, 4, f) &&
522 fontname>constFontnameMin && fontname<constFontnameMax)
523 ok=true;
524 fclose(f);
528 return ok;
531 // This function is *only* used for the generation of AFMs from PFMs.
532 static bool isAType1(const QString &fname)
534 static const char * constStr="%!PS-AdobeFont-";
535 static const unsigned int constStrLen=15;
536 static const unsigned int constPfbOffset=6;
537 static const unsigned int constPfbLen=constStrLen+constPfbOffset;
539 QByteArray name(QFile::encodeName(fname));
540 char buffer[constPfbLen];
541 bool match=false;
543 if(Misc::checkExt(name, "pfa"))
545 FILE *f=fopen(name.constData(), "r");
547 if(f)
549 if(constStrLen==fread(buffer, 1, constStrLen, f))
550 match=0==memcmp(buffer, constStr, constStrLen);
551 fclose(f);
554 else if(Misc::checkExt(name, "pfb"))
556 static const char constPfbMarker=0x80;
558 FILE *f=fopen(name.constData(), "r");
560 if(f)
562 if(constPfbLen==fread(buffer, 1, constPfbLen, f))
563 match=buffer[0]==constPfbMarker && 0==memcmp(&buffer[constPfbOffset], constStr,
564 constStrLen);
565 fclose(f);
569 return match;
572 static QString getMatch(const QString &file, const char *extension)
574 QString f(Misc::changeExt(file, extension));
576 return Misc::fExists(f) ? f : QString();
579 struct KfiFont
581 struct Path
583 Path(const QString &p=QString()) : orig(p) { }
585 QString orig,
586 modified;
588 bool operator==(const Path &p) const { return p.orig==orig; }
591 KfiFont(const QString &n=QString(), const QString &p=QString()) : name(n)
592 { if(!p.isEmpty()) paths.append(Path(p)); }
594 QString name;
595 QList<Path> paths;
597 bool operator==(const KfiFont &f) const { return f.name==name; }
600 struct KfiFontList : public QList<KfiFont>
602 Iterator locate(const KfiFont &t) { int i = indexOf(t); return (-1==i ? end() : (begin()+i)); }
605 static void setName(const QString &orig, QString &modified, int pos, bool hidden)
607 modified=orig.mid(pos);
608 modified=modified.replace('/', '_');
609 if(hidden && '.'!=modified[0])
610 modified='.'+modified;
614 // This function returns a set of maping of from -> to for copy/move operations
615 static bool getFontList(const CDisabledFonts::TFileList &files, QMap<QString, QString> &map)
617 // First of all create a list of font files, and their paths
618 CDisabledFonts::TFileList::ConstIterator it=files.begin(),
619 end=files.end();
620 KfiFontList list;
622 for(;it!=end; ++it)
624 QString name(Misc::getFile(*it)),
625 path(Misc::getDir(*it));
626 KfiFontList::Iterator entry=list.locate(KfiFont(name));
628 if(entry!=list.end())
630 if(!(*entry).paths.contains(path))
631 (*entry).paths.append(path);
633 else
634 list.append(KfiFont(name, path));
637 KfiFontList::Iterator fIt(list.begin()),
638 fEnd(list.end());
640 for(; fIt!=fEnd; ++fIt)
642 bool hidden='.'==(*fIt).name[0];
643 QList<KfiFont::Path>::Iterator pBegin((*fIt).paths.begin()),
644 pIt(++pBegin),
645 pEnd((*fIt).paths.end());
646 --pBegin;
648 if((*fIt).paths.count()>1)
650 // There's more than 1 file with the same name, but in a different location
651 // therefore, take the unique part of the path, and replace / with _
652 // e.g.
653 // /usr/X11R6/lib/X11/fonts/75dpi/times.pcf.gz
654 // /usr/X11R6/lib/X11/fonts/100dpi/times.pcf.gz
656 // Will produce:
657 // 75dpi_times.pcf.gz
658 // 100dpi_times.pcf.gz
659 int beginLen((*pBegin).orig.length());
661 for(; pIt!=pEnd; ++pIt)
663 unsigned int len=qMin((*pIt).orig.length(), beginLen);
664 bool modified=false;
666 for(unsigned int i=0; i<len; ++i)
667 if((*pIt).orig[i]!=(*pBegin).orig[i])
669 setName((*pIt).orig, (*pIt).modified, i, hidden);
670 if((*pBegin).modified.isEmpty())
671 setName((*pBegin).orig, (*pBegin).modified, i, hidden);
672 modified=true;
673 break;
675 if(!modified)
676 if(beginLen>(*pIt).orig.length())
678 if((*pBegin).modified.isEmpty())
679 setName((*pBegin).orig, (*pBegin).modified, (*pIt).orig.length(), hidden);
681 else
682 setName((*pIt).orig, (*pIt).modified, beginLen, hidden);
685 for(pIt=(*fIt).paths.begin(); pIt!=pEnd; ++pIt)
686 if(hidden && '.'==(*pIt).modified[0] && '.'==(*fIt).name[0])
687 map[(*pIt).orig+(*fIt).name]=(*pIt).modified+(*fIt).name.mid(1);
688 else
689 map[(*pIt).orig+(*fIt).name]=(*pIt).modified+(*fIt).name;
692 return list.count() ? true : false;
695 inline QString getDestFolder(const QString &folder, const QString &file)
697 return folder+file[0].toLower()+'/';
700 // Extract just the family from a font name
701 static QString getFamily(const QString &font)
703 int commaPos=font.lastIndexOf(',');
704 return -1==commaPos ? font : font.left(commaPos);
707 inline qulonglong toBit(QFontDatabase::WritingSystem ws)
709 return ((qulonglong)1) << (int)ws;
712 //.........the following section is inspired by qfontdatabase_x11.cpp / loadFontConfig
715 // Unfortunately FontConfig doesn't know about some languages. We have to test these through the
716 // charset. The lists below contain the systems where we need to do this.
717 static const struct
719 QFontDatabase::WritingSystem ws;
720 ushort ch;
721 } sampleCharForWritingSystem[] =
723 { QFontDatabase::Telugu, 0xc15 },
724 { QFontDatabase::Kannada, 0xc95 },
725 { QFontDatabase::Malayalam, 0xd15 },
726 { QFontDatabase::Sinhala, 0xd9a },
727 { QFontDatabase::Myanmar, 0x1000 },
728 { QFontDatabase::Ogham, 0x1681 },
729 { QFontDatabase::Runic, 0x16a0 },
730 { QFontDatabase::Any, 0x0 }
733 static qulonglong getWritingSystems(FcPattern *pat)
735 qulonglong ws(0);
737 FcLangSet *langset(0);
738 const CDisabledFonts::LangWritingSystemMap *langForWritingSystem(CDisabledFonts::languageForWritingSystemMap());
740 if (FcResultMatch==FcPatternGetLangSet(pat, FC_LANG, 0, &langset))
742 for (int i = 0; langForWritingSystem[i].lang; ++i)
743 if (FcLangDifferentLang!=FcLangSetHasLang(langset, langForWritingSystem[i].lang))
744 ws|=toBit(langForWritingSystem[i].ws);
746 else
747 ws|=toBit(QFontDatabase::Other);
749 FcCharSet *cs(0);
751 if (FcResultMatch == FcPatternGetCharSet(pat, FC_CHARSET, 0, &cs))
753 // some languages are not supported by FontConfig, we rather check the
754 // charset to detect these
755 for (int i = 0; QFontDatabase::Any!=sampleCharForWritingSystem[i].ws; ++i)
756 if (FcCharSetHasChar(cs, sampleCharForWritingSystem[i].ch))
757 ws|=toBit(sampleCharForWritingSystem[i].ws);
760 return ws;
763 static QString obtainMimeType(const QString &file)
765 if(Misc::checkExt(file, "ttf") || Misc::checkExt(file, "ttc"))
766 return "application/x-font-ttf";
767 if(Misc::checkExt(file, "otf"))
768 return "application/x-font-otf";
769 if(Misc::checkExt(file, "pfa") || Misc::checkExt(file, "pfb"))
770 return "application/x-font-type1";
771 if(Misc::checkExt(file, "pcf.gz") || Misc::checkExt(file, "pcf"))
772 return "application/x-font-pcf";
773 if(Misc::checkExt(file, "bdf.gz") || Misc::checkExt(file, "bdf"))
774 return "application/x-font-bdf";
776 // File extension check failed, use kmimetype to read contents...
777 return KMimeType::findByPath(file)->name();
780 //.........
782 CKioFonts::CKioFonts(const QByteArray &pool, const QByteArray &app)
783 : KIO::SlaveBase(KFI_KIO_FONTS_PROTOCOL, pool, app),
784 itsRoot(Misc::root()),
785 itsAddToSysFc(false),
786 itsLastFcCheckTime(0),
787 itsFontList(NULL),
788 itsSocket(NULL),
789 itsSuProc(NULL)
791 KFI_DBUG;
793 slaveInstance=this;
795 if(!itsRoot)
797 // Set core dump size to 0 because we will have
798 // root's password in memory.
799 struct rlimit rlim;
800 rlim.rlim_cur=rlim.rlim_max=0;
801 setrlimit(RLIMIT_CORE, &rlim);
804 // Check with fontconfig for folder locations...
806 // 1. Get list of fontconfig dirs
807 // 2. For user, look for any starting with $HOME - but prefer $HOME/.fonts
808 // 3. For system, look for any starting with /usr/local/share - but prefer /usr/local/share/fonts
809 // 4. If either are not found, then add to local.conf / .fonts.conf
811 FcStrList *list=FcConfigGetFontDirs(FcInitLoadConfigAndFonts());
812 QStringList dirs;
813 FcChar8 *dir;
815 while((dir=FcStrListNext(list)))
816 dirs.append(Misc::dirSyntax((const char *)dir));
818 EFolder mainFolder=FOLDER_SYS;
820 if(!itsRoot)
822 QString home(Misc::dirSyntax(QDir::homePath())),
823 defaultDir(Misc::dirSyntax(QDir::homePath()+"/.fonts/")),
824 dir(Misc::getFolder(defaultDir, home, dirs));
826 if(dir.isEmpty()) // Then no $HOME/ was found in fontconfigs dirs!
828 KXftConfig xft(KXftConfig::Dirs, false);
829 xft.addDir(defaultDir);
830 xft.apply();
831 dir=defaultDir;
833 mainFolder=FOLDER_USER;
834 itsFolders[FOLDER_USER].setLocation(dir, false);
837 QString sysDefault("/usr/local/share/fonts/"),
838 sysDir(Misc::getFolder(sysDefault, "/usr/local/share/", dirs));
840 if(sysDir.isEmpty())
842 if(itsRoot)
844 KXftConfig xft(KXftConfig::Dirs, true);
845 xft.addDir(sysDefault);
846 xft.apply();
848 else
849 itsAddToSysFc=true;
851 sysDir=sysDefault;
854 itsFolders[FOLDER_SYS].setLocation(sysDir, true);
857 // Ensure exists
858 if(!Misc::dExists(itsFolders[mainFolder].location))
859 Misc::createDir(itsFolders[mainFolder].location);
861 updateFontList();
864 void CKioFonts::cleanup()
866 slaveInstance=NULL;
868 KFI_DBUG;
869 itsFolders[FOLDER_USER].disabled->save();
870 doModified();
871 quitHelper();
873 delete itsSuProc;
874 delete itsSocket;
877 void CKioFonts::listDir(const KUrl &url)
879 KFI_DBUG << url.path() << " query:" << url.query();
881 clearFontList(); // Always refresh font list when listing...
882 if(updateFontList() && checkUrl(url, true))
884 KIO::UDSEntry entry;
885 int size=0,
886 sections=url.path().split('/', QString::SkipEmptyParts).count();
887 if(itsRoot || sections!=0)
889 if(!itsRoot && isAllFolder(getSect(url.path())))
891 totalSize(itsFolders[FOLDER_SYS].fontMap.count()+itsFolders[FOLDER_SYS].disabled->items().count()+
892 itsFolders[FOLDER_USER].fontMap.count()+itsFolders[FOLDER_USER].disabled->items().count());
893 listDir(FOLDER_SYS, entry);
894 listDir(FOLDER_USER, entry);
896 else
898 EFolder folder=getFolder(url);
900 totalSize(itsFolders[folder].fontMap.count()+itsFolders[folder].disabled->items().count());
901 listDir(folder, entry);
904 else
906 size=2;
907 totalSize(size);
908 createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_USER), itsFolders[FOLDER_USER].location,
909 false);
910 listEntry(entry, false);
911 createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_SYS), itsFolders[FOLDER_SYS].location,
912 true);
913 listEntry(entry, false);
916 listEntry(size ? entry : KIO::UDSEntry(), true);
917 finished();
920 KFI_DBUG << "finished!";
923 void CKioFonts::listDir(EFolder folder, KIO::UDSEntry &entry)
925 if(itsFolders[folder].fontMap.count())
927 TFontMap::Iterator it=itsFolders[folder].fontMap.begin(),
928 end=itsFolders[folder].fontMap.end();
930 for ( ; it != end; ++it)
932 entry.clear();
933 if(createFontUDSEntry(entry, it.key(), it.value().files, it.value().styleVal,
934 it.value().writingSystems, FOLDER_SYS==folder))
935 listEntry(entry, false);
939 CDisabledFonts::TFontList::Iterator dIt(itsFolders[folder].disabled->items().begin()),
940 dEnd(itsFolders[folder].disabled->items().end());
942 for(; dIt!=dEnd; ++dIt)
943 if(createFontUDSEntry(entry, (*dIt).getName(), (*dIt).files,
944 (*dIt).styleInfo, (*dIt).writingSystems, FOLDER_SYS==folder, true))
945 listEntry(entry, false);
948 void CKioFonts::stat(const KUrl &url)
950 KFI_DBUG << url.prettyUrl() << " query:" << url.query();
952 KIO::UDSEntry entry;
953 bool err=false;
955 if(checkUrl(url, true, false))
957 QString path(url.path(KUrl::RemoveTrailingSlash));
959 if(path.isEmpty())
961 error(KIO::ERR_COULD_NOT_STAT, url.prettyUrl());
962 return;
965 QStringList pathList(path.split('/', QString::SkipEmptyParts));
967 switch(pathList.count())
969 case 0:
970 err=!createFolderUDSEntry(entry, i18n("Fonts"),
971 itsFolders[itsRoot ? FOLDER_SYS : FOLDER_USER].location,
972 false);
973 break;
974 case 1:
975 if(itsRoot)
976 err=!createStatEntry(entry, url, FOLDER_SYS);
977 else
978 if(isUserFolder(pathList[0]))
979 err=!createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_USER),
980 itsFolders[FOLDER_USER].location, false);
981 else if(isSysFolder(pathList[0]))
982 err=!createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_SYS),
983 itsFolders[FOLDER_SYS].location, true);
984 else if(isAllFolder(pathList[0]))
985 err=!createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_ALL),
986 itsFolders[FOLDER_SYS].location, true);
987 else
989 error(KIO::ERR_SLAVE_DEFINED,
990 i18n("Please specify \"%1\" or \"%2\".",
991 i18n(KFI_KIO_FONTS_USER), i18n(KFI_KIO_FONTS_SYS)));
992 return;
994 break;
995 default:
996 err=!createStatEntry(entry, url, getFolder(url));
999 else if(!itsRoot && 1==url.path(KUrl::RemoveTrailingSlash)
1000 .split('/', QString::SkipEmptyParts).count())
1003 // If a user (non-root) copies a font to fonts:/, kio_fonts will redirect to fonts:/Personal
1004 // If the font already exists in fonts:/Personal, konqueror will then do a stat on
1005 // fonts:/<filename> to get its file size in the "overwrite" dialog.
1008 // But, the font will not exist in fonts:/<filename> - so we need to see if it exists
1009 // in fonts:/Personal/<filename> in order to get the correct size. Otherwise konqueror, etc,
1010 // display 0 bytes!
1012 KUrl modUrl(url);
1014 modUrl.setPath(QChar('/')+i18n(KFI_KIO_FONTS_USER)+QChar('/')+url.fileName());
1015 err=!createStatEntry(entry, modUrl, FOLDER_USER);
1017 else
1019 error(KIO::ERR_SLAVE_DEFINED, i18n("Please specify \"%1\" or \"%2\".",
1020 i18n(KFI_KIO_FONTS_USER), i18n(KFI_KIO_FONTS_SYS)));
1021 return;
1024 if(err)
1026 error(KIO::ERR_DOES_NOT_EXIST, url.prettyUrl());
1027 return;
1030 statEntry(entry);
1031 finished();
1034 bool CKioFonts::createStatEntry(KIO::UDSEntry &entry, const KUrl &url, EFolder folder)
1036 KFI_DBUG << url.path();
1038 // First try to create stat entry without refreshing lists...
1039 bool ok=createStatEntryReal(entry, url, folder) && getSize(entry)>0;
1041 // Hmm... well that failed, so refresh lists and try again!
1042 if(!ok)
1044 KFI_DBUG << "refresh font list";
1045 entry.clear();
1046 clearFontList();
1047 updateFontList();
1048 ok=createStatEntryReal(entry, url, folder) && getSize(entry)>0;
1051 // Perhaps its not a valid font? Try to stat on location+name
1052 if(!ok)
1054 KFI_DBUG << "could not find";
1056 QStringList folders;
1058 folders.append(itsFolders[folder].location);
1059 folders.append(getDestFolder(itsFolders[folder].location, url.fileName()));
1061 QStringList::Iterator it(folders.begin()),
1062 end(folders.end());
1064 for(; it!=end; ++it)
1065 for(int t=0; t<3 && !ok; ++t)
1067 CDisabledFonts::TFileList files;
1068 QString fileName=0==t
1069 ? url.fileName()
1070 : 1==t ? modifyName(url.fileName()) // lowercase
1071 : modifyName(url.fileName(), true); // uppercase
1073 files.append(CDisabledFonts::TFile((*it)+fileName));
1074 entry.clear();
1075 ok=createFontUDSEntry(entry, i18n("Invalid Font"), files,
1076 KFI_NO_STYLE_INFO, 0, FOLDER_SYS==folder,
1077 Misc::isHidden(url)) && getSize(entry)>0;
1081 if(!ok)
1082 entry.clear();
1084 return ok;
1087 bool CKioFonts::createStatEntryReal(KIO::UDSEntry &entry, const KUrl &url, EFolder folder)
1089 KFI_DBUG << url.path();
1091 TFontMap::Iterator it=getMap(url);
1093 if(it!=itsFolders[folder].fontMap.end())
1095 KFI_DBUG << "its a normal font";
1096 return createFontUDSEntry(entry, it.key(), it.value().files, it.value().styleVal,
1097 it.value().writingSystems, FOLDER_SYS==folder);
1100 KFI_DBUG << "try looking in disabled fonts";
1102 QString name=Misc::getFile(removeMultipleExtension(url));
1103 CDisabledFonts::TFontList::Iterator dIt=itsFolders[folder].disabled->find(name,
1104 Misc::getIntQueryVal(url, KFI_KIO_FACE, 0));
1106 if(dIt!=itsFolders[folder].disabled->items().end())
1108 KFI_DBUG << "its a disabled font";
1109 return createFontUDSEntry(entry, (*dIt).getName(), (*dIt).files, (*dIt).styleInfo,
1110 (*dIt).writingSystems, FOLDER_SYS==folder, true);
1113 return false;
1116 void CKioFonts::get(const KUrl &url)
1118 KFI_DBUG << url.path() << " query:" << url.query();
1120 bool thumb="1"==metaData("thumbnail");
1121 CDisabledFonts::TFileList srcFiles;
1123 // Any error will be logged in getSourceFiles
1124 if(updateFontList() && checkUrl(url) && getSourceFiles(url, srcFiles))
1127 // The thumbnail job always donwloads non-local files to /tmp/... and passes this file name to
1128 // the thumbnail creator. However, in the case of fonts which are split among many files, this
1129 // wont work. Therefore, when the thumbnail code asks for the font to donwload, just return
1130 // the family and style info for enabled fonts, and the filename for disabled fonts. This way
1131 // the font-thumbnail creator can read this and just ask Xft/fontconfig for the font data.
1132 if(thumb)
1134 QByteArray array;
1135 QTextStream stream(&array, QIODevice::WriteOnly);
1136 EFolder folder(getFolder(url));
1137 TFontMap::Iterator it(getMap(url)),
1138 end(itsFolders[folder].fontMap.end());
1140 emit mimeType("text/plain");
1142 if(it==end)
1145 // OK, its a disabled font - if possible try to return the location of the font file
1146 // itself.
1147 QString name(Misc::getFile(removeMultipleExtension(url)));
1148 CDisabledFonts::TFontList::Iterator dIt(itsFolders[folder].disabled
1149 ->find(name, Misc::getIntQueryVal(url,
1150 KFI_KIO_FACE, 0))),
1151 dEnd(itsFolders[folder].disabled->items().end());
1152 bool found=false;
1154 if(dIt!=dEnd)
1156 CDisabledFonts::TFileList::ConstIterator fIt((*dIt).files.begin()),
1157 fEnd((*dIt).files.end());
1159 for(; fIt!=fEnd; ++fIt)
1160 if(isScalable((*fIt).path))
1162 KFI_DBUG << "hasMetaData(\"thumbnail\"), so return FILE: "
1163 << (*fIt).path << " / " << (*fIt).face;
1164 stream << KFI_PATH_KEY << (*fIt).path << endl
1165 << KFI_FACE_KEY << (*fIt).face << endl;
1166 found=true;
1167 break;
1171 if(!found)
1173 KFI_DBUG << "hasMetaData(\"thumbnail\"), so return Url: " << url;
1174 stream << url.prettyUrl();
1177 else
1179 KFI_DBUG << "hasMetaData(\"thumbnail\"), so return DETAILS: " << it.key() << " / "
1180 << (*it).styleVal;
1182 stream << KFI_NAME_KEY << it.key() << endl
1183 << KFI_STYLE_KEY << (*it).styleVal << endl;
1185 if(1==(*it).files.count())
1187 CDisabledFonts::TFileList::ConstIterator fIt((*it).files.begin());
1189 stream << KFI_PATH_KEY << (*fIt).path << endl
1190 << KFI_FACE_KEY << (*fIt).face << endl;
1194 totalSize(array.size());
1195 data(array);
1196 processedSize(array.size());
1197 data(QByteArray());
1198 processedSize(array.size());
1199 finished();
1200 KFI_DBUG << "Finished thumbnail...";
1201 return;
1204 QString realPath;
1205 KDE_struct_stat buff;
1206 bool multiple=false;
1208 if(1==srcFiles.count())
1209 realPath=srcFiles.first().path;
1210 else // Font is made up of multiple files - so create .zip of them all!
1212 KTemporaryFile tmpFile;
1214 if(tmpFile.open())
1216 KZip zip(tmpFile.fileName());
1218 tmpFile.setAutoRemove(false);
1219 realPath=tmpFile.fileName();
1221 if(zip.open(QIODevice::WriteOnly))
1223 QMap<QString, QString> map;
1225 getFontList(srcFiles, map);
1227 QMap<QString, QString>::Iterator fIt(map.begin()),
1228 fEnd(map.end());
1231 // Iterate through created list, and add to zip archive
1232 // ...unhide any hidden files
1233 for(; fIt!=fEnd; ++fIt)
1234 zip.addLocalFile(fIt.key(), Misc::unhide(fIt.value()));
1236 multiple=true;
1237 zip.close();
1242 QByteArray realPathC(QFile::encodeName(realPath));
1243 KFI_DBUG << "real: " << realPathC;
1245 if (-2==KDE_stat(realPathC.constData(), &buff))
1246 error(EACCES==errno ? KIO::ERR_ACCESS_DENIED : KIO::ERR_DOES_NOT_EXIST, url.prettyUrl());
1247 else if (S_ISDIR(buff.st_mode))
1248 error(KIO::ERR_IS_DIRECTORY, url.prettyUrl());
1249 else if (!S_ISREG(buff.st_mode))
1250 error(KIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyUrl());
1251 else
1253 int fd = KDE_open(realPathC.constData(), O_RDONLY);
1255 if (fd < 0)
1256 error(KIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyUrl());
1257 else
1259 // Determine the mimetype of the file to be retrieved, and emit it.
1260 // This is mandatory in all slaves (for KRun/BrowserRun to work).
1261 emit mimeType(KMimeType::findByPath(realPathC, buff.st_mode)->name());
1263 totalSize(buff.st_size);
1265 KIO::filesize_t processed=0;
1266 char buffer[MAX_IPC_SIZE];
1267 QByteArray array;
1269 while(1)
1271 int n=::read(fd, buffer, MAX_IPC_SIZE);
1273 if (-1==n)
1275 if (EINTR==errno)
1276 continue;
1278 error(KIO::ERR_COULD_NOT_READ, url.prettyUrl());
1279 ::close(fd);
1280 if(multiple)
1281 ::unlink(realPathC);
1282 return;
1284 if (0==n)
1285 break; // Finished
1287 array=array.fromRawData(buffer, n);
1288 data(array);
1289 array.clear();
1291 processed+=n;
1292 processedSize(processed);
1295 data(QByteArray());
1296 ::close(fd);
1297 processedSize(buff.st_size);
1298 finished();
1301 if(multiple)
1302 ::unlink(realPathC);
1306 void CKioFonts::put(const KUrl &u, int mode, KIO::JobFlags flags)
1308 KFI_DBUG << u.path() << " query:" << u.query();
1310 if(Misc::isHidden(u))
1312 error(KIO::ERR_SLAVE_DEFINED, i18n("Cannot install %1\nHidden fonts cannot be "
1313 "installed.", u.prettyUrl()));
1314 return;
1317 // updateFontList(); // CPD: don't update font list upon a put - it's too slow. Just stat on
1318 // // filename!
1319 //checkUrl(u) // CPD: Don't need to check Url, as the call to "confirmUrl()" below will sort out
1320 // // any probs!
1322 KUrl url(u);
1324 correctUrl(url);
1326 bool nrs(nonRootSys(url)),
1327 clearList(!hasMetaData(KFI_KIO_NO_CLEAR));
1328 EFolder destFolder(getFolder(url));
1329 QString destFolderReal(getDestFolder(itsFolders[destFolder].location, url.fileName())),
1330 dest(destFolderReal+modifyName(url.fileName()));
1331 QByteArray destC(QFile::encodeName(dest));
1332 KDE_struct_stat buffDest;
1333 bool destExists(KDE_lstat(destC.constData(), &buffDest)!= -1);
1335 if (destExists && !(flags & KIO::Overwrite) && !(flags & KIO::Resume))
1337 error(KIO::ERR_FILE_ALREADY_EXIST, url.prettyUrl());
1338 return;
1341 if(nrs && !getRootPasswd()) // Need to check can get root passwd before start download...
1343 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
1344 return;
1348 // As we don't get passed a mime-type the following needs to happen:
1350 // 1. Download to a temporary file
1351 // 2. Check with FreeType that the file is a font, or that it is
1352 // an AFM or PFM file
1353 KTemporaryFile tmpFile;
1354 QByteArray tmpFileC(QFile::encodeName(tmpFile.fileName()));
1356 tmpFile.setAutoRemove(true);
1358 if(putReal(tmpFile.fileName(), tmpFileC, destExists, mode, flags))
1360 EFileType type(checkFile(tmpFile.fileName(), u)); // error logged in checkFile
1362 if(FILE_UNKNOWN==type)
1363 return;
1365 int timeout(reconfigTimeout());
1367 if(nrs) // Ask root to move the tmp font...
1369 QList<TCommand> cmd;
1370 TCommand c(KFI::CMD_MOVE_FILE);
1372 addCreateFolderCmd(itsFolders[FOLDER_SYS].location, cmd);
1373 if(destFolderReal!=itsFolders[FOLDER_SYS].location)
1374 addCreateFolderCmd(destFolderReal, cmd);
1376 c.args.append(tmpFile.fileName());
1377 c.args.append(dest);
1378 c.args.append(0);
1379 c.args.append(0);
1381 cmd.append(c);
1383 // Get root to move this to fonts folder...
1384 if(doRootCmd(cmd, false)) // Already asked for passwd...
1386 tmpFile.setAutoRemove(false);
1387 if(FILE_FONT==type)
1388 modified(timeout, FOLDER_SYS, clearList, destFolderReal);
1389 createAfm(dest, true);
1391 else
1393 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
1394 return;
1397 else // Move it to our font folder...
1399 tmpFile.setAutoRemove(false);
1400 if(!Misc::dExists(destFolderReal))
1401 Misc::createDir(destFolderReal);
1402 if(0==::rename(tmpFileC.constData(), destC.constData()))
1404 Misc::setFilePerms(destC);
1405 if(FILE_FONT==type)
1406 modified(timeout, FOLDER_USER, clearList, destFolderReal);
1407 createAfm(dest);
1409 else
1411 error(KIO::ERR_SLAVE_DEFINED, i18n("Could not access \"%1\" folder.",
1412 i18n(KFI_KIO_FONTS_USER)));
1413 return;
1417 finished();
1421 QString CKioFonts::getUserName(uid_t uid)
1423 if (!itsUserCache.contains(uid))
1425 struct passwd *user = getpwuid(uid);
1426 if(user)
1427 itsUserCache.insert(uid, QString::fromLatin1(user->pw_name));
1428 else
1429 return QString::number(uid);
1431 return itsUserCache[uid];
1434 QString CKioFonts::getGroupName(gid_t gid)
1436 if (!itsGroupCache.contains(gid))
1438 struct group *grp = getgrgid(gid);
1439 if(grp)
1440 itsGroupCache.insert(gid, QString::fromLatin1(grp->gr_name));
1441 else
1442 return QString::number(gid);
1444 return itsGroupCache[gid];
1447 bool CKioFonts::createFontUDSEntry(KIO::UDSEntry &entry, const QString &name,
1448 const CDisabledFonts::TFileList &patterns,
1449 quint32 styleVal, qulonglong writingSystems,
1450 bool sys, bool hidden)
1452 //KFI_DBUG << "name:" << name << " style:" << styleVal << " #"
1453 // << patterns.count();
1456 // First of all get list of real files - i.e. remove any duplicates due to symlinks
1457 CDisabledFonts::TFileList files;
1459 getFontFiles(patterns, files);
1461 CDisabledFonts::TFileList::ConstIterator it(files.begin()),
1462 end(files.end());
1464 if(files.count()>1)
1467 // Sort list of files - placing scalable ones first. This is because, when determening the
1468 // mimetype, the 1st valid file will be chosen. In case of mixed bitmap/scalable - prefer
1469 // scalable
1470 CDisabledFonts::TFileList sorted;
1472 for(; it!=end; ++it)
1473 if(isScalable(*it))
1474 sorted.prepend(*it);
1475 else
1476 sorted.append(*it);
1477 files=sorted;
1478 it=files.begin();
1479 end=files.end();
1482 entry.clear();
1483 entry.insert(KIO::UDSEntry::UDS_SIZE, getSize(files));
1485 for(; it!=end; ++it)
1487 QByteArray cPath(QFile::encodeName(*it));
1488 KDE_struct_stat buff;
1490 if(-1!=KDE_lstat(cPath, &buff))
1492 if(0==writingSystems)
1493 writingSystems=toBit(QFontDatabase::Other);
1495 entry.insert(KIO::UDSEntry::UDS_NAME, name);
1496 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, buff.st_mode&S_IFMT);
1497 entry.insert(KIO::UDSEntry::UDS_ACCESS, buff.st_mode&07777);
1498 entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.st_mtime);
1499 entry.insert(KIO::UDSEntry::UDS_USER, getUserName(buff.st_uid));
1500 entry.insert(KIO::UDSEntry::UDS_GROUP, getGroupName(buff.st_gid));
1501 entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, buff.st_atime);
1502 entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, obtainMimeType(*it));
1503 entry.insert(UDS_EXTRA_FC_STYLE, styleVal);
1504 entry.insert(UDS_EXTRA_WRITING_SYSTEMS, writingSystems);
1506 if(hidden)
1507 entry.insert(KIO::UDSEntry::UDS_HIDDEN, 1);
1509 entry.insert(UDS_EXTRA_FILE_NAME, (*it).path);
1510 entry.insert(UDS_EXTRA_FOUNDRY, (*it).foundry);
1511 if(files.count()>1)
1512 entry.insert(UDS_EXTRA_FILE_LIST, files.toString(true));
1514 QString url(KFI_KIO_FONTS_PROTOCOL+QString::fromLatin1(":/"));
1516 if(!Misc::root())
1518 url+=sys ? i18n(KFI_KIO_FONTS_SYS) : i18n(KFI_KIO_FONTS_USER);
1519 url+=QString::fromLatin1("/");
1522 #ifdef KFI_KIO_ALL_URLS_HAVE_NAME
1523 addKnownExtension(url, files, name, hidden);
1524 #else
1525 if(files.count()>1)
1527 if(hidden)
1528 url+='.';
1529 url+=name+QString::fromLatin1(KFI_FONTS_PACKAGE);
1531 else
1532 url+=Misc::getFile(*it);
1533 #endif
1535 if(files.count()==1 && (*it).face>0)
1537 KUrl kUrl(url);
1539 kUrl.setQuery("?"KFI_KIO_FACE"="+QString::number((*it).face));
1540 entry.insert(KIO::UDSEntry::UDS_URL, kUrl.url());
1542 else
1543 entry.insert(KIO::UDSEntry::UDS_URL, url);
1545 return true; // This file was OK, so use its values...
1548 return false;
1551 bool CKioFonts::createFolderUDSEntry(KIO::UDSEntry &entry, const QString &name,
1552 const QString &path, bool sys)
1554 KFI_DBUG << "name:" << name << " path:" << path << " sys?:" << sys;
1556 KDE_struct_stat buff;
1557 QByteArray cPath(QFile::encodeName(path));
1559 entry.clear();
1561 if(-1!=KDE_lstat(cPath, &buff))
1563 entry.insert(KIO::UDSEntry::UDS_NAME, name);
1565 if (S_ISLNK(buff.st_mode))
1567 KFI_DBUG << path << " is a link";
1569 char buffer2[1000];
1570 int n=readlink(cPath, buffer2, 1000);
1571 if(n!= -1)
1572 buffer2[n]='\0';
1574 if(-1==KDE_stat(cPath, &buff))
1576 // It is a link pointing to nowhere
1577 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFMT - 1);
1578 entry.insert(KIO::UDSEntry::UDS_ACCESS, S_IRWXU | S_IRWXG | S_IRWXO);
1579 entry.insert(KIO::UDSEntry::UDS_SIZE, 0);
1580 goto notype;
1582 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); // CPD Treat links as regular folder...
1584 else
1585 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, buff.st_mode&S_IFMT);
1586 entry.insert(KIO::UDSEntry::UDS_ACCESS, buff.st_mode&07777);
1587 entry.insert(KIO::UDSEntry::UDS_SIZE, buff.st_size);
1589 notype:
1590 entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, buff.st_mtime);
1591 entry.insert(KIO::UDSEntry::UDS_USER, getUserName(buff.st_uid));
1592 entry.insert(KIO::UDSEntry::UDS_GROUP, getGroupName(buff.st_gid));
1593 entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, buff.st_atime);
1594 entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/directory"));
1595 return true;
1597 else if (sys && !Misc::root()) // Default system fonts folder does not actually exist yet!
1599 KFI_DBUG << "Default system folder (" << path
1600 << ") does not yet exist, so create dummy entry";
1601 entry.insert(KIO::UDSEntry::UDS_NAME, name);
1602 entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
1603 entry.insert(KIO::UDSEntry::UDS_ACCESS, 0744);
1604 entry.insert(KIO::UDSEntry::UDS_USER, QString::fromLatin1("root"));
1605 entry.insert(KIO::UDSEntry::UDS_GROUP, QString::fromLatin1("root"));
1606 entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/directory"));
1607 return true;
1610 return false;
1613 bool CKioFonts::putReal(const QString &destOrig, const QByteArray &destOrigC, bool origExists,
1614 int mode, KIO::JobFlags flags)
1616 bool markPartial=config()->readEntry("MarkPartial", true);
1617 QString dest;
1619 if (markPartial)
1621 QString destPart(destOrig+QString::fromLatin1(".part"));
1622 QByteArray destPartC(QFile::encodeName(destPart));
1624 dest = destPart;
1626 KDE_struct_stat buffPart;
1627 bool partExists=(-1!=KDE_stat(destPartC.constData(), &buffPart));
1629 if (partExists && !(flags & KIO::Resume) && buffPart.st_size>0)
1631 // Maybe we can use this partial file for resuming
1632 // Tell about the size we have, and the app will tell us
1633 // if it's ok to resume or not.
1634 flags |=canResume(buffPart.st_size) ? KIO::Resume : KIO::DefaultFlags;
1636 if (!(flags & KIO::Resume))
1637 if (!::remove(destPartC.constData()))
1638 partExists = false;
1639 else
1641 error(KIO::ERR_CANNOT_DELETE_PARTIAL, destPart);
1642 return false;
1646 else
1648 dest = destOrig;
1649 if (origExists && !(flags & KIO::Resume))
1650 ::remove(destOrigC.constData());
1651 // Catch errors when we try to open the file.
1654 QByteArray destC(QFile::encodeName(dest));
1656 int fd;
1658 if (flags & KIO::Resume)
1660 fd = KDE_open(destC.constData(), O_RDWR); // append if resuming
1661 KDE_lseek(fd, 0, SEEK_END); // Seek to end
1663 else
1665 // WABA: Make sure that we keep writing permissions ourselves,
1666 // otherwise we can be in for a surprise on NFS.
1667 fd = KDE_open(destC.constData(), O_CREAT | O_TRUNC | O_WRONLY,
1668 -1==mode ? 0666: mode | S_IWUSR | S_IRUSR);
1671 if (fd < 0)
1673 error(EACCES==errno ? KIO::ERR_WRITE_ACCESS_DENIED : KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest);
1674 return false;
1677 int result;
1678 // Loop until we got 0 (end of data)
1681 QByteArray buffer;
1683 dataReq(); // Request for data
1684 result = readData(buffer);
1685 if(result > 0 && !writeAll(fd, buffer.constData(), buffer.size()))
1687 if(ENOSPC==errno) // disk full
1689 error(KIO::ERR_DISK_FULL, destOrig);
1690 result = -2; // means: remove dest file
1692 else
1694 error(KIO::ERR_COULD_NOT_WRITE, destOrig);
1695 result = -1;
1699 while(result>0);
1701 if (result<0)
1703 ::close(fd);
1704 if (-1==result)
1705 ::remove(destC.constData());
1706 else if (markPartial)
1708 KDE_struct_stat buff;
1710 if ((-1==KDE_stat(destC.constData(), &buff)) ||
1711 (buff.st_size<config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE)))
1712 ::remove(destC.constData());
1714 ::exit(255);
1717 if (-1==fd) // we got nothing to write out, so we never opened the file
1719 finished();
1720 return false;
1723 if (::close(fd))
1725 error(KIO::ERR_COULD_NOT_WRITE, destOrig);
1726 return false;
1729 // after full download rename the file back to original name
1730 if (markPartial && ::rename(destC.constData(), destOrigC.constData()))
1732 error(KIO::ERR_CANNOT_RENAME_PARTIAL, destOrig);
1733 return false;
1736 return true;
1739 void CKioFonts::copy(const KUrl &src, const KUrl &d, int mode, KIO::JobFlags flags)
1742 // Support:
1743 // Copying to fonts:/
1744 // Copying from fonts:/ and file:/
1746 KFI_DBUG << src.prettyUrl() << " query:" << src.query() << " - "
1747 << d.prettyUrl() << " query:" << d.query();
1749 if(Misc::isHidden(d))
1751 error(KIO::ERR_SLAVE_DEFINED,
1752 i18n("Cannot copy %1 to %2\nHidden/disabled fonts cannot be installed.",
1753 src.prettyUrl(), d.prettyUrl()));
1754 return;
1757 bool fromFonts=KFI_KIO_FONTS_PROTOCOL==src.protocol();
1759 // CPD: don't update font list upon a copy from file - it's too slow. Just stat on filename!
1760 if(!fromFonts || updateFontList() && checkUrl(src) && checkAllowed(src))
1762 //checkUrl(u) // CPD as per comment in ::put()
1764 CDisabledFonts::TFileList srcFiles;
1765 int timeout(reconfigTimeout());
1767 if(getSourceFiles(src, srcFiles)) // Any error will be logged in getSourceFiles
1769 KUrl dest(d);
1771 correctUrl(dest);
1773 bool metrics(fromFonts ? false : Misc::isMetrics(src.fileName())),
1774 clearList(!hasMetaData(KFI_KIO_NO_CLEAR));
1775 EFolder destFolder(getFolder(dest));
1776 QMap<QString, QString> map;
1778 if(!fromFonts)
1779 map[src.path()]=src.fileName();
1781 // As above, if copying from file, then only stat on dest filename, but if from fonts to
1782 // fonts need to get the list of possible source files, etc.
1783 if(fromFonts ? confirmMultiple(src, srcFiles,
1784 FOLDER_SYS==destFolder ? FOLDER_USER : FOLDER_SYS, OP_COPY) &&
1785 getFontList(srcFiles, map) &&
1786 checkDestFiles(src, map, dest, destFolder, flags)
1787 : checkDestFile(src, dest, destFolder, flags) )
1789 if(nonRootSys(dest))
1791 QList<TCommand> cmd;
1792 int size=0;
1793 CDirList addedFolders;
1794 QMap<QString, QString>::Iterator fIt(map.begin()),
1795 fEnd(map.end());
1797 for(; fIt!=fEnd; ++fIt)
1799 TCommand c(KFI::CMD_COPY_FILE);
1800 QString destFolderReal(getDestFolder(itsFolders[destFolder].location,
1801 Misc::getFile(*fIt)));
1803 if(!addedFolders.contains(itsFolders[FOLDER_SYS].location) &&
1804 addCreateFolderCmd(itsFolders[FOLDER_SYS].location, cmd))
1805 addedFolders.add(itsFolders[FOLDER_SYS].location);
1807 if(!addedFolders.contains(destFolderReal) &&
1808 addCreateFolderCmd(destFolderReal, cmd))
1809 addedFolders.add(destFolderReal);
1811 c.args.append(fIt.key());
1812 c.args.append(destFolderReal+modifyName(fIt.value()));
1813 cmd.append(c);
1815 int s=getSize(QFile::encodeName(fIt.key()));
1816 if(s>0)
1817 size+=s;
1820 totalSize(size);
1822 if(doRootCmd(cmd, true))
1824 if(!metrics)
1825 modified(timeout, destFolder, clearList, addedFolders);
1826 processedSize(size);
1827 if(src.isLocalFile() && 1==srcFiles.count())
1828 createAfm(itsFolders[destFolder].location+modifyName(map.begin().value()), true);
1830 else
1832 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
1833 return;
1836 else
1838 QMap<QString, QString>::Iterator fIt(map.begin()),
1839 fEnd(map.end());
1840 QString destFolderReal;
1842 for(; fIt!=fEnd; ++fIt)
1844 destFolderReal=getDestFolder(itsFolders[destFolder].location,
1845 Misc::getFile(*fIt));
1847 QByteArray realSrc(QFile::encodeName(fIt.key())),
1848 realDest(QFile::encodeName(destFolderReal+
1849 modifyName(fIt.value())));
1850 KDE_struct_stat buffSrc;
1852 if(-1==KDE_stat(realSrc.constData(), &buffSrc))
1854 error(EACCES==errno ? KIO::ERR_ACCESS_DENIED : KIO::ERR_DOES_NOT_EXIST,
1855 src.prettyUrl());
1856 return;
1859 int srcFd=KDE_open(realSrc.constData(), O_RDONLY);
1861 if (srcFd<0)
1863 error(KIO::ERR_CANNOT_OPEN_FOR_READING, src.prettyUrl());
1864 return;
1867 if(!Misc::dExists(destFolderReal))
1868 Misc::createDir(destFolderReal);
1870 // WABA: Make sure that we keep writing permissions ourselves,
1871 // otherwise we can be in for a surprise on NFS.
1872 int destFd=KDE_open(realDest.constData(), O_CREAT | O_TRUNC | O_WRONLY,
1873 -1==mode ? 0666 : mode | S_IWUSR);
1875 if (destFd<0)
1877 error(EACCES==errno
1878 ? KIO::ERR_WRITE_ACCESS_DENIED
1879 : KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest.prettyUrl());
1880 ::close(srcFd);
1881 return;
1884 totalSize(buffSrc.st_size);
1886 KIO::filesize_t processed = 0;
1887 char buffer[MAX_IPC_SIZE];
1888 QByteArray array;
1890 while(1)
1892 int n=::read(srcFd, buffer, MAX_IPC_SIZE);
1894 if(-1==n)
1896 error(KIO::ERR_COULD_NOT_READ, src.prettyUrl());
1897 ::close(srcFd);
1898 ::close(destFd);
1899 return;
1901 if(0==n)
1902 break; // Finished
1904 if(!writeAll(destFd, buffer, n))
1906 ::close(srcFd);
1907 ::close(destFd);
1908 if (ENOSPC==errno) // disk full
1910 error(KIO::ERR_DISK_FULL, dest.prettyUrl());
1911 remove(realDest.constData());
1913 else
1914 error(KIO::ERR_COULD_NOT_WRITE, dest.prettyUrl());
1915 return;
1918 processed += n;
1919 processedSize(processed);
1922 ::close(srcFd);
1924 if(::close(destFd))
1926 error(KIO::ERR_COULD_NOT_WRITE, dest.prettyUrl());
1927 return;
1930 Misc::setFilePerms(realDest);
1932 // copy access and modification time
1933 struct utimbuf ut;
1935 ut.actime = buffSrc.st_atime;
1936 ut.modtime = buffSrc.st_mtime;
1937 ::utime(realDest.constData(), &ut);
1939 processedSize(buffSrc.st_size);
1940 if(!metrics)
1941 modified(timeout, destFolder, clearList, destFolderReal);
1944 if(src.isLocalFile() && 1==srcFiles.count())
1945 createAfm(destFolderReal+modifyName(map.begin().value()));
1948 finished();
1954 void CKioFonts::rename(const KUrl &src, const KUrl &d, KIO::JobFlags flags)
1956 KFI_DBUG << src.prettyUrl() << " query:" << src.query() << " - "
1957 << d.prettyUrl() << " query:" << d.query() << ", " << (flags & KIO::Overwrite);
1959 int timeout(reconfigTimeout());
1961 if(src.directory()==d.directory())
1963 if(!itsRoot && "/"==src.directory())
1965 error(KIO::ERR_SLAVE_DEFINED, i18n("You cannot rename font folders"));
1966 return;
1969 CDisabledFonts::TFontList::Iterator disabledIt;
1970 TFontMap::Iterator enabledIt;
1971 const CDisabledFonts::TFileList *entries(getEntries(src, enabledIt, disabledIt));
1973 if(!entries)
1975 KFI_DBUG << "No entries found, updating font list antry again...";
1976 updateFontList();
1977 entries=getEntries(src, enabledIt, disabledIt);
1980 if(entries)
1982 QString destFile(Misc::getFile(d.path())),
1983 srcFile(Misc::getFile(src.path())),
1984 destEn(destFile.mid(1)),
1985 srcEn(srcFile.mid(1));
1986 EFolder folder(getFolder(d));
1987 QString srcName(Misc::getFile(removeMultipleExtension(src)));
1988 bool clearList(!hasMetaData(KFI_KIO_NO_CLEAR)),
1989 nrs(nonRootSys(src)),
1990 enable(Misc::isHidden(srcFile) && !Misc::isHidden(destFile) && srcEn==destFile),
1991 disable(!Misc::isHidden(srcFile) && Misc::isHidden(destFile) &&
1992 destEn==srcFile);
1994 if(enable && disabledIt!=itsFolders[folder].disabled->items().end())
1996 if(confirmMultiple(src, (*disabledIt).files, folder, OP_ENABLE))
1998 CDirList folders;
1999 CDisabledFonts::TFileList::ConstIterator it((*disabledIt).files.begin()),
2000 end((*disabledIt).files.end());
2001 bool ok(false);
2003 for(; it!=end; ++it)
2004 folders.add(Misc::getDir(*it));
2006 if(nrs)
2008 TCommand c(KFI::CMD_ENABLE_FONT);
2010 c.args.append((*disabledIt).family);
2011 c.args.append((int)(*disabledIt).styleInfo);
2012 ok=doRootCmd(c);
2014 else
2015 ok=itsFolders[folder].disabled->enable(disabledIt);
2017 if(ok)
2019 modified(timeout, folder, clearList, folders);
2020 finished();
2022 else
2023 if(nrs)
2024 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
2025 else
2026 error(KIO::ERR_DOES_NOT_EXIST, src.prettyUrl());
2028 return;
2030 else if (disable && disabledIt==itsFolders[folder].disabled->items().end())
2032 if(confirmMultiple(src, (*enabledIt).files, folder, OP_DISABLE))
2034 CDirList folders;
2035 QMap<int, QString> names;
2036 CDisabledFonts::TFileList::ConstIterator it((*enabledIt).files.begin()),
2037 end((*enabledIt).files.end());
2038 bool ok=false;
2040 for(; it!=end; ++it)
2041 folders.add(Misc::getDir(*it));
2044 // If there is only 1 file mapped to this fontname, see if this file maps
2045 // to multiple font names - as would be the case in a TTC file...
2046 if(1==(*enabledIt).files.count())
2047 names=getFontIndexToNameEntries(folder, (*((*enabledIt).files.begin())).path);
2049 if(0==names.count())
2050 names[0]=enabledIt.key(); // Multiple files -> cant use index :-(
2052 QMap<int, QString>::ConstIterator nameIt(names.begin()),
2053 nameEnd(names.end());
2055 for(; nameIt!=nameEnd; ++nameIt)
2056 if(nrs)
2058 TCommand c(KFI::CMD_DISABLE_FONT);
2060 c.args.append(getFamily(enabledIt.key()));
2061 c.args.append((int)(*enabledIt).styleVal);
2062 c.args.append((*enabledIt).writingSystems);
2063 c.args.append((int)(nameIt.key()));
2064 c.args.append((*enabledIt).files.count());
2065 for(it=(*enabledIt).files.begin(); it!=end; ++it)
2067 c.args.append((*it).path);
2068 c.args.append((*it).foundry);
2070 ok=doRootCmd(c);
2072 else
2074 QString fontStr(*nameIt);
2075 int commaPos=fontStr.indexOf(',');
2076 CDisabledFonts::TFont font(-1==commaPos
2077 ? fontStr
2078 : fontStr.left(commaPos),
2079 (*enabledIt).styleVal,
2080 (*enabledIt).writingSystems);
2082 font.files=(*enabledIt).files;
2083 if(1==font.files.count())
2084 (*(font.files.begin())).face=nameIt.key();
2085 ok=itsFolders[folder].disabled->disable(font);
2088 if(ok)
2090 modified(timeout, folder, clearList, folders);
2091 finished();
2093 else
2094 if(nrs)
2095 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
2096 else
2097 error(KIO::ERR_DOES_NOT_EXIST, src.prettyUrl());
2098 return;
2100 error(KIO::ERR_SLAVE_DEFINED, i18n("Sorry, internal error - could not find font."));
2101 return;
2103 else if(enable || disable)
2104 error(KIO::ERR_SLAVE_DEFINED, enable
2105 ? i18n("Could not enable %1\n"
2106 "An enabled font already exists, please delete the disabled one.",
2107 src.prettyUrl())
2108 : i18n("Could not disable %1\n"
2109 "A disabled font already exists, please delete the enabled one.",
2110 src.prettyUrl()));
2111 else
2112 error(KIO::ERR_SLAVE_DEFINED, i18n("Sorry, fonts cannot be renamed."));
2114 return;
2116 error(KIO::ERR_DOES_NOT_EXIST, src.prettyUrl());
2117 return;
2119 else if(itsRoot) // Should never happen...
2121 error(KIO::ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, KIO::CMD_RENAME));
2122 return;
2124 else if(Misc::isHidden(src) || Misc::isHidden(d))
2126 error(KIO::ERR_SLAVE_DEFINED, i18n("Cannot move %1 to %2\nDisabled fonts cannot be moved.",
2127 src.prettyUrl(), d.prettyUrl()));
2128 return;
2130 else
2133 // Can't rename from/to file:/ -> therefore rename can only be from fonts:/System to
2134 // fonts:/Personal, or vice versa.
2135 CDisabledFonts::TFileList srcFiles;
2137 if(getSourceFiles(src, srcFiles)) // Any error will be logged in getSourceFiles
2139 KUrl dest(d);
2141 correctUrl(dest);
2143 EFolder destFolder(getFolder(dest));
2144 QMap<QString, QString> map;
2146 if(confirmMultiple(src, srcFiles, FOLDER_SYS==destFolder ? FOLDER_USER : FOLDER_SYS, OP_MOVE) &&
2147 getFontList(srcFiles, map) && checkDestFiles(src, map, dest, destFolder, flags))
2149 QMap<QString, QString>::Iterator fIt(map.begin()),
2150 fEnd(map.end());
2151 bool askPasswd=true,
2152 toSys=FOLDER_SYS==destFolder;
2154 for(; fIt!=fEnd; ++fIt)
2156 QString destFolderReal(getDestFolder(itsFolders[destFolder].location, fIt.value()));
2157 QList<TCommand> cmd;
2158 TCommand c(KFI::CMD_MOVE_FILE);
2160 if(toSys)
2162 addCreateFolderCmd(itsFolders[FOLDER_SYS].location, cmd);
2163 if(destFolderReal!=itsFolders[FOLDER_SYS].location)
2164 addCreateFolderCmd(destFolderReal, cmd);
2167 c.args.append(fIt.key());
2168 c.args.append(destFolderReal+fIt.value());
2169 c.args.append((int)(toSys ? 0 : getuid()));
2170 c.args.append((int)(toSys ? 0 : getgid()));
2171 cmd.append(c);
2173 QString sysDir,
2174 userDir;
2176 if(FOLDER_SYS==destFolder)
2178 sysDir=destFolderReal;
2179 userDir=Misc::getDir(fIt.key());
2181 else
2183 userDir=destFolderReal;
2184 sysDir=Misc::getDir(fIt.key());
2187 if(doRootCmd(cmd, askPasswd))
2189 modified(timeout, FOLDER_SYS, true, sysDir);
2190 modified(timeout, FOLDER_USER, true, userDir);
2191 askPasswd=false; // Don't keep on asking for password...
2193 else
2195 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
2196 return;
2202 finished();
2205 void CKioFonts::del(const KUrl &url, bool)
2207 KFI_DBUG << url.path() << " query:" << url.query();
2209 const CDisabledFonts::TFileList *entries;
2210 CDisabledFonts::TFontList::Iterator disabledIt;
2211 TFontMap::Iterator enabledIt;
2213 if(checkUrl(url) && checkAllowed(url) &&
2214 (( (entries=getEntries(url, enabledIt, disabledIt)) && entries->count() && checkFiles(*entries)) ||
2215 ( updateFontList() && (entries=getEntries(url, enabledIt, disabledIt)) && entries->count() &&
2216 checkFiles(*entries))) && confirmMultiple(url, entries, getFolder(url), OP_DELETE))
2218 CDisabledFonts::TFileList::ConstIterator it,
2219 end(entries->end());
2220 CDirList modifiedDirs;
2221 bool clearList(!hasMetaData(KFI_KIO_NO_CLEAR));
2222 int timeout(reconfigTimeout());
2224 if(nonRootSys(url))
2226 if(disabledIt!=itsFolders[FOLDER_SYS].disabled->items().end())
2228 TCommand c(KFI::CMD_DELETE_DISABLED_FONT);
2230 c.args.append((*disabledIt).family);
2231 c.args.append((int)(*disabledIt).styleInfo);
2233 if(doRootCmd(c))
2234 itsFolders[FOLDER_SYS].disabled->refresh();
2235 else
2237 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
2238 return;
2241 else
2243 QList<TCommand> cmd;
2245 for(it=entries->begin(); it!=end; ++it)
2247 modifiedDirs.add(Misc::getDir(*it));
2248 cmd.append(TCommand(KFI::CMD_DELETE_FILE, (*it).path));
2250 QStringList files;
2252 Misc::getAssociatedFiles(*it, files);
2254 if(files.count())
2256 QStringList::Iterator fIt,
2257 fEnd=files.end();
2259 for(fIt=files.begin(); fIt!=fEnd; ++fIt)
2260 cmd.append(TCommand(KFI::CMD_DELETE_FILE, *fIt));
2264 if(doRootCmd(cmd))
2265 modified(timeout, FOLDER_SYS, clearList, modifiedDirs);
2266 else
2268 error(KIO::ERR_ACCESS_DENIED, KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS);
2269 return;
2273 else
2275 for(it=entries->begin(); it!=end; ++it)
2277 if (0!=unlink(QFile::encodeName(*it).constData()))
2278 error(EACCES==errno || EPERM==errno
2279 ? KIO::ERR_ACCESS_DENIED
2280 : EISDIR==errno
2281 ? KIO::ERR_IS_DIRECTORY
2282 : KIO::ERR_CANNOT_DELETE,
2283 *it);
2284 else
2286 modifiedDirs.add(Misc::getDir(*it));
2288 QStringList files;
2290 Misc::getAssociatedFiles(*it, files);
2292 if(files.count())
2294 QStringList::Iterator fIt,
2295 fEnd=files.end();
2297 for(fIt=files.begin(); fIt!=fEnd; ++fIt)
2298 unlink(QFile::encodeName(*fIt).constData());
2303 if(disabledIt!=itsFolders[itsRoot ? FOLDER_SYS : FOLDER_USER].disabled->items().end())
2305 itsFolders[itsRoot ? FOLDER_SYS : FOLDER_USER].disabled->remove(disabledIt);
2306 itsFolders[itsRoot ? FOLDER_SYS : FOLDER_USER].disabled->refresh();
2308 else
2309 modified(timeout, itsRoot ? FOLDER_SYS : FOLDER_USER, clearList, modifiedDirs);
2311 finished();
2313 else if(isATtc(QFile::encodeName(url.fileName())))
2314 finished();
2315 else
2316 error(KIO::ERR_SLAVE_DEFINED, i18n("Could not access \"%1\".", url.prettyUrl()));
2319 void CKioFonts::modified(int timeout, EFolder folder, bool clearList, const CDirList &dirs)
2321 KFI_DBUG << "timout:" << timeout << " folder:" << (int)folder << " clearList:" << clearList;
2323 if(FOLDER_SYS!=folder || itsRoot)
2325 if(dirs.count())
2327 CDirList::ConstIterator it(dirs.begin()),
2328 end(dirs.end());
2330 for(; it!=end; ++it)
2331 itsFolders[folder].modified.add(*it);
2333 else
2334 itsFolders[folder].modified.add(itsFolders[folder].location);
2335 setTimeoutSpecialCommand(timeout ? timeout : -1);
2338 if(clearList)
2339 clearFontList(); // List of fonts has changed.../
2342 void CKioFonts::special(const QByteArray &a)
2344 KFI_DBUG;
2346 if(a.size())
2348 QDataStream stream(a);
2349 int cmd;
2351 stream >> cmd;
2353 switch (cmd)
2355 case SPECIAL_RESCAN:
2356 clearFontList();
2357 updateFontList();
2358 finished();
2359 break;
2360 case SPECIAL_CONFIGURE:
2362 if(itsRoot)
2364 KUrl url;
2366 stream >> url;
2368 if(url.isValid())
2369 itsFolders[FOLDER_SYS].disabled->reload();
2370 if(0==itsFolders[FOLDER_SYS].modified.count())
2371 configure(FOLDER_SYS);
2373 else
2374 for(;;)
2376 KUrl url;
2378 stream >> url;
2380 if(url.isEmpty() || !url.isValid())
2381 break;
2383 QString sect(getSect(url.path()));
2385 if(isSysFolder(sect))
2387 itsFolders[FOLDER_SYS].disabled->reload();
2388 if(0==itsFolders[FOLDER_SYS].modified.count())
2389 configure(FOLDER_SYS);
2391 else if(isUserFolder(sect))
2393 itsFolders[FOLDER_USER].disabled->reload();
2394 if(0==itsFolders[FOLDER_USER].modified.count())
2395 configure(FOLDER_USER);
2399 if(itsFolders[FOLDER_USER].disabled->modified())
2400 itsFolders[FOLDER_USER].disabled->reload();
2401 doModified();
2402 clearFontList();
2403 updateFontList();
2404 finished();
2405 break;
2407 default:
2408 error(KIO::ERR_UNSUPPORTED_ACTION, QString::number(cmd));
2411 else
2412 doModified();
2415 bool CKioFonts::configure(EFolder folder)
2417 bool refreshX(false);
2419 if(!itsRoot && FOLDER_SYS==folder)
2421 QList<TCommand> cmd;
2423 if(itsAddToSysFc)
2425 itsAddToSysFc=false;
2426 cmd.append(TCommand(KFI::CMD_ADD_DIR_TO_FONTCONFIG, itsFolders[FOLDER_SYS].location));
2429 if(itsFolders[FOLDER_SYS].modified.count())
2431 CDirList::ConstIterator it(itsFolders[FOLDER_SYS].modified.begin()),
2432 end(itsFolders[FOLDER_SYS].modified.end());
2434 for(; it!=end; ++it)
2435 if(Misc::fExists((*it)+"fonts.dir"))
2437 cmd.append(TCommand(KFI::CMD_CFG_DIR_FOR_X, *it));
2438 refreshX=true;
2442 cmd.append(TCommand(KFI::CMD_FC_CACHE));
2443 doRootCmd(cmd, false);
2445 else
2447 Misc::doCmd(FC_CACHE_CMD);
2448 KFI_DBUG << "RUN: " << FC_CACHE_CMD;
2450 itsFolders[folder].disabled->save();
2452 CDirList::ConstIterator it(itsFolders[folder].modified.begin()),
2453 end(itsFolders[folder].modified.end());
2455 for(; it!=end; ++it)
2456 if(Misc::fExists((*it)+"fonts.dir"))
2458 Misc::configureForX11(*it);
2459 refreshX=true;
2462 return refreshX;
2465 void CKioFonts::doModified()
2467 KFI_DBUG;
2468 bool refreshX(false),
2469 clear(false);
2471 infoMessage(i18n("Configuring installed fonts..."));
2472 setTimeoutSpecialCommand(-1); // Cancel timer
2474 if(itsFolders[FOLDER_SYS].modified.count())
2476 refreshX=configure(FOLDER_SYS);
2477 itsFolders[FOLDER_SYS].modified.clear();
2478 clear=true;
2481 if(!itsRoot && itsFolders[FOLDER_USER].modified.count())
2483 refreshX=configure(FOLDER_USER);
2484 itsFolders[FOLDER_USER].modified.clear();
2485 clear=true;
2488 if(clear)
2489 clearFontList();
2490 if(refreshX)
2491 Misc::doCmd("xset", "fp", "rehash");
2492 infoMessage(QString());
2493 KFI_DBUG << "finished";
2496 bool CKioFonts::getRootPasswd(bool askPasswd)
2498 KFI_DBUG;
2500 if(hasMetaData(KFI_KIO_PASS))
2502 itsPasswd=metaData(KFI_KIO_PASS);
2503 return !itsPasswd.isEmpty();
2506 if(!askPasswd)
2507 return !itsPasswd.isEmpty();
2509 KIO::AuthInfo authInfo;
2510 SuProcess proc(KFI_SYS_USER);
2511 bool error=false;
2512 int attempts=0;
2513 QString errorMsg;
2515 authInfo.url=KUrl(KFI_KIO_FONTS_PROTOCOL":/"KFI_KIO_FONTS_SYS"/");
2516 authInfo.keepPassword=false;
2517 authInfo.caption=i18n("Authorisation Required");
2518 authInfo.username=i18n(KFI_AUTHINF_USER);
2520 if(proc.useUsersOwnPassword())
2521 authInfo.prompt=i18n("The requested action requires administrator privileges.\n"
2522 "If you have these privileges, then please enter your password.");
2523 else
2524 authInfo.prompt=i18n("The requested action requires administrator privileges.\n"
2525 "Please enter the system administrator's password.");
2527 if(!checkCachedAuthentication(authInfo) && !askPasswd)
2528 authInfo.password=itsPasswd;
2530 if(askPasswd)
2532 while(!error && 0!=proc.checkInstall(authInfo.password.toLocal8Bit()))
2534 KFI_DBUG << "ATTEMPT : " << attempts;
2535 if(1==attempts++)
2536 errorMsg=i18n("Incorrect password.\n");
2537 if(attempts>2 || !openPasswordDialog(authInfo, errorMsg))
2538 error=true;
2540 if(!error && authInfo.keepPassword)
2541 cacheAuthentication(authInfo);
2543 else
2544 error=proc.checkInstall(authInfo.password.toLocal8Bit()) ? true : false;
2546 itsPasswd= error ? QString() : authInfo.password;
2547 return !itsPasswd.isEmpty();
2550 void CKioFonts::quitHelper()
2552 if(itsServer.isOpen() && itsSuProc && itsSocket && itsSuProc->isRunning())
2554 KFI_DBUG;
2555 if(itsSocket->write(QVariant((int)KFI::CMD_QUIT)))
2557 bool res;
2558 if(itsSocket->read(res, 10) && res)
2560 itsSuProc->terminate();
2561 itsSuProc->wait(100);
2567 bool CKioFonts::doRootCmd(QList<TCommand> &cmd, bool askPasswd)
2569 KFI_DBUG;
2571 if(cmd.count() && getRootPasswd(askPasswd))
2573 if(!itsServer.isOpen())
2575 KFI_DBUG << "Open server socket";
2576 // Open socket for communication with helper app...
2577 itsServer.open();
2580 if(itsServer.isOpen())
2582 if(itsSuProc && !itsSuProc->isRunning())
2584 KFI_DBUG << "Delete client socket";
2585 delete itsSocket;
2586 itsSocket=NULL;
2587 delete itsSuProc;
2588 itsSuProc=NULL;
2591 if(!itsSuProc)
2593 // Start helper app...
2594 KFI_DBUG << "Start helper...";
2595 itsSuProc=new CSuProc(itsServer.name(), itsPasswd);
2596 itsSuProc->start();
2599 if(!itsSocket)
2601 // Wait for helper app to connect...
2602 KFI_DBUG << "Wait for client...";
2603 itsSocket=itsServer.waitForClient();
2606 if(itsSocket)
2608 // Write commands to helper, and wait for replies...
2609 QList<TCommand>::ConstIterator it(cmd.begin()),
2610 end(cmd.end());
2611 bool commsError(false);
2613 for(; it!=end && !commsError; ++it)
2615 KFI_DBUG << "Send command #" << (*it).cmd;
2617 if(itsSocket->write(QVariant((int)(*it).cmd)))
2619 QList<QVariant>::ConstIterator argIt((*it).args.begin()),
2620 argEnd((*it).args.end());
2622 for(; argIt!=argEnd && !commsError; ++argIt)
2623 if(!itsSocket->write(*argIt))
2625 KFI_DBUG << "Failed to write arg";
2626 commsError=true;
2629 if(!commsError) // Wait for response!
2631 bool res;
2633 if(itsSocket->read(res, CMD_FC_CACHE==(*it).cmd ||
2634 CMD_CFG_DIR_FOR_X==(*it).cmd
2635 ? 600 // fc-cache can take a *long* time...
2636 : 10))
2638 if(!res &&
2639 CMD_ADD_DIR_TO_FONTCONFIG!=(*it).cmd &&
2640 CMD_CFG_DIR_FOR_X!=(*it).cmd)
2642 KFI_DBUG << "Command failed :-(";
2643 return false;
2646 else
2648 KFI_DBUG << "Failed to read response";
2649 commsError=true;
2653 else
2655 KFI_DBUG << "Failed to write command id";
2656 commsError=true;
2660 if(!commsError)
2661 return true;
2662 delete itsSocket;
2663 itsSocket=NULL;
2665 else
2666 KFI_DBUG << "No socket connection :-(";
2670 return false;
2673 bool CKioFonts::doRootCmd(const TCommand &cmd, bool askPasswd)
2675 QList<TCommand> cmds;
2677 cmds.append(cmd);
2678 return doRootCmd(cmds, askPasswd);
2681 void CKioFonts::correctUrl(KUrl &url)
2683 KFI_DBUG << url.path();
2684 if(!itsRoot)
2686 QString sect(getSect(url.path()));
2688 if(!isSysFolder(sect) && !isUserFolder(sect))
2690 url.setPath(QChar('/')+i18n(KFI_KIO_FONTS_USER)+QChar('/')+url.fileName());
2691 KFI_DBUG << "Changed URL to:" << url.path();
2696 void CKioFonts::clearFontList()
2698 KFI_DBUG;
2700 if(itsFontList)
2701 FcFontSetDestroy(itsFontList);
2703 itsFontList=NULL;
2704 itsFolders[FOLDER_SYS].fontMap.clear();
2705 if(!itsRoot)
2706 itsFolders[FOLDER_USER].fontMap.clear();
2709 bool CKioFonts::updateFontList()
2711 KFI_DBUG;
2713 // For some reason just the "!FcConfigUptoDate(0)" check does not always work :-(
2714 if(0!=itsLastFcCheckTime &&
2715 (!itsFontList || !FcConfigUptoDate(0) ||
2716 (abs(time(NULL)-itsLastFcCheckTime)>constMaxFcCheckTime)))
2718 KFI_DBUG << "itsFontList:" << (intptr_t)itsFontList
2719 << " FcConfigUptoDate:" << (int)FcConfigUptoDate(0)
2720 << " time diff:" << abs(time(NULL)-itsLastFcCheckTime)
2721 << " max:" << constMaxFcCheckTime;
2722 FcInitReinitialize();
2723 clearFontList();
2726 if(!itsRoot)
2728 if(itsServer.isOpen() && itsSuProc && itsSocket)
2729 doRootCmd(TCommand(KFI::CMD_RELOAD_DISABLED_LIST), false);
2730 itsFolders[FOLDER_USER].disabled->refresh();
2732 itsFolders[FOLDER_SYS].disabled->refresh();
2734 if(!itsFontList)
2736 KFI_DBUG << "update list of fonts";
2738 itsLastFcCheckTime=time(NULL);
2740 FcPattern *pat = FcPatternCreate();
2741 FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_FAMILY,
2742 #ifdef KFI_USE_TRANSLATED_FAMILY_NAME
2743 FC_FAMILYLANG,
2744 #endif
2745 FC_WEIGHT, FC_LANG, FC_CHARSET,
2746 //FC_SCALABLE,
2747 #ifndef KFI_FC_NO_WIDTHS
2748 FC_WIDTH,
2749 #endif
2750 FC_SLANT, FC_INDEX, FC_FOUNDRY, (void*)0);
2752 itsFontList=FcFontList(0, pat, os);
2754 FcPatternDestroy(pat);
2755 FcObjectSetDestroy(os);
2757 if (itsFontList)
2760 // defoma (DEbian FOnt MAnanager) installs sym links into /var/lib/defoma/fontconfig.d, but also
2761 // places this folder into fontconfigs search path. Leading to duplicate font files. Therefore, just
2762 // ignore defoma's sym links...
2763 // -> Can't just ignore these, as if a font is disabled fontconfig will still list it, as it sees the symlink
2764 //static const char * constDefomaLocation="/var/lib/defoma/fontconfig.d";
2766 QString home(Misc::dirSyntax(QDir::homePath()));
2768 for (int i = 0; i < itsFontList->nfont; i++)
2770 EFolder folder=FOLDER_SYS;
2771 QString fileName(Misc::fileSyntax(FC::getFcString(itsFontList->fonts[i], FC_FILE)));
2773 if(!fileName.isEmpty()) // && 0!=fileName.indexOf(constDefomaLocation))
2775 QString name,
2776 foundry(FC::getFcString(itsFontList->fonts[i], FC_FOUNDRY));
2777 quint32 styleVal;
2778 int index;
2780 if(!itsRoot && 0==fileName.indexOf(home))
2781 folder=FOLDER_USER;
2783 FC::getDetails(itsFontList->fonts[i], name, styleVal, index);
2785 TFontDetails &details=itsFolders[folder].fontMap[name];
2786 bool use=true;
2788 details.styleVal=styleVal;
2789 details.writingSystems|=getWritingSystems(itsFontList->fonts[i]);
2791 if(details.files.count()) // Check for duplicates...
2793 CDisabledFonts::TFileList::Iterator it,
2794 end=details.files.end();
2796 for(it=details.files.begin(); use && it!=end; ++it)
2797 if(fileName==*it)
2798 use=false;
2800 if(use)
2801 details.files.append(CDisabledFonts::TFile(fileName, index, foundry));
2806 KFI_DBUG << "updated list of fonts";
2809 if(NULL==itsFontList)
2811 error(KIO::ERR_SLAVE_DEFINED, i18n("Internal fontconfig error."));
2812 return false;
2815 return true;
2818 CKioFonts::EFolder CKioFonts::getFolder(const KUrl &url)
2820 return itsRoot || isSysFolder(getSect(url.path())) ? FOLDER_SYS : FOLDER_USER;
2823 CKioFonts::TFontMap::Iterator CKioFonts::getMap(const KUrl &url)
2825 KFI_DBUG << url.prettyUrl();
2827 int face(Misc::getIntQueryVal(url, KFI_KIO_FACE, 0));
2828 EFolder folder(getFolder(url));
2829 TFontMap::Iterator it=itsFolders[folder].fontMap.find(removeMultipleExtension(url)),
2830 end(itsFolders[folder].fontMap.end());
2832 if(it==end) // Perhaps it was fonts:/System/times.ttf ???
2834 QString fName(Misc::getFile(url.path()));
2836 for(int t=0; t<3; ++t)
2838 QString fileName=0==t
2839 ? fName
2840 : 1==t ? modifyName(fName) // lowercase
2841 : modifyName(fName, true); // uppercase
2843 KFI_DBUG << "look for " << fileName;
2845 for(it=itsFolders[folder].fontMap.begin(); it!=end; ++it)
2847 CDisabledFonts::TFileList::Iterator sIt((*it).files.begin()),
2848 sEnd((*it).files.end());
2850 for(;sIt!=sEnd; ++sIt)
2851 if(Misc::getFile(*sIt)==fileName && (*sIt).face==face)
2852 return it;
2857 return it;
2860 const CDisabledFonts::TFileList * CKioFonts::getEntries(const KUrl &url,
2861 TFontMap::Iterator &enabledIt,
2862 CDisabledFonts::TFontList::Iterator &disabledIt)
2864 KFI_DBUG << url.prettyUrl();
2866 EFolder folder=getFolder(url);
2867 TFontMap::Iterator it(getMap(url)),
2868 end(itsFolders[folder].fontMap.end());
2869 QString name=Misc::getFile(removeMultipleExtension(url));
2870 CDisabledFonts::TFontList::Iterator dIt(itsFolders[folder].disabled->find(name,
2871 Misc::getIntQueryVal(url, KFI_KIO_FACE, 0))),
2872 dEnd(itsFolders[folder].disabled->items().end());
2874 enabledIt=end;
2875 disabledIt=dEnd;
2877 if(it!=end && dIt==dEnd)
2879 KFI_DBUG << "found enabled";
2880 enabledIt=it;
2881 return &(it.value().files);
2883 else if (it==end && dIt!=dEnd)
2885 disabledIt=dIt;
2886 KFI_DBUG << "found disabled";
2887 return &((*dIt).files);
2889 else if(it!=end && dIt!=dEnd)
2891 KFI_DBUG << "found both!";
2893 // Oops... we have a match for both a hidden, and non-hidden font! Have to ask which one...
2894 // This should never really happen, as hidden fonts will start with a period.
2895 if(KMessageBox::Yes==messageBox(QuestionYesNo,
2896 i18n("The selected URL (%1) matches both an enabled, and disabled "
2897 "font. Which one do you wish to access?", url.prettyUrl()),
2898 i18n("Duplicate Font"), i18n("Enabled Font"),
2899 i18n("Disabled Font")))
2901 enabledIt=it;
2902 return &(it.value().files);
2904 else
2906 disabledIt=dIt;
2907 return &((*dIt).files);
2911 KFI_DBUG << "found none";
2912 return NULL;
2915 QStringList CKioFonts::getFontNameEntries(EFolder folder, const QString &file, bool disabledFonts)
2917 QStringList rv;
2919 if(disabledFonts)
2921 CDisabledFonts::TFontList::Iterator it(itsFolders[folder].disabled->items().begin()),
2922 end(itsFolders[folder].disabled->items().end());
2924 for(; it!=end; ++it)
2926 CDisabledFonts::TFileList::ConstIterator patIt,
2927 patEnd=(*it).files.end();
2929 for(patIt=(*it).files.begin(); patIt!=patEnd; ++patIt)
2930 if((*patIt).path==file)
2932 rv.append((*it).name);
2933 break;
2937 else
2939 TFontMap::Iterator it,
2940 end=itsFolders[folder].fontMap.end();
2942 for(it=itsFolders[folder].fontMap.begin(); it!=end; ++it)
2944 CDisabledFonts::TFileList::ConstIterator patIt,
2945 patEnd=it.value().files.end();
2947 for(patIt=it.value().files.begin(); patIt!=patEnd; ++patIt)
2948 if((*patIt).path==file)
2950 rv.append(it.key());
2951 break;
2955 return rv;
2958 QMap<int, QString> CKioFonts::getFontIndexToNameEntries(EFolder folder, const QString &file)
2960 QMap<int, QString> rv;
2961 TFontMap::Iterator it,
2962 end=itsFolders[folder].fontMap.end();
2964 for(it=itsFolders[folder].fontMap.begin(); it!=end; ++it)
2966 CDisabledFonts::TFileList::Iterator patIt,
2967 patEnd=it.value().files.end();
2969 for(patIt=it.value().files.begin(); patIt!=patEnd; ++patIt)
2970 if((*patIt).path==file)
2972 rv[(*patIt).face]=it.key();
2973 break;
2977 return rv;
2980 QString * CKioFonts::getEntry(EFolder folder, const QString &file, bool full)
2982 TFontMap::Iterator it,
2983 end=itsFolders[folder].fontMap.end();
2985 for(it=itsFolders[folder].fontMap.begin(); it!=end; ++it)
2987 CDisabledFonts::TFileList::Iterator patIt,
2988 patEnd=it.value().files.end();
2990 for(patIt=it.value().files.begin(); patIt!=patEnd; ++patIt)
2991 if( (full && (*patIt).path==file) ||
2992 (!full && Misc::getFile(*patIt)==file))
2993 return &((*patIt).path);
2996 return NULL;
2999 CKioFonts::EFileType CKioFonts::checkFile(const QString &file, const KUrl &url)
3002 // To speed things up, check the files extension 1st...
3003 if(Misc::checkExt(file, "bdf") || Misc::checkExt(file, "bdf.gz") ||
3004 Misc::checkExt(file, "pcf") || Misc::checkExt(file, "pcf.gz"))
3006 // Need to check whether bitmaps have been hidden from from fontconfig - as happens on KUbuntu...
3007 if(FC::bitmapsEnabled())
3008 return FILE_FONT;
3009 else
3010 error(KIO::ERR_SLAVE_DEFINED, i18n("You cannot install bitmap fonts, as these have been "
3011 "disabled on your system."));
3013 else if(isAAfm(file) || isAPfm(file))
3014 return FILE_METRICS;
3015 else if(Misc::isPackage(file))
3016 error(KIO::ERR_SLAVE_DEFINED, i18n("You cannot install a fonts package directly.\n"
3017 "Please extract %1, and install the components individually.",
3018 url.prettyUrl()));
3019 else
3022 // Check that file is a font via FreeType...
3023 int count=0;
3024 FcPattern *pat=FcFreeTypeQuery((const FcChar8 *)(QFile::encodeName(file).constData()), 0, NULL,
3025 &count);
3027 if(pat)
3029 FcBool scalable;
3031 if(FcResultMatch==FcPatternGetBool(pat, FC_SCALABLE, 0, &scalable) && scalable)
3033 // check too see whether font is already installed!
3034 int weight(KFI_NULL_SETTING), slant(KFI_NULL_SETTING), width(KFI_NULL_SETTING);
3036 FcPatternGetInteger(pat, FC_WEIGHT, 0, &weight);
3037 FcPatternGetInteger(pat, FC_SLANT, 0, &slant);
3038 #ifndef KFI_FC_NO_WIDTHS
3039 FcPatternGetInteger(pat, FC_WIDTH, 0, &width);
3040 #endif
3042 QString name(FC::createName(pat, weight, width, slant));
3044 KFI_DBUG << "Check for name:" << name;
3046 // TODO: CDisabledFonts need to find on family & style info? Also need a find() that does
3047 // not take into account face! Perhaps use -1?
3048 if(itsFolders[FOLDER_SYS].fontMap.contains(name) ||
3049 itsFolders[FOLDER_SYS].disabled->items().end()!=
3050 itsFolders[FOLDER_SYS].disabled->find(name, 1) ||
3051 (!itsRoot && (itsFolders[FOLDER_USER].fontMap.contains(name) ||
3052 itsFolders[FOLDER_USER].disabled->items().end()!=
3053 itsFolders[FOLDER_USER].disabled->find(name, 1))))
3055 FcPatternDestroy(pat);
3056 error(KIO::ERR_SLAVE_DEFINED, i18n("File %1 contains the font:\n%2\n"
3057 "A font with this name is already installed.\n",
3058 url.prettyUrl(), name));
3059 return FILE_UNKNOWN;
3062 FcPatternDestroy(pat);
3063 return FILE_FONT;
3066 error(KIO::ERR_SLAVE_DEFINED, i18n("Could not determine file type for: %1\n"
3067 "Only fonts may be installed.", url.prettyUrl()));
3069 return FILE_UNKNOWN;
3072 bool CKioFonts::getSourceFiles(const KUrl &src, CDisabledFonts::TFileList &files, bool removeSymLinks)
3074 if(KFI_KIO_FONTS_PROTOCOL==src.protocol())
3076 CDisabledFonts::TFontList::Iterator disabledIt;
3077 TFontMap::Iterator enabledIt;
3078 const CDisabledFonts::TFileList *entries=getEntries(src, enabledIt, disabledIt);
3080 if(entries)
3081 getFontFiles(*entries, files, removeSymLinks);
3083 else
3084 if(src.isLocalFile())
3085 if(FILE_UNKNOWN!=checkFile(src.path(), src))
3086 files.append(CDisabledFonts::TFile(src.path()));
3087 else
3088 return false; // error logged in checkFile...
3090 if(files.count())
3092 CDisabledFonts::TFileList::Iterator it,
3093 end=files.end();
3095 for(it=files.begin(); it!=end; ++it)
3097 QByteArray realSrc=QFile::encodeName(*it);
3098 KDE_struct_stat buffSrc;
3100 if (-1==KDE_stat(realSrc.constData(), &buffSrc))
3102 error(EACCES==errno ? KIO::ERR_ACCESS_DENIED : KIO::ERR_DOES_NOT_EXIST,
3103 src.prettyUrl());
3104 return false;
3106 if(S_ISDIR(buffSrc.st_mode))
3108 error(KIO::ERR_IS_DIRECTORY, src.prettyUrl());
3109 return false;
3111 if(S_ISFIFO(buffSrc.st_mode) || S_ISSOCK(buffSrc.st_mode))
3113 error(KIO::ERR_CANNOT_OPEN_FOR_READING, src.prettyUrl());
3114 return false;
3118 else
3120 error(KIO::ERR_DOES_NOT_EXIST, src.prettyUrl());
3121 return false;
3124 return true;
3127 bool CKioFonts::checkDestFile(const KUrl &src, const KUrl &dest, EFolder destFolder, KIO::JobFlags flags)
3129 QStringList folders;
3131 folders.append(itsFolders[destFolder].location);
3132 folders.append(getDestFolder(itsFolders[destFolder].location, src.fileName()));
3134 QStringList::Iterator it(folders.begin()),
3135 end(folders.end());
3136 QString destFile;
3138 for(; it!=end; ++it)
3140 if(!(flags & KIO::Overwrite) && (Misc::fExists(destFile=(*it)+src.fileName()) ||
3141 Misc::fExists(destFile=(*it)+modifyName(src.fileName())) ||
3142 Misc::fExists(destFile=(*it)+modifyName(src.fileName(), true)) ) )
3144 // If copying / moving a TTC and it is the *same* file, then don't log an error, but
3145 // don't continue the transaction...
3147 // Reason being that fonts:/ lists the font names (not filenames) so for a TTC there'll
3148 // be multiple entries...
3149 if(isSameTtc(src.path(), destFile))
3150 finished();
3151 else
3152 error(KIO::ERR_FILE_ALREADY_EXIST, dest.prettyUrl());
3153 return false;
3156 bool isHidden=Misc::isHidden(src);
3157 QString other(isHidden ? src.fileName().mid(1)
3158 : QChar('.')+src.fileName());
3160 if(Misc::fExists((*it)+other) ||
3161 Misc::fExists((*it)+modifyName(other)) ||
3162 Misc::fExists((*it)+modifyName(other, true)))
3164 error(KIO::ERR_SLAVE_DEFINED,
3165 isHidden
3166 ? i18n("Could not install %1\nA matching enabled font already exists. "
3167 "Please disable that.", src.prettyUrl())
3168 : i18n("Could not install %1\nA matching disabled font already exists. "
3169 "Please enable that.", src.prettyUrl()));
3170 return false;
3173 return true;
3176 bool CKioFonts::checkDestFiles(const KUrl &src, QMap<QString, QString> &map, const KUrl &dest,
3177 EFolder destFolder, KIO::JobFlags flags)
3180 // Check whether files exist at destination...
3182 if(dest.protocol()==src.protocol() &&
3183 dest.directory()==src.directory()) // Check whether confirmUrl changed a "cp fonts:/System
3184 // fonts:/" to "cp fonts:/System fonts:/System"
3186 error(KIO::ERR_FILE_ALREADY_EXIST, dest.prettyUrl());
3187 return false;
3190 if(!(flags & KIO::Overwrite))
3192 QMap<QString, QString>::Iterator fIt(map.begin()),
3193 fEnd(map.end());
3194 QString *destEntry;
3196 for(; fIt!=fEnd; ++fIt)
3197 if(NULL!=(destEntry=getEntry(destFolder, fIt.value())) ||
3198 NULL!=(destEntry=getEntry(destFolder, modifyName(fIt.value()))) || // lowercase
3199 NULL!=(destEntry=getEntry(destFolder, modifyName(fIt.value()), true))) // uppercase
3201 // If copying / moving a TTC and it is the *same* file, then don't log an error, but
3202 // don't continue the transaction...
3204 // Reason being that fonts:/ lists the font names (not filenames) so for a TTC there'll
3205 // be multiple entries for a TTC...
3206 if(isSameTtc(src.path(), *destEntry))
3207 finished();
3208 else
3209 error(KIO::ERR_FILE_ALREADY_EXIST, dest.prettyUrl());
3210 return false;
3214 return true;
3218 // Gather the number and names of the font faces located in "files". If there is more than 1 face
3219 // (such as there would be for a TTC font), then ask the user for confirmation of the action.
3220 bool CKioFonts::confirmMultiple(const KUrl &url, const CDisabledFonts::TFileList &files, EFolder folder,
3221 EOp op)
3223 if(KFI_KIO_FONTS_PROTOCOL!=url.protocol())
3224 return true;
3226 CDisabledFonts::TFileList::ConstIterator it,
3227 end=files.end();
3228 QStringList fonts;
3230 for(it=files.begin(); it!=files.end(); ++it)
3232 QStringList fn(getFontNameEntries(folder, *it, OP_ENABLE==op));
3233 QStringList::Iterator fnIt(fn.begin()),
3234 fnEnd(fn.end());
3236 for(; fnIt!=fnEnd; ++fnIt)
3237 if(-1==fonts.indexOf(*fnIt))
3238 fonts.append(*fnIt);
3241 if(fonts.count()>1)
3243 QString out,
3244 question;
3245 QStringList::Iterator it,
3246 end=fonts.end();
3248 for(it=fonts.begin(); it!=end; ++it)
3249 out+=QString("<li>")+*it+QString("</li>");
3251 switch(op)
3253 case OP_MOVE:
3254 question=i18n("<p>You are attempting to move a font that is located in a file alongside "
3255 "other fonts; in order to proceed with the moving they will "
3256 "all have to be moved. The affected fonts are:</p>"
3257 "<ul>%1</ul><p>\n Do you wish to move all of these?</p>", out);
3258 break;
3259 case OP_COPY:
3260 question=i18n("<p>You are attempting to copy a font that is located in a file alongside "
3261 "other fonts; in order to proceed with the copying they will "
3262 "all have to be copied. The affected fonts are:</p>"
3263 "<ul>%1</ul><p>\n Do you wish to copy all of these?</p>", out);
3264 break;
3265 case OP_DELETE:
3266 question=i18n("<p>You are attempting to delete a font that is located in a file alongside "
3267 "other fonts; in order to proceed with the deleting they will "
3268 "all have to be deleted. The affected fonts are:</p>"
3269 "<ul>%1</ul><p>\n Do you wish to delete all of these?</p>", out);
3270 break;
3271 case OP_ENABLE:
3272 question=i18n("<p>You are attempting to enable a font that is located in a file alongside "
3273 "other fonts; in order to proceed with the enabling they will "
3274 "all have to be enabled. The affected fonts are:</p>"
3275 "<ul>%1</ul><p>\n Do you wish to enable all of these?</p>", out);
3276 break;
3277 case OP_DISABLE:
3278 question=i18n("<p>You are attempting to disable a font that is located in a file alongside "
3279 "other fonts; in order to proceed with the disabling they will "
3280 "all have to be disabled. The affected fonts are:</p>"
3281 "<ul>%1</ul><p>\n Do you wish to disable all of these?</p>", out);
3282 break;
3285 if(KMessageBox::No==messageBox(question, QuestionYesNo))
3287 error(KIO::ERR_USER_CANCELED, url.prettyUrl());
3288 return false;
3292 return true;
3295 bool CKioFonts::confirmMultiple(const KUrl &url, const CDisabledFonts::TFileList *patterns,
3296 EFolder folder, EOp op)
3298 if(KFI_KIO_FONTS_PROTOCOL!=url.protocol())
3299 return true;
3301 return patterns ? confirmMultiple(url, *patterns, folder, op) : false;
3304 bool CKioFonts::checkUrl(const KUrl &u, bool rootOk, bool logError)
3306 if(KFI_KIO_FONTS_PROTOCOL==u.protocol() && (!rootOk || (rootOk && "/"!=u.path())))
3308 QString sect(getSect(u.path()));
3310 if(itsRoot)
3312 if((isSysFolder(sect) || isUserFolder(sect)) &&
3313 (itsFolders[FOLDER_SYS].fontMap.end()==itsFolders[FOLDER_SYS].fontMap.find(sect)))
3314 //CPD: TODO: || it has a font specified! e.g. fonts:/System/Times -> even in have a
3315 // fonts:/System font, redirect should still happen
3317 redirection(getRedirect(u));
3318 finished();
3319 return false;
3322 else
3323 if(!isSysFolder(sect) && !isUserFolder(sect) && !isAllFolder(sect) )
3325 if(logError)
3326 error(KIO::ERR_SLAVE_DEFINED, i18n("Please specify \"%1\" or \"%2\".",
3327 i18n(KFI_KIO_FONTS_USER), i18n(KFI_KIO_FONTS_SYS)));
3328 return false;
3332 return true;
3335 bool CKioFonts::checkAllowed(const KUrl &u)
3337 if (KFI_KIO_FONTS_PROTOCOL==u.protocol())
3339 QString ds(Misc::dirSyntax(u.path()));
3341 if(ds==QString(QChar('/')+i18n(KFI_KIO_FONTS_USER)+QChar('/')) ||
3342 ds==QString(QChar('/')+i18n(KFI_KIO_FONTS_SYS)+QChar('/')) ||
3343 ds==QString(QChar('/')+QString::fromLatin1(KFI_KIO_FONTS_USER)+QChar('/')) ||
3344 ds==QString(QChar('/')+QString::fromLatin1(KFI_KIO_FONTS_SYS)+QChar('/')))
3346 error(KIO::ERR_SLAVE_DEFINED,
3347 i18n("Sorry, you cannot rename, move, copy, or delete either \"%1\" or \"%2\".",
3348 i18n(KFI_KIO_FONTS_USER), i18n(KFI_KIO_FONTS_SYS))); \
3349 return false;
3353 return true;
3357 // Create an AFM from a Type 1 (pfa/pfb) font and its PFM file...
3358 void CKioFonts::createAfm(const QString &file, bool nrs)
3360 if(nrs && itsPasswd.isEmpty())
3361 return;
3363 bool type1=isAType1(file),
3364 pfm=!type1 && isAPfm(file); // No point checking if is pfm if its a type1
3366 if(type1 || pfm)
3368 // pf2afm wants files with lowercase extension, so just check for lowercase!
3369 // -- when a font is installed, the extension is converted to lowercase anyway...
3370 QString afm=getMatch(file, "afm");
3372 if(afm.isEmpty()) // No point creating if AFM already exists!
3374 QString pfm,
3377 if(type1) // Its a Type1, so look for existing PFM
3379 pfm=getMatch(file, "pfm");
3380 t1=file;
3382 else // Its a PFM, so look for existing Type1
3384 t1=getMatch(file, "pfa");
3385 if(t1.isEmpty())
3386 t1=getMatch(file, "pfb");
3387 pfm=file;
3390 if(!t1.isEmpty() && !pfm.isEmpty()) // Do we have both Type1 and PFM?
3392 QString name(t1.left(t1.length()-4)); // pf2afm wants name without extension...
3394 if(nrs)
3395 doRootCmd(TCommand(KFI::CMD_CREATE_AFM, name));
3396 else
3397 Misc::doCmd("pf2afm", QFile::encodeName(name));
3403 int CKioFonts::reconfigTimeout()
3405 return hasMetaData(KFI_KIO_TIMEOUT)
3406 ? metaData(KFI_KIO_TIMEOUT).toInt()
3407 : DEFAULT_TIMEOUT;
3410 void CKioFonts::TFolder::setLocation(const QString &l, bool sys)
3412 location=l;
3413 delete disabled;
3414 disabled=new CDisabledFonts(sys);