diff options
Diffstat (limited to 'kcontrol/kfontinst/kio')
-rw-r--r-- | kcontrol/kfontinst/kio/KioFonts.cpp | 2534 | ||||
-rw-r--r-- | kcontrol/kfontinst/kio/KioFonts.h | 161 | ||||
-rw-r--r-- | kcontrol/kfontinst/kio/Makefile.am | 20 | ||||
-rw-r--r-- | kcontrol/kfontinst/kio/folder.desktop | 80 | ||||
-rw-r--r-- | kcontrol/kfontinst/kio/fonts.desktop | 85 | ||||
-rw-r--r-- | kcontrol/kfontinst/kio/fonts.protocol | 18 | ||||
-rw-r--r-- | kcontrol/kfontinst/kio/package.desktop | 74 | ||||
-rw-r--r-- | kcontrol/kfontinst/kio/system-folder.desktop | 79 |
8 files changed, 3051 insertions, 0 deletions
diff --git a/kcontrol/kfontinst/kio/KioFonts.cpp b/kcontrol/kfontinst/kio/KioFonts.cpp new file mode 100644 index 000000000..e5bc2d6af --- /dev/null +++ b/kcontrol/kfontinst/kio/KioFonts.cpp @@ -0,0 +1,2534 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Class Name : KFI::CKioFonts +// Author : Craig Drummond +// Project : K Font Installer +// Creation Date : 05/03/2003 +// Version : $Revision$ $Date$ +// +//////////////////////////////////////////////////////////////////////////////// +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//////////////////////////////////////////////////////////////////////////////// +// (C) Craig Drummond, 2003, 2004 +//////////////////////////////////////////////////////////////////////////////// + +/*************************************************************************** + + NOTE: Large sections of this code are copied from kio_file + -- can't just inherit from kio_file as kio_file uses "error(...); + return;" So there is no way to know if an error occured! + + ***************************************************************************/ + +#include "KioFonts.h" +#include <stdlib.h> +#include <pwd.h> +#include <grp.h> +#include <sys/types.h> +#include <utime.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <unistd.h> +#include <fcntl.h> +#include <kio/global.h> +#include <kio/ioslave_defaults.h> +#include <kio/netaccess.h> +#include <kio/slaveinterface.h> +#include <kio/connection.h> +#include <qtextstream.h> +#include <kmimetype.h> +#include <kmessagebox.h> +#include <kprocess.h> +#include <qdir.h> +#include <qdatastream.h> +#include <qregexp.h> +#include <kinstance.h> +#include <klargefile.h> +#include <ktempfile.h> +#include <kdesu/su.h> +#include <kprocess.h> +#include <kdebug.h> +#include <ktar.h> +#include <kxftconfig.h> +#include <fontconfig/fontconfig.h> +#include "KfiConstants.h" +#include "FcEngine.h" +#include "Misc.h" +#include <X11/Xlib.h> +#include <fixx11h.h> +#include <ctype.h> + +//#define KFI_FORCE_DEBUG_TO_STDERR + +#ifdef KFI_FORCE_DEBUG_TO_STDERR + +#include <qtextstream.h> +QTextOStream ostr(stderr); +#define KFI_DBUG ostr << "[" << (int)(getpid()) << "] " + +#else + +#define KFI_DBUG kdDebug() << "[" << (int)(getpid()) << "] " + +#endif + +#define MAX_IPC_SIZE (1024*32) +#define TIMEOUT 2 // Time between last mod and writing files... +#define MAX_NEW_FONTS 50 // #fonts that can be installed before automatically configuring (related to above) +#define FC_CACHE_CMD "fc-cache" + +static const char * constMultipleExtension=".fonts.tar.gz"; // Fonts that have multiple files are returned as a .tar.gz! +static const int constMaxLastDestTime=5; +static const int constMaxFcCheckTime=10; + +extern "C" +{ + KDE_EXPORT int kdemain(int argc, char **argv); +} + +int kdemain(int argc, char **argv) +{ + if (argc != 4) + { + fprintf(stderr, "Usage: kio_" KFI_KIO_FONTS_PROTOCOL " protocol domain-socket1 domain-socket2\n"); + exit(-1); + } + + KLocale::setMainCatalogue(KFI_CATALOGUE); + + KInstance instance("kio_" KFI_KIO_FONTS_PROTOCOL); + KFI::CKioFonts slave(argv[2], argv[3]); + + slave.dispatchLoop(); + + return 0; +} + +namespace KFI +{ + +inline bool isSysFolder(const QString §) +{ + return i18n(KFI_KIO_FONTS_SYS)==sect || KFI_KIO_FONTS_SYS==sect; +} + +inline bool isUserFolder(const QString §) +{ + return i18n(KFI_KIO_FONTS_USER)==sect || KFI_KIO_FONTS_USER==sect; +} + +static QString removeMultipleExtension(const KURL &url) +{ + QString fname(url.fileName()); + int pos; + + if(-1!=(pos=fname.findRev(QString::fromLatin1(constMultipleExtension)))) + fname=fname.left(pos); + + return fname; +} + +static QString modifyName(const QString &fname) +{ + static const char constSymbols[]={ '-', ' ', ':', 0 }; + + QString rv(fname); + int dotPos=rv.findRev('.'); + + if(-1!=dotPos) + { + unsigned int rvLen=rv.length(); + + for(unsigned int i=dotPos+1; i<rvLen; ++i) + rv[i]=rv[i].lower(); + } + + for(int s=0; constSymbols[s]; ++s) + rv=rv.replace(constSymbols[s], '_'); + + return rv; +} + +static int getSize(const QCString &file) +{ + KDE_struct_stat buff; + + if(-1!=KDE_lstat(file, &buff)) + { + if (S_ISLNK(buff.st_mode)) + { + char buffer2[1000]; + int n=readlink(file, buffer2, 1000); + if(n!= -1) + buffer2[n]='\0'; + + if(-1==KDE_stat(file, &buff)) + return -1; + } + return buff.st_size; + } + + return -1; +} + +static int getFontSize(const QString &file) +{ + int size=0; + + KURL::List urls; + QStringList files; + + Misc::getAssociatedUrls(KURL(file), urls); + + files.append(file); + + if(urls.count()) + { + KURL::List::Iterator uIt, + uEnd=urls.end(); + + for(uIt=urls.begin(); uIt!=uEnd; ++uIt) + files.append((*uIt).path()); + } + + QStringList::Iterator it(files.begin()), + end(files.end()); + + for(; it!=end; ++it) + { + int s=getSize(QFile::encodeName(*it)); + + if(s>-1) + size+=s; + } + + return size; +} + +static int getSize(QValueList<FcPattern *> &patterns) +{ + QValueList<FcPattern *>::Iterator it, + end=patterns.end(); + int size=0; + + for(it=patterns.begin(); it!=end; ++it) + size+=getFontSize(CFcEngine::getFcString(*it, FC_FILE)); + + return size; +} + +static void addAtom(KIO::UDSEntry &entry, unsigned int ID, long l, const QString &s=QString::null) +{ + KIO::UDSAtom atom; + atom.m_uds = ID; + atom.m_long = l; + atom.m_str = s; + entry.append(atom); +} + +static bool createFolderUDSEntry(KIO::UDSEntry &entry, const QString &name, const QString &path, bool sys) +{ + KFI_DBUG << "createFolderUDSEntry " << name << ' ' << path << ' ' << sys << ' ' << endl; + + KDE_struct_stat buff; + QCString cPath(QFile::encodeName(path)); + + entry.clear(); + + if(-1!=KDE_lstat(cPath, &buff)) + { + addAtom(entry, KIO::UDS_NAME, 0, name); + + if (S_ISLNK(buff.st_mode)) + { + KFI_DBUG << path << " is a link" << endl; + + char buffer2[1000]; + int n=readlink(cPath, buffer2, 1000); + if(n!= -1) + buffer2[n]='\0'; + + addAtom(entry, KIO::UDS_LINK_DEST, 0, QString::fromLocal8Bit(buffer2)); + + if(-1==KDE_stat(cPath, &buff)) + { + // It is a link pointing to nowhere + addAtom(entry, KIO::UDS_FILE_TYPE, S_IFMT - 1); + addAtom(entry, KIO::UDS_ACCESS, S_IRWXU | S_IRWXG | S_IRWXO); + addAtom(entry, KIO::UDS_SIZE, 0); + goto notype; + } + } + + addAtom(entry, KIO::UDS_FILE_TYPE, buff.st_mode&S_IFMT); + addAtom(entry, KIO::UDS_ACCESS, buff.st_mode&07777); + addAtom(entry, KIO::UDS_SIZE, buff.st_size); + + notype: + addAtom(entry, KIO::UDS_MODIFICATION_TIME, buff.st_mtime); + + struct passwd *user = getpwuid(buff.st_uid); + addAtom(entry, KIO::UDS_USER, 0, user ? user->pw_name : QString::number(buff.st_uid).latin1()); + + struct group *grp = getgrgid(buff.st_gid); + addAtom(entry, KIO::UDS_GROUP, 0, grp ? grp->gr_name : QString::number(buff.st_gid).latin1()); + + addAtom(entry, KIO::UDS_ACCESS_TIME, buff.st_atime); + addAtom(entry, KIO::UDS_MIME_TYPE, 0, sys + ? KFI_KIO_FONTS_PROTOCOL"/system-folder" + : KFI_KIO_FONTS_PROTOCOL"/folder"); + addAtom(entry, KIO::UDS_GUESSED_MIME_TYPE, 0, "application/octet-stream"); + QString url(KFI_KIO_FONTS_PROTOCOL+QString::fromLatin1(":/")); + return true; + } + else if (sys && !Misc::root()) // Default system fonts folder does not actually exist yet! + { + KFI_DBUG << "Default system folder (" << path << ") does not yet exist, so create dummy entry" << endl; + addAtom(entry, KIO::UDS_NAME, 0, name); + addAtom(entry, KIO::UDS_FILE_TYPE, S_IFDIR); + addAtom(entry, KIO::UDS_ACCESS, 0744); + addAtom(entry, KIO::UDS_USER, 0, "root"); + addAtom(entry, KIO::UDS_GROUP, 0, "root"); + addAtom(entry, KIO::UDS_MIME_TYPE, 0, KFI_KIO_FONTS_PROTOCOL"/system-folder"); + addAtom(entry, KIO::UDS_GUESSED_MIME_TYPE, 0, "application/octet-stream"); + + return true; + } + + + return false; +} + +static bool createFontUDSEntry(KIO::UDSEntry &entry, const QString &name, QValueList<FcPattern *> &patterns, bool sys) +{ + KFI_DBUG << "createFontUDSEntry " << name << ' ' << patterns.count() << endl; + + bool multiple=true; + + if(1==patterns.count()) // Only one font file, but are there any .pfm or .afm files? + { + KURL::List urls; + + Misc::getAssociatedUrls(KURL(CFcEngine::getFcString(patterns.first(), FC_FILE)), urls); + + if(0==urls.count()) + multiple=false; + } + + // + // In case of mixed bitmap/scalable - prefer scalable + QValueList<FcPattern *> sortedPatterns; + QValueList<FcPattern *>::Iterator it, + end(patterns.end()); + FcBool b=FcFalse; + + for(it=patterns.begin(); it!=end; ++it) + if(FcResultMatch==FcPatternGetBool(*it, FC_SCALABLE, 0, &b) && b) + sortedPatterns.prepend(*it); + else + sortedPatterns.append(*it); + + end=sortedPatterns.end(); + entry.clear(); + addAtom(entry, KIO::UDS_SIZE, getSize(patterns)); + + for(it=sortedPatterns.begin(); it!=end; ++it) + { + QString path(CFcEngine::getFcString(*it, FC_FILE)); + QCString cPath(QFile::encodeName(path)); + KDE_struct_stat buff; + + if(-1!=KDE_lstat(cPath, &buff)) + { + addAtom(entry, KIO::UDS_NAME, 0, name); + + if (S_ISLNK(buff.st_mode)) + { + KFI_DBUG << path << " is a link" << endl; + + char buffer2[1000]; + int n=readlink(cPath, buffer2, 1000); + + if(n!= -1) + buffer2[n]='\0'; + + addAtom(entry, KIO::UDS_LINK_DEST, 0, QString::fromLocal8Bit(buffer2)); + + if(-1==KDE_stat(cPath, &buff)) + { + // It is a link pointing to nowhere + addAtom(entry, KIO::UDS_FILE_TYPE, S_IFMT - 1); + addAtom(entry, KIO::UDS_ACCESS, S_IRWXU | S_IRWXG | S_IRWXO); + goto notype; + } + } + + addAtom(entry, KIO::UDS_FILE_TYPE, buff.st_mode&S_IFMT); + addAtom(entry, KIO::UDS_ACCESS, buff.st_mode&07777); + + notype: + addAtom(entry, KIO::UDS_MODIFICATION_TIME, buff.st_mtime); + + struct passwd *user = getpwuid(buff.st_uid); + addAtom(entry, KIO::UDS_USER, 0, user ? user->pw_name : QString::number(buff.st_uid).latin1()); + + struct group *grp = getgrgid(buff.st_gid); + addAtom(entry, KIO::UDS_GROUP, 0, grp ? grp->gr_name : QString::number(buff.st_gid).latin1()); + + addAtom(entry, KIO::UDS_ACCESS_TIME, buff.st_atime); + addAtom(entry, KIO::UDS_MIME_TYPE, 0, KMimeType::findByPath(path, 0, true)->name()); + addAtom(entry, KIO::UDS_GUESSED_MIME_TYPE, 0, "application/octet-stream"); + + QString url(KFI_KIO_FONTS_PROTOCOL+QString::fromLatin1(":/")); + + if(!Misc::root()) + { + url+=sys ? i18n(KFI_KIO_FONTS_SYS) : i18n(KFI_KIO_FONTS_USER); + url+=QString::fromLatin1("/"); + } + if(multiple) + url+=name+QString::fromLatin1(constMultipleExtension); + else + url+=Misc::getFile(path); + addAtom(entry, KIO::UDS_URL, 0, url); + return true; // This file was OK, so use its values... + } + } + return false; +} + +enum EUrlStatus +{ + BAD_URL, + URL_OK, + REDIRECT_URL +}; + +static KURL getRedirect(const KURL &u) +{ + // Go from fonts:/System to fonts:/ + + KURL redirect(u); + QString path(u.path()), + sect(CKioFonts::getSect(path)); + + path.remove(sect); + path.replace("//", "/"); + redirect.setPath(path); + + KFI_DBUG << "Redirect from " << u.path() << " to " << redirect.path() << endl; + return redirect; +} + +static bool nonRootSys(const KURL &u) +{ + return !Misc::root() && isSysFolder(CKioFonts::getSect(u.path())); +} + +static QString getFontFolder(const QString &defaultDir, const QString &root, QStringList &dirs) +{ + if(dirs.contains(defaultDir)) + return defaultDir; + else + { + QStringList::Iterator it, + end=dirs.end(); + bool found=false; + + for(it=dirs.begin(); it!=end && !found; ++it) + if(0==(*it).find(root)) + return *it; + } + + return QString::null; +} + +static bool writeAll(int fd, const char *buf, size_t len) +{ + while(len>0) + { + ssize_t written=write(fd, buf, len); + if (written<0 && EINTR!=errno) + return false; + buf+=written; + len-=written; + } + return true; +} + +static bool checkExt(const char *fname, const char *ext) +{ + unsigned int len=strlen(fname); + + return len>4 ? (fname[len-4]=='.' && tolower(fname[len-3])==ext[0] && tolower(fname[len-2])==ext[1] && + tolower(fname[len-1])==ext[2]) + : false; +} + +static bool isAAfm(const QString &fname) +{ + if(checkExt(QFile::encodeName(fname), "afm")) // CPD? Is this a necessary check? + { + QFile file(fname); + + if(file.open(IO_ReadOnly)) + { + QTextStream stream(&file); + QString line; + + for(int lc=0; lc<30 && !stream.atEnd(); ++lc) + { + line=stream.readLine(); + + if(line.contains("StartFontMetrics")) + { + file.close(); + return true; + } + } + + file.close(); + } + } + + return false; +} + +static bool isAPfm(const QString &fname) +{ + bool ok=false; + + // I know extension checking is bad, but Ghostscript's pf2afm requires the pfm file to + // have the .pfm extension... + if(checkExt(QFile::encodeName(fname), "pfm")) + { + // + // OK, the extension matches, so perform a little contents checking... + FILE *f=fopen(QFile::encodeName(fname).data(), "r"); + + if(f) + { + static const unsigned long constCopyrightLen = 60; + static const unsigned long constTypeToExt = 49; + static const unsigned long constExtToFname = 20; + static const unsigned long constExtLen = 30; + static const unsigned long constFontnameMin = 75; + static const unsigned long constFontnameMax = 512; + + unsigned short version=0, + type=0, + extlen=0; + unsigned long length=0, + fontname=0, + fLength=0; + + fseek(f, 0, SEEK_END); + fLength=ftell(f); + fseek(f, 0, SEEK_SET); + + if(2==fread(&version, 1, 2, f) && // Read version + 4==fread(&length, 1, 4, f) && // length... + length==fLength && + 0==fseek(f, constCopyrightLen, SEEK_CUR) && // Skip copyright notice... + 2==fread(&type, 1, 2, f) && + 0==fseek(f, constTypeToExt, SEEK_CUR) && + 2==fread(&extlen, 1, 2, f) && + extlen==constExtLen && + 0==fseek(f, constExtToFname, SEEK_CUR) && + 4==fread(&fontname, 1, 4, f) && + fontname>constFontnameMin && fontname<constFontnameMax) + ok=true; + fclose(f); + } + } + + return ok; +} + +// +// This function is *only* used for the generation of AFMs from PFMs. +static bool isAType1(const QString &fname) +{ + static const char * constStr="%!PS-AdobeFont-"; + static const unsigned int constStrLen=15; + static const unsigned int constPfbOffset=6; + static const unsigned int constPfbLen=constStrLen+constPfbOffset; + + QCString name(QFile::encodeName(fname)); + char buffer[constPfbLen]; + bool match=false; + + if(checkExt(name, "pfa")) + { + FILE *f=fopen(name.data(), "r"); + + if(f) + { + if(constStrLen==fread(buffer, 1, constStrLen, f)) + match=0==memcmp(buffer, constStr, constStrLen); + fclose(f); + } + } + else if(checkExt(name, "pfb")) + { + static const char constPfbMarker=0x80; + + FILE *f=fopen(name.data(), "r"); + + if(f) + { + if(constPfbLen==fread(buffer, 1, constPfbLen, f)) + match=buffer[0]==constPfbMarker && 0==memcmp(&buffer[constPfbOffset], constStr, constStrLen); + fclose(f); + } + } + + return match; +} + +static QString getMatch(const QString &file, const char *extension) +{ + QString f(Misc::changeExt(file, extension)); + + return Misc::fExists(f) ? f : QString::null; +} + +inline bool isHidden(const KURL &u) +{ + return QChar('.')==u.fileName()[0]; +} + +struct FontList +{ + struct Path + { + Path(const QString &p=QString::null) : orig(p) { } + + QString orig, + modified; + + bool operator==(const Path &p) const { return p.orig==orig; } + }; + + FontList(const QString &n=QString::null, const QString &p=QString::null) : name(n) { if(!p.isEmpty()) paths.append(Path(p)); } + + QString name; + QValueList<Path> paths; + + bool operator==(const FontList &f) const { return f.name==name; } +}; + +// +// This function returns a set of maping of from -> to for copy/move operations +static bool getFontList(const QStringList &files, QMap<QString, QString> &map) +{ + // + // First of all create a list of font files, and their paths + QStringList::ConstIterator it=files.begin(), + end=files.end(); + QValueList<FontList> list; + + for(;it!=end; ++it) + { + QString name(Misc::getFile(*it)), + path(Misc::getDir(*it)); + QValueList<FontList>::Iterator entry=list.find(FontList(name)); + + if(entry!=list.end()) + { + if(!(*entry).paths.contains(path)) + (*entry).paths.append(path); + } + else + list.append(FontList(name, path)); + } + + QValueList<FontList>::Iterator fIt(list.begin()), + fEnd(list.end()); + + for(; fIt!=fEnd; ++fIt) + { + QValueList<FontList::Path>::Iterator pBegin((*fIt).paths.begin()), + pIt(++pBegin), + pEnd((*fIt).paths.end()); + --pBegin; + + if((*fIt).paths.count()>1) + { + // There's more than 1 file with the same name, but in a different locations + // therefore, take the unique part of the path, and replace / with _ + // e.g. + // /usr/X11R6/lib/X11/fonts/75dpi/times.pcf.gz + // /usr/X11R6/lib/X11/fonts/100dpi/times.pcf.gz + // + // Will produce: + // 75dpi_times.pcf.gz + // 100dpi_times.pcf.gz + unsigned int beginLen((*pBegin).orig.length()); + + for(; pIt!=pEnd; ++pIt) + { + unsigned int len=QMIN((*pIt).orig.length(), beginLen); + + for(unsigned int i=0; i<len; ++i) + if((*pIt).orig[i]!=(*pBegin).orig[i]) + { + (*pIt).modified=(*pIt).orig.mid(i); + (*pIt).modified=(*pIt).modified.replace('/', '_'); + if((*pBegin).modified.isEmpty()) + { + (*pBegin).modified=(*pBegin).orig.mid(i); + (*pBegin).modified=(*pBegin).modified.replace('/', '_'); + } + break; + } + } + } + for(pIt=(*fIt).paths.begin(); pIt!=pEnd; ++pIt) + map[(*pIt).orig+(*fIt).name]=(*pIt).modified+(*fIt).name; + } + + return list.count() ? true : false; +} + +CKioFonts::CKioFonts(const QCString &pool, const QCString &app) + : KIO::SlaveBase(KFI_KIO_FONTS_PROTOCOL, pool, app), + itsRoot(Misc::root()), + itsUsingFcFpe(false), + itsUsingXfsFpe(false), + itsHasSys(false), + itsAddToSysFc(false), + itsFontChanges(0), + itsLastDest(DEST_UNCHANGED), + itsLastDestTime(0), + itsLastFcCheckTime(0), + itsFontList(NULL) +{ + KFI_DBUG << "Constructor" << endl; + + // Set core dump size to 0 because we will have + // root's password in memory. + struct rlimit rlim; + rlim.rlim_cur=rlim.rlim_max=0; + itsCanStorePasswd=setrlimit(RLIMIT_CORE, &rlim) ? false : true; + + // + // Check with fontconfig for folder locations... + // + // 1. Get list of fontconfig dirs + // 2. For user, look for any starting with $HOME - but prefer $HOME/.fonts + // 3. For system, look for any starting with /usr/local/share - but prefer /usr/local/share/fonts + // 4. If either are not found, then add to local.conf / .fonts.conf + + FcStrList *list=FcConfigGetFontDirs(FcInitLoadConfigAndFonts()); + QStringList dirs; + FcChar8 *dir; + + while((dir=FcStrListNext(list))) + dirs.append(Misc::dirSyntax((const char *)dir)); + + EFolder mainFolder=FOLDER_SYS; + + if(!itsRoot) + { + QString home(Misc::dirSyntax(QDir::homeDirPath())), + defaultDir(Misc::dirSyntax(QDir::homeDirPath()+"/.fonts/")), + dir(getFontFolder(defaultDir, home, dirs)); + + if(dir.isEmpty()) // Then no $HOME/ was found in fontconfigs dirs! + { + KXftConfig xft(KXftConfig::Dirs, false); + xft.addDir(defaultDir); + xft.apply(); + dir=defaultDir; + } + mainFolder=FOLDER_USER; + itsFolders[FOLDER_USER].location=dir; + } + + QString sysDefault("/usr/local/share/fonts/"), + sysDir(getFontFolder(sysDefault, "/usr/local/share/", dirs)); + + if(sysDir.isEmpty()) + { + if(itsRoot) + { + KXftConfig xft(KXftConfig::Dirs, true); + xft.addDir(sysDefault); + xft.apply(); + } + else + itsAddToSysFc=true; + + sysDir=sysDefault; + } + + itsFolders[FOLDER_SYS].location=sysDir; + + // + // Ensure exists + if(!Misc::dExists(itsFolders[mainFolder].location)) + Misc::createDir(itsFolders[mainFolder].location); + + // + // Work out best params to send to kfontinst + + // ...determine if X already knows about the system font path... + Display *xDisplay=XOpenDisplay(NULL); + + if(xDisplay) + { + int numPaths=0; + char **paths=XGetFontPath(xDisplay, &numPaths); + + if(numPaths>0) + for(int path=0; path<numPaths && !itsUsingFcFpe; ++path) + if(paths[path][0]=='/') + { + if(Misc::dirSyntax(paths[path])==itsFolders[FOLDER_SYS].location) + itsHasSys=true; + } + else + { + QString str(paths[path]); + + str.replace(QRegExp("\\s*"), ""); + + if(0==str.find("unix/:")) + itsUsingXfsFpe=true; + else if("fontconfig"==str) + itsUsingFcFpe=true; + } + XFreeFontPath(paths); + XCloseDisplay(xDisplay); + } +} + +CKioFonts::~CKioFonts() +{ + KFI_DBUG << "Destructor" << endl; + doModified(); +} + +void CKioFonts::listDir(const KURL &url) +{ + KFI_DBUG << "listDir " << url.path() << endl; + + if(updateFontList() && checkUrl(url, true)) + { + KIO::UDSEntry entry; + int size=0; + + if(itsRoot || QStringList::split('/', url.path(), false).count()!=0) + { + EFolder folder=getFolder(url); + + totalSize(itsFolders[folder].fontMap.count()); + if(itsFolders[folder].fontMap.count()) + { + QMap<QString, QValueList<FcPattern *> >::Iterator it=itsFolders[folder].fontMap.begin(), + end=itsFolders[folder].fontMap.end(); + + for ( ; it != end; ++it) + { + entry.clear(); + if(createFontUDSEntry(entry, it.key(), it.data(), FOLDER_SYS==folder)) + listEntry(entry, false); + } + } + } + else + { + size=2; + totalSize(size); + createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_USER), itsFolders[FOLDER_USER].location, false); + listEntry(entry, false); + createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_SYS), itsFolders[FOLDER_SYS].location, true); + listEntry(entry, false); + } + + listEntry(size ? entry : KIO::UDSEntry(), true); + finished(); + } + + KFI_DBUG << "listDir - finished!" << endl; +} + +void CKioFonts::stat(const KURL &url) +{ + KFI_DBUG << "stat " << url.prettyURL() << endl; + + if(updateFontList() && checkUrl(url, true)) + { + QString path(url.path(-1)); + + if(path.isEmpty()) + { + error(KIO::ERR_COULD_NOT_STAT, url.prettyURL()); + return; + } + + QStringList pathList(QStringList::split('/', path, false)); + KIO::UDSEntry entry; + bool err=false; + + switch(pathList.count()) + { + case 0: + err=!createFolderUDSEntry(entry, i18n("Fonts"), itsFolders[itsRoot ? FOLDER_SYS : FOLDER_USER].location, false); + break; + case 1: + if(itsRoot) + err=!createStatEntry(entry, url, FOLDER_SYS); + else + if(isUserFolder(pathList[0])) + err=!createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_USER), itsFolders[FOLDER_USER].location, false); + else if(isSysFolder(pathList[0])) + err=!createFolderUDSEntry(entry, i18n(KFI_KIO_FONTS_SYS), itsFolders[FOLDER_USER].location, true); + else + { + error(KIO::ERR_SLAVE_DEFINED, + i18n("Please specify \"%1\" or \"%2\".").arg(i18n(KFI_KIO_FONTS_USER)).arg(i18n(KFI_KIO_FONTS_SYS))); + return; + } + break; + default: + err=!createStatEntry(entry, url, getFolder(url)); + } + + if(err) + { + error(KIO::ERR_DOES_NOT_EXIST, url.prettyURL()); + return; + } + + statEntry(entry); + finished(); + } +} + +bool CKioFonts::createStatEntry(KIO::UDSEntry &entry, const KURL &url, EFolder folder) +{ + KFI_DBUG << "createStatEntry " << url.path() << endl; + + QMap<QString, QValueList<FcPattern *> >::Iterator it=getMap(url); + + if(it!=itsFolders[folder].fontMap.end()) + return createFontUDSEntry(entry, it.key(), it.data(), FOLDER_SYS==folder); + return false; +} + +void CKioFonts::get(const KURL &url) +{ + KFI_DBUG << "get " << url.path() << " query:" << url.query() << endl; + + bool thumb="1"==metaData("thumbnail"); + QStringList srcFiles; + + if(updateFontList() && checkUrl(url) && getSourceFiles(url, srcFiles)) // Any error will be logged in getSourceFiles + { + // + // The thumbnail job always donwloads non-local files to /tmp/... and passes this file name to the thumbnail + // creator. However, in the case of fonts which are split among many files, this wont work. Therefore, when the + // thumbnail code asks for the font to donwload, just return the URL used. This way the font-thumbnail creator can + // read this and just ask Xft/fontconfig for the font data. + if(thumb) + { + QByteArray array; + QTextOStream stream(array); + + emit mimeType("text/plain"); + + KFI_DBUG << "hasMetaData(\"thumbnail\"), so return: " << url.prettyURL() << endl; + + stream << url.prettyURL(); + totalSize(array.size()); + data(array); + processedSize(array.size()); + data(QByteArray()); + processedSize(array.size()); + finished(); + return; + } + + QString realPath, + useMime; + KDE_struct_stat buff; + bool multiple=false; + + if(1==srcFiles.count()) + realPath=srcFiles.first(); + else // Font is made up of multiple files - so create .tar.gz of them all! + { + KTempFile tmpFile; + KTar tar(tmpFile.name(), "application/x-gzip"); + + tmpFile.setAutoDelete(false); + realPath=tmpFile.name(); + + if(tar.open(IO_WriteOnly)) + { + QMap<QString, QString> map; + + getFontList(srcFiles, map); + + QMap<QString, QString>::Iterator fIt(map.begin()), + fEnd(map.end()); + + // + // Iterate through created list, and add to tar archive + for(; fIt!=fEnd; ++fIt) + tar.addLocalFile(fIt.key(), fIt.data()); + + multiple=true; + tar.close(); + } + } + + QCString realPathC(QFile::encodeName(realPath)); + KFI_DBUG << "real: " << realPathC << endl; + + if (-2==KDE_stat(realPathC.data(), &buff)) + error(EACCES==errno ? KIO::ERR_ACCESS_DENIED : KIO::ERR_DOES_NOT_EXIST, url.prettyURL()); + else if (S_ISDIR(buff.st_mode)) + error(KIO::ERR_IS_DIRECTORY, url.prettyURL()); + else if (!S_ISREG(buff.st_mode)) + error(KIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyURL()); + else + { + int fd = KDE_open(realPathC.data(), O_RDONLY); + + if (fd < 0) + error(KIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyURL()); + else + { + // Determine the mimetype of the file to be retrieved, and emit it. + // This is mandatory in all slaves (for KRun/BrowserRun to work). + emit mimeType(useMime.isEmpty() ? KMimeType::findByPath(realPathC, buff.st_mode, true)->name() : useMime); + + totalSize(buff.st_size); + + KIO::filesize_t processed=0; + char buffer[MAX_IPC_SIZE]; + QByteArray array; + + while(1) + { + int n=::read(fd, buffer, MAX_IPC_SIZE); + if (-1==n) + { + if (errno == EINTR) + continue; + error(KIO::ERR_COULD_NOT_READ, url.prettyURL()); + close(fd); + if(multiple) + ::unlink(realPathC); + return; + } + if (0==n) + break; // Finished + + array.setRawData(buffer, n); + data(array); + array.resetRawData(buffer, n); + + processed+=n; + processedSize(processed); + } + + data(QByteArray()); + close(fd); + + processedSize(buff.st_size); + finished(); + } + } + if(multiple) + ::unlink(realPathC); + } +} + +void CKioFonts::put(const KURL &u, int mode, bool overwrite, bool resume) +{ + KFI_DBUG << "put " << u.path() << endl; + + if(isHidden(u)) + { + error(KIO::ERR_WRITE_ACCESS_DENIED, u.prettyURL()); + return; + } + + // updateFontList(); // CPD: dont update font list upon a put - is too slow. Just stat on filename! + + //checkUrl(u) // CPD: Don't need to check URL, as the call to "confirmUrl()" below will sort out any probs! + + KURL url(u); + bool changed=confirmUrl(url), + nrs=nonRootSys(url); + EFolder destFolder(getFolder(url)); + QString dest=itsFolders[destFolder].location+modifyName(url.fileName()), + passwd; + QCString destC=QFile::encodeName(dest); + KDE_struct_stat buffDest; + bool destExists=(KDE_lstat(destC.data(), &buffDest)!= -1); + + if (destExists && !overwrite && !resume) + { + error(KIO::ERR_FILE_ALREADY_EXIST, url.prettyURL()); + return; + } + + if(nrs) // Need to check can get root passwd before start download... + { + passwd=getRootPasswd(); + + if(passwd.isEmpty()) + { + error(KIO::ERR_SLAVE_DEFINED, i18n("Could not access \"%1\" folder.").arg(i18n(KFI_KIO_FONTS_SYS))); + return; + } + } + + // + // As we don't get passed a mime-type the following needs to happen: + // + // 1. Download to a temporary file + // 2. Check with FreeType that the file is a font, or that it is + // an AFM or PFM file + // 3. If its OK, then get the fonts "name" from + KTempFile tmpFile; + QCString tmpFileC(QFile::encodeName(tmpFile.name())); + + tmpFile.setAutoDelete(true); + + if(putReal(tmpFile.name(), tmpFileC, destExists, mode, resume)) + { + if(!checkFile(tmpFile.name())) // error logged in checkFile + return; + + if(nrs) // Ask root to copy the font... + { + QCString cmd; + + if(!Misc::dExists(itsFolders[destFolder].location)) + { + cmd+="mkdir "; + cmd+=QFile::encodeName(KProcess::quote(itsFolders[destFolder].location)); + cmd+=" && chmod 0755 "; + cmd+=QFile::encodeName(KProcess::quote(itsFolders[destFolder].location)); + cmd+=" && "; + } + cmd+="cp -f "; + cmd+=QFile::encodeName(KProcess::quote(tmpFileC)); + cmd+=" "; + cmd+=QFile::encodeName(KProcess::quote(destC)); + cmd+=" && chmod 0644 "; + cmd+=destC; + + if(!itsCanStorePasswd) + createRootRefreshCmd(cmd); + + // Get root to move this to fonts folder... + if(doRootCmd(cmd, passwd)) + { + modified(FOLDER_SYS); + createAfm(dest, true, passwd); + } + else + { + error(KIO::ERR_SLAVE_DEFINED, i18n("Could not access \"%1\" folder.").arg(i18n(KFI_KIO_FONTS_SYS))); + return; + } + } + else // Move it to our font folder... + { + tmpFile.setAutoDelete(false); + if(Misc::doCmd("mv", "-f", tmpFileC, destC)) + { + ::chmod(destC.data(), Misc::FILE_PERMS); + modified(FOLDER_USER); + createAfm(dest); + } + else + { + error(KIO::ERR_SLAVE_DEFINED, i18n("Could not access \"%1\" folder.").arg(i18n(KFI_KIO_FONTS_USER))); + return; + } + } + + finished(); + + if(changed) + itsLastDestTime=time(NULL); + } +} + +bool CKioFonts::putReal(const QString &destOrig, const QCString &destOrigC, bool origExists, + int mode, bool resume) +{ + bool markPartial=config()->readBoolEntry("MarkPartial", true); + QString dest; + + if (markPartial) + { + QString destPart(destOrig+QString::fromLatin1(".part")); + QCString destPartC(QFile::encodeName(destPart)); + + dest = destPart; + + KDE_struct_stat buffPart; + bool partExists=(-1!=KDE_stat(destPartC.data(), &buffPart)); + + if (partExists && !resume && buffPart.st_size>0) + { + // Maybe we can use this partial file for resuming + // Tell about the size we have, and the app will tell us + // if it's ok to resume or not. + resume=canResume(buffPart.st_size); + + if (!resume) + if (!::remove(destPartC.data())) + partExists = false; + else + { + error(KIO::ERR_CANNOT_DELETE_PARTIAL, destPart); + return false; + } + } + } + else + { + dest = destOrig; + if (origExists && !resume) + ::remove(destOrigC.data()); + // Catch errors when we try to open the file. + } + + QCString destC(QFile::encodeName(dest)); + + int fd; + + if (resume) + { + fd = KDE_open(destC.data(), O_RDWR); // append if resuming + KDE_lseek(fd, 0, SEEK_END); // Seek to end + } + else + { + // WABA: Make sure that we keep writing permissions ourselves, + // otherwise we can be in for a surprise on NFS. + fd = KDE_open(destC.data(), O_CREAT | O_TRUNC | O_WRONLY, -1==mode ? 0666 : mode | S_IWUSR | S_IRUSR); + } + + if (fd < 0) + { + error(EACCES==errno ? KIO::ERR_WRITE_ACCESS_DENIED : KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest); + return false; + } + + int result; + // Loop until we got 0 (end of data) + do + { + QByteArray buffer; + + dataReq(); // Request for data + result = readData(buffer); + if(result > 0 && !writeAll(fd, buffer.data(), buffer.size())) + { + if(ENOSPC==errno) // disk full + { + error(KIO::ERR_DISK_FULL, destOrig); + result = -2; // means: remove dest file + } + else + { + error(KIO::ERR_COULD_NOT_WRITE, destOrig); + result = -1; + } + } + } + while(result>0); + + if (result<0) + { + close(fd); + if (-1==result) + ::remove(destC.data()); + else if (markPartial) + { + KDE_struct_stat buff; + + if ((-1==KDE_stat(destC.data(), &buff)) || + (buff.st_size<config()->readNumEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE))) + ::remove(destC.data()); + } + ::exit(255); + } + + if (-1==fd) // we got nothing to write out, so we never opened the file + { + finished(); + return false; + } + + if (close(fd)) + { + error(KIO::ERR_COULD_NOT_WRITE, destOrig); + return false; + } + + // after full download rename the file back to original name + if (markPartial && ::rename(destC.data(), destOrigC.data())) + { + error(KIO::ERR_CANNOT_RENAME_PARTIAL, destOrig); + return false; + } + + return true; +} + +void CKioFonts::copy(const KURL &src, const KURL &d, int mode, bool overwrite) +{ + // + // Support: + // Copying to fonts:/ + // Copying from fonts:/ and file:/ + // + KFI_DBUG << "copy " << src.prettyURL() << " - " << d.prettyURL() << endl; + + if(isHidden(d)) + { + error(KIO::ERR_WRITE_ACCESS_DENIED, d.prettyURL()); + return; + } + + bool fromFonts=KFI_KIO_FONTS_PROTOCOL==src.protocol(); + + if((!fromFonts || updateFontList()) // CPD: dont update font list upon a copy from file - is too slow. Just stat on filename! + && checkUrl(src) && checkAllowed(src)) + { + //checkUrl(u) // CPD as per comment in ::put() + + QStringList srcFiles; + + if(getSourceFiles(src, srcFiles)) // Any error will be logged in getSourceFiles + { + KURL dest(d); + bool changed=confirmUrl(dest); + EFolder destFolder(getFolder(dest)); + QMap<QString, QString> map; + + if(!fromFonts) + map[src.path()]=src.fileName(); + + // As above, if copying from file, then only stat on dest filename, but if from fonts to fonts need to + // get the list of possible source files, etc. + if(fromFonts ? confirmMultiple(src, srcFiles, FOLDER_SYS==destFolder ? FOLDER_USER : FOLDER_SYS, OP_COPY) && + getFontList(srcFiles, map) && + checkDestFiles(src, map, dest, destFolder, overwrite) + : checkDestFile(src, dest, destFolder, overwrite) ) + { + if(nonRootSys(dest)) + { + QCString cmd; + int size=0; + + if(!Misc::dExists(itsFolders[destFolder].location)) + { + cmd+="mkdir "; + cmd+=QFile::encodeName(KProcess::quote(itsFolders[destFolder].location)); + cmd+=" && chmod 0755 "; + cmd+=QFile::encodeName(KProcess::quote(itsFolders[destFolder].location)); + cmd+=" && "; + } + + QMap<QString, QString>::Iterator fIt(map.begin()), + fEnd(map.end()); + + for(; fIt!=fEnd; ++fIt) + { + cmd+="cp -f "; + cmd+=QFile::encodeName(KProcess::quote(fIt.key())); + cmd+=" "; + cmd+=QFile::encodeName(KProcess::quote(itsFolders[destFolder].location+modifyName(fIt.data()))); + int s=getSize(QFile::encodeName(fIt.key())); + if(s>0) + size+=s; + if(++fIt!=fEnd) + cmd+=" && "; + --fIt; + } + + if(!itsCanStorePasswd) + createRootRefreshCmd(cmd); + + totalSize(size); + + QString passwd=getRootPasswd(); + + if(doRootCmd(cmd, passwd)) + { + modified(destFolder); + processedSize(size); + if(src.isLocalFile() && 1==srcFiles.count()) + createAfm(itsFolders[destFolder].location+modifyName(map.begin().data()), true, passwd); + } + else + { + error(KIO::ERR_SLAVE_DEFINED, i18n("Could not access \"%1\" folder.").arg(i18n(KFI_KIO_FONTS_SYS))); + return; + } + } + else + { + QMap<QString, QString>::Iterator fIt(map.begin()), + fEnd(map.end()); + + for(; fIt!=fEnd; ++fIt) + { + QCString realSrc(QFile::encodeName(fIt.key())), + realDest(QFile::encodeName(itsFolders[destFolder].location+modifyName(fIt.data()))); + KDE_struct_stat buffSrc; + + if(-1==KDE_stat(realSrc.data(), &buffSrc)) + { + error(EACCES==errno ? KIO::ERR_ACCESS_DENIED : KIO::ERR_DOES_NOT_EXIST, src.prettyURL()); + return; + } + + int srcFd=KDE_open(realSrc.data(), O_RDONLY); + + if (srcFd<0) + { + error(KIO::ERR_CANNOT_OPEN_FOR_READING, src.prettyURL()); + return; + } + + if(!Misc::dExists(itsFolders[destFolder].location)) + Misc::createDir(itsFolders[destFolder].location); + + // WABA: Make sure that we keep writing permissions ourselves, + // otherwise we can be in for a surprise on NFS. + int destFd=KDE_open(realDest.data(), O_CREAT | O_TRUNC | O_WRONLY, -1==mode ? 0666 : mode | S_IWUSR); + + if (destFd<0) + { + error(EACCES==errno ? KIO::ERR_WRITE_ACCESS_DENIED : KIO::ERR_CANNOT_OPEN_FOR_WRITING, dest.prettyURL()); + close(srcFd); + return; + } + + totalSize(buffSrc.st_size); + + KIO::filesize_t processed = 0; + char buffer[MAX_IPC_SIZE]; + QByteArray array; + + while(1) + { + int n=::read(srcFd, buffer, MAX_IPC_SIZE); + + if(-1==n) + { + if (errno == EINTR) + continue; + error(KIO::ERR_COULD_NOT_READ, src.prettyURL()); + close(srcFd); + close(destFd); + return; + } + if(0==n) + break; // Finished + + if(!writeAll(destFd, buffer, n)) + { + close(srcFd); + close(destFd); + if (ENOSPC==errno) // disk full + { + error(KIO::ERR_DISK_FULL, dest.prettyURL()); + remove(realDest.data()); + } + else + error(KIO::ERR_COULD_NOT_WRITE, dest.prettyURL()); + return; + } + + processed += n; + processedSize(processed); + } + + close(srcFd); + + if(close(destFd)) + { + error(KIO::ERR_COULD_NOT_WRITE, dest.prettyURL()); + return; + } + + ::chmod(realDest.data(), Misc::FILE_PERMS); + + // copy access and modification time + struct utimbuf ut; + + ut.actime = buffSrc.st_atime; + ut.modtime = buffSrc.st_mtime; + ::utime(realDest.data(), &ut); + + processedSize(buffSrc.st_size); + modified(destFolder); + } + + if(src.isLocalFile() && 1==srcFiles.count()) + createAfm(itsFolders[destFolder].location+modifyName(map.begin().data())); + } + + finished(); + + if(changed) + itsLastDestTime=time(NULL); + } + } + } +} + +void CKioFonts::rename(const KURL &src, const KURL &d, bool overwrite) +{ + KFI_DBUG << "rename " << src.prettyURL() << " - " << d.prettyURL() << ", " << overwrite << endl; + + if(src.directory()==d.directory()) + error(KIO::ERR_SLAVE_DEFINED, i18n("Sorry, fonts cannot be renamed.")); + else if(itsRoot) // Should never happen... + error(KIO::ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, KIO::CMD_RENAME)); + else + { + // + // Can't rename from/to file:/ -> therefore rename can only be from fonts:/System to fonts:/Personal, + // or vice versa. + + QStringList srcFiles; + + if(getSourceFiles(src, srcFiles)) // Any error will be logged in getSourceFiles + { + KURL dest(d); + bool changed=confirmUrl(dest); + EFolder destFolder(getFolder(dest)); + QMap<QString, QString> map; + + if(confirmMultiple(src, srcFiles, FOLDER_SYS==destFolder ? FOLDER_USER : FOLDER_SYS, OP_MOVE) && + getFontList(srcFiles, map) && + checkDestFiles(src, map, dest, destFolder, overwrite)) + { + QMap<QString, QString>::Iterator fIt(map.begin()), + fEnd(map.end()); + bool askPasswd=true, + toSys=FOLDER_SYS==destFolder; + QCString userId, + groupId, + destDir(QFile::encodeName(KProcess::quote(itsFolders[destFolder].location))); + + userId.setNum(toSys ? 0 : getuid()); + groupId.setNum(toSys ? 0 : getgid()); + + for(; fIt!=fEnd; ++fIt) + { + QCString cmd, + destFile(QFile::encodeName(KProcess::quote(itsFolders[destFolder].location+fIt.data()))); + + if(toSys && !Misc::dExists(itsFolders[destFolder].location)) + { + cmd+="mkdir "; + cmd+=destDir; + cmd+=" && "; + } + + cmd+="mv -f "; + cmd+=QFile::encodeName(KProcess::quote(fIt.key())); + cmd+=" "; + cmd+=destFile; + cmd+=" && chmod -f 0644 "; + cmd+=destFile; + cmd+=" && chown -f "; + cmd+=userId; + cmd+=":"; + cmd+=groupId; + cmd+=" "; + cmd+=destFile; + + QString sysDir, + userDir; + + if(FOLDER_SYS==destFolder) + { + sysDir=itsFolders[destFolder].location; + userDir=Misc::getDir(fIt.key()); + } + else + { + userDir=itsFolders[destFolder].location; + sysDir=Misc::getDir(fIt.key()); + } + + if(!itsCanStorePasswd) + createRootRefreshCmd(cmd, sysDir); + + if(doRootCmd(cmd, askPasswd)) + { + modified(FOLDER_SYS, true, sysDir); + modified(FOLDER_USER, true, userDir); + askPasswd=false; // Don't keep on asking for password... + } + else + { + error(KIO::ERR_SLAVE_DEFINED, i18n("Could not access \"%1\" folder.").arg(i18n(KFI_KIO_FONTS_SYS))); + return; + } + } + if(changed) + itsLastDestTime=time(NULL); + } + } + } +} + +void CKioFonts::del(const KURL &url, bool) +{ + KFI_DBUG << "del " << url.path() << endl; + + QValueList<FcPattern *> *entries; + + if(checkUrl(url) && checkAllowed(url) && + updateFontList() && (entries=getEntries(url)) && entries->count() && + confirmMultiple(url, entries, getFolder(url), OP_DELETE)) + { + QValueList<FcPattern *>::Iterator it, + end=entries->end(); + CDirList modifiedDirs; + bool clearList=KFI_KIO_NO_CLEAR!=url.query(); + + if(nonRootSys(url)) + { + QCString cmd("rm -f"); + + for(it=entries->begin(); it!=end; ++it) + { + QString file(CFcEngine::getFcString(*it, FC_FILE)); + + modifiedDirs.add(Misc::getDir(file)); + cmd+=" "; + cmd+=QFile::encodeName(KProcess::quote(file)); + + KURL::List urls; + + Misc::getAssociatedUrls(KURL(file), urls); + + if(urls.count()) + { + KURL::List::Iterator uIt, + uEnd=urls.end(); + + for(uIt=urls.begin(); uIt!=uEnd; ++uIt) + { + cmd+=" "; + cmd+=QFile::encodeName(KProcess::quote((*uIt).path())); + } + } + } + + if(!itsCanStorePasswd) + createRootRefreshCmd(cmd, modifiedDirs); + + if(doRootCmd(cmd)) + modified(FOLDER_SYS, clearList, modifiedDirs); + else + error(KIO::ERR_SLAVE_DEFINED, i18n("Could not access \"%1\" folder.").arg(i18n(KFI_KIO_FONTS_SYS))); + } + else + { + for(it=entries->begin(); it!=end; ++it) + { + QString file(CFcEngine::getFcString(*it, FC_FILE)); + + if (0!=unlink(QFile::encodeName(file).data())) + error(EACCES==errno || EPERM==errno + ? KIO::ERR_ACCESS_DENIED + : EISDIR==errno + ? KIO::ERR_IS_DIRECTORY + : KIO::ERR_CANNOT_DELETE, + file); + else + { + modifiedDirs.add(Misc::getDir(file)); + + KURL::List urls; + + Misc::getAssociatedUrls(KURL(file), urls); + + if(urls.count()) + { + KURL::List::Iterator uIt, + uEnd=urls.end(); + + for(uIt=urls.begin(); uIt!=uEnd; ++uIt) + unlink(QFile::encodeName((*uIt).path()).data()); + } + } + } + modified(itsRoot ? FOLDER_SYS : FOLDER_USER, clearList, modifiedDirs); + } + finished(); + } +} + +void CKioFonts::modified(EFolder folder, bool clearList, const CDirList &dirs) +{ + KFI_DBUG << "modified(" << (int)folder << ")\n"; + + if(FOLDER_SYS!=folder || itsCanStorePasswd || itsRoot) + { + if(dirs.count()) + { + CDirList::ConstIterator it(dirs.begin()), + end(dirs.end()); + + for(; it!=end; ++it) + itsFolders[folder].modified.add(*it); + } + else + itsFolders[folder].modified.add(itsFolders[folder].location); + + if(++itsFontChanges>MAX_NEW_FONTS) + { + setTimeoutSpecialCommand(0); // Cancel timer + doModified(); + } + else + setTimeoutSpecialCommand(TIMEOUT); + } + + if(FOLDER_SYS==folder && !itsRoot && !itsCanStorePasswd) + { + // If we modified sys, we're not root, and couldn't store the passwd, then kfontinst has already been called + // so no need to ask it to add folder to fontconfig and X's config files... + itsHasSys=true; + itsAddToSysFc=false; + } + if(clearList) + clearFontList(); // List of fonts has changed.../ +} + +void CKioFonts::special(const QByteArray &a) +{ + KFI_DBUG << "special" << endl; + + if(a.size()) + { + QDataStream stream(a, IO_ReadOnly); + int cmd; + + stream >> cmd; + + switch (cmd) + { + case SPECIAL_RESCAN: + clearFontList(); + updateFontList(); + finished(); + break; + case SPECIAL_RECONFIG: // Only itended to be called from kcmfontinst - when a user has re-enabled doX or doGs + if(itsRoot && !itsFolders[FOLDER_SYS].modified.contains(itsFolders[FOLDER_SYS].location)) + itsFolders[FOLDER_SYS].modified.add(itsFolders[FOLDER_SYS].location); + else if(!itsRoot && !itsFolders[FOLDER_USER].modified.contains(itsFolders[FOLDER_USER].location)) + itsFolders[FOLDER_USER].modified.add(itsFolders[FOLDER_USER].location); + + doModified(); + finished(); + break; + default: + error( KIO::ERR_UNSUPPORTED_ACTION, QString::number(cmd)); + } + } + else + doModified(); +} + +void CKioFonts::createRootRefreshCmd(QCString &cmd, const CDirList &dirs, bool reparseCfg) +{ + if(reparseCfg) + reparseConfig(); + + if(!cmd.isEmpty()) + cmd+=" && "; + + cmd+=FC_CACHE_CMD; + + if(dirs.count()) + { + CDirList::ConstIterator it(dirs.begin()), + end(dirs.end()); + + for(; it!=end; ++it) + { + QCString tmpCmd; + + if(*it==itsFolders[FOLDER_SYS].location) + { + if(0!=itsNrsKfiParams[0]) + tmpCmd+=itsNrsKfiParams; + } + else + if(0!=itsNrsNonMainKfiParams[0]) + tmpCmd+=itsNrsNonMainKfiParams; + + if(!tmpCmd.isEmpty()) + { + cmd+=" && kfontinst "; + cmd+=tmpCmd; + cmd+=" "; + cmd+=QFile::encodeName(KProcess::quote(*it)); + } + } + } + else if (0!=itsNrsKfiParams[0]) + { + cmd+=" && kfontinst "; + cmd+=itsNrsKfiParams; + cmd+=" "; + cmd+=QFile::encodeName(KProcess::quote(itsFolders[FOLDER_SYS].location)); + } +} + +void CKioFonts::doModified() +{ + KFI_DBUG << "doModified" << endl; + + if(itsFolders[FOLDER_SYS].modified.count() || itsFolders[FOLDER_USER].modified.count()) + reparseConfig(); + + itsFontChanges=0; + if(itsFolders[FOLDER_SYS].modified.count()) + { + if(itsRoot) + { + Misc::doCmd(FC_CACHE_CMD); + KFI_DBUG << "RUN(root): " << FC_CACHE_CMD << endl; + + // + // If a non-default folder has been modified, always configure X + if(NULL==strchr(itsKfiParams, 'x') && + (itsFolders[FOLDER_SYS].modified.count()>1 || !itsFolders[FOLDER_SYS].modified.contains(itsFolders[FOLDER_SYS].location))) + { + if(0==itsKfiParams[0]) + strcpy(itsKfiParams, "-x"); + else + strcat(itsKfiParams, "x"); + } + + if(0!=itsKfiParams[0]) + { + CDirList::ConstIterator it(itsFolders[FOLDER_SYS].modified.begin()), + end(itsFolders[FOLDER_SYS].modified.end()); + + for(; it!=end; ++it) + { + Misc::doCmd("kfontinst", itsKfiParams, QFile::encodeName(*it)); + KFI_DBUG << "RUN(root): kfontinst " << itsKfiParams << ' ' << *it << endl; + } + + if(itsFolders[FOLDER_SYS].modified.contains(itsFolders[FOLDER_SYS].location)) + { + itsHasSys=true; + itsAddToSysFc=false; + } + } + } + else + { + QCString cmd; + + createRootRefreshCmd(cmd, itsFolders[FOLDER_SYS].modified, false); + if(doRootCmd(cmd, false) && itsFolders[FOLDER_SYS].modified.contains(itsFolders[FOLDER_SYS].location)) + { + itsHasSys=true; + itsAddToSysFc=false; + } + if(NULL==strstr(itsNrsKfiParams, "s")) + Misc::doCmd("xset", "fp", "rehash"); // doRootCmd can only refresh if xfs is being used, so try here anyway... + } + itsFolders[FOLDER_SYS].modified.clear(); + } + + if(!itsRoot && itsFolders[FOLDER_USER].modified.count()) + { + Misc::doCmd(FC_CACHE_CMD); + KFI_DBUG << "RUN(non-root): " << FC_CACHE_CMD << endl; + + if(0!=itsKfiParams[0]) + { + CDirList::ConstIterator it(itsFolders[FOLDER_USER].modified.begin()), + end(itsFolders[FOLDER_USER].modified.end()); + + for(; it!=end; ++it) + { + Misc::doCmd("kfontinst", itsKfiParams, QFile::encodeName(*it)); + KFI_DBUG << "RUN(non-root): kfontinst " << itsKfiParams << ' ' << *it << endl; + } + } + itsFolders[FOLDER_USER].modified.clear(); + } + + KFI_DBUG << "finished ModifiedDirs" << endl; +} + +#define SYS_USER "root" +QString CKioFonts::getRootPasswd(bool askPasswd) +{ + KFI_DBUG << "getRootPasswd" << endl; + KIO::AuthInfo authInfo; + SuProcess proc(SYS_USER); + bool error=false; + int attempts=0; + QString errorMsg; + + authInfo.url=KURL(KFI_KIO_FONTS_PROTOCOL ":///"); + authInfo.username=SYS_USER; + authInfo.keepPassword=true; + + if(!checkCachedAuthentication(authInfo) && !askPasswd) + authInfo.password=itsPasswd; + + if(askPasswd) + while(!error && 0!=proc.checkInstall(authInfo.password.local8Bit())) + { + KFI_DBUG << "ATTEMPT : " << attempts << endl; + if(1==attempts) + errorMsg=i18n("Incorrect password.\n"); + if((!openPassDlg(authInfo, errorMsg) && attempts) || ++attempts>4 || SYS_USER!=authInfo.username) + error=true; + } + else + error=proc.checkInstall(authInfo.password.local8Bit()) ? true : false; + return error ? QString::null : authInfo.password; +} + +bool CKioFonts::doRootCmd(const char *cmd, const QString &passwd) +{ + KFI_DBUG << "doRootCmd " << cmd << endl; + + if(!passwd.isEmpty()) + { + SuProcess proc(SYS_USER); + + if(itsCanStorePasswd) + itsPasswd=passwd; + + KFI_DBUG << "Try to run command" << endl; + proc.setCommand(cmd); + return proc.exec(passwd.local8Bit()) ? false : true; + } + + return false; +} + +bool CKioFonts::confirmUrl(KURL &url) +{ + KFI_DBUG << "confirmUrl " << url.path() << endl; + if(!itsRoot) + { + QString sect(getSect(url.path())); + + if(!isSysFolder(sect) && !isUserFolder(sect)) + { + bool changeToSystem=false; + + if(DEST_UNCHANGED!=itsLastDest && itsLastDestTime && (abs(time(NULL)-itsLastDestTime) < constMaxLastDestTime)) + changeToSystem=DEST_SYS==itsLastDest; + else + changeToSystem=KMessageBox::No==messageBox(QuestionYesNo, + i18n("Do you wish to install the font into \"%1\" (in which " + "case the font will only be usable by you), or \"%2\" (" + "the font will be usable by all users - but you will " + "need to know the administrator's password)?") + .arg(i18n(KFI_KIO_FONTS_USER)).arg(i18n(KFI_KIO_FONTS_SYS)), + i18n("Where to Install"), i18n(KFI_KIO_FONTS_USER), + i18n(KFI_KIO_FONTS_SYS)); + + if(changeToSystem) + { + itsLastDest=DEST_SYS; + url.setPath(QChar('/')+i18n(KFI_KIO_FONTS_SYS)+QChar('/')+url.fileName()); + } + else + { + itsLastDest=DEST_USER; + url.setPath(QChar('/')+i18n(KFI_KIO_FONTS_USER)+QChar('/')+url.fileName()); + } + + KFI_DBUG << "Changed URL to:" << url.path() << endl; + return true; + } + } + + return false; +} + +void CKioFonts::clearFontList() +{ + KFI_DBUG << "clearFontList" << endl; + + if(itsFontList) + FcFontSetDestroy(itsFontList); + + itsFontList=NULL; + itsFolders[FOLDER_SYS].fontMap.clear(); + itsFolders[FOLDER_USER].fontMap.clear(); +} + +bool CKioFonts::updateFontList() +{ + KFI_DBUG << "updateFontList" << endl; + + if(!itsFontList || !FcConfigUptoDate(0) || // For some reason just the "!FcConfigUptoDate(0)" check does not always work :-( + (abs(time(NULL)-itsLastFcCheckTime)>constMaxFcCheckTime)) + { + FcInitReinitialize(); + clearFontList(); + } + + if(!itsFontList) + { + KFI_DBUG << "updateFontList - update list of fonts " << endl; + + itsLastFcCheckTime=time(NULL); + + FcPattern *pat = FcPatternCreate(); + FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_FAMILY, FC_WEIGHT, FC_SCALABLE, +#ifdef KFI_FC_HAS_WIDTHS + FC_WIDTH, +#endif + FC_SLANT, (void*)0); + + itsFontList=FcFontList(0, pat, os); + + FcPatternDestroy(pat); + FcObjectSetDestroy(os); + + if (itsFontList) + { + QString home(Misc::dirSyntax(QDir::homeDirPath())); + + for (int i = 0; i < itsFontList->nfont; i++) + { + EFolder folder=FOLDER_SYS; + QString file(Misc::fileSyntax(CFcEngine::getFcString(itsFontList->fonts[i], FC_FILE))); + + if(!file.isEmpty()) + { + if(!itsRoot && 0==file.find(home)) + folder=FOLDER_USER; + + QValueList<FcPattern *> &patterns= + itsFolders[folder].fontMap[CFcEngine::createName(itsFontList->fonts[i])]; + bool use=true; + + if(patterns.count()) // Check for duplicates... + { + QValueList<FcPattern *>::Iterator it, + end=patterns.end(); + + for(it=patterns.begin(); use && it!=end; ++it) + if(file==(Misc::fileSyntax(CFcEngine::getFcString(*it, FC_FILE)))) + use=false; + } + if(use) + patterns.append(itsFontList->fonts[i]); + } + } + } + } + + if(NULL==itsFontList) + { + error(KIO::ERR_SLAVE_DEFINED, i18n("Internal fontconfig error.")); + return false; + } + + return true; +} + +CKioFonts::EFolder CKioFonts::getFolder(const KURL &url) +{ + return itsRoot || isSysFolder(getSect(url.path())) ? FOLDER_SYS : FOLDER_USER; +} + +QMap<QString, QValueList<FcPattern *> >::Iterator CKioFonts::getMap(const KURL &url) +{ + EFolder folder(getFolder(url)); + QMap<QString, QValueList<FcPattern *> >::Iterator it=itsFolders[folder].fontMap.find(removeMultipleExtension(url)); + + if(it==itsFolders[folder].fontMap.end()) // Perhaps it was fonts:/System/times.ttf ??? + { + FcPattern *pat=getEntry(folder, url.fileName(), false); + + if(pat) + it=itsFolders[folder].fontMap.find(CFcEngine::createName(pat)); + } + + return it; +} + +QValueList<FcPattern *> * CKioFonts::getEntries(const KURL &url) +{ + QMap<QString, QValueList<FcPattern *> >::Iterator it=getMap(url); + + if(it!=itsFolders[getFolder(url)].fontMap.end()) + return &(it.data()); + + error(KIO::ERR_SLAVE_DEFINED, i18n("Could not access \"%1\".").arg(url.prettyURL())); + return NULL; +} + +FcPattern * CKioFonts::getEntry(EFolder folder, const QString &file, bool full) +{ + QMap<QString, QValueList<FcPattern *> >::Iterator it, + end=itsFolders[folder].fontMap.end(); + + for(it=itsFolders[folder].fontMap.begin(); it!=end; ++it) + { + QValueList<FcPattern *>::Iterator patIt, + patEnd=it.data().end(); + + for(patIt=it.data().begin(); patIt!=patEnd; ++patIt) + if( (full && CFcEngine::getFcString(*patIt, FC_FILE)==file) || + (!full && Misc::getFile(CFcEngine::getFcString(*patIt, FC_FILE))==file)) + return *patIt; + } + + return NULL; +} + +bool CKioFonts::checkFile(const QString &file) +{ + QCString cFile(QFile::encodeName(file)); + + // + // To speed things up, check the files extension 1st... + if(checkExt(cFile, "ttf") || checkExt(cFile, "otf") || checkExt(cFile, "ttc") || checkExt(cFile, "pfa") || checkExt(cFile, "pfb") || + isAAfm(file) || isAPfm(file)) + return true; + + // + // No exension match, so try querying with FreeType... + int count=0; + FcPattern *pat=FcFreeTypeQuery((const FcChar8 *)(QFile::encodeName(file).data()), 0, NULL, &count); + + if(pat) + { + FcPatternDestroy(pat); + return true; + } + + error(KIO::ERR_SLAVE_DEFINED, i18n("<p>Only fonts may be installed.</p><p>If installing a fonts package (*%1), then " + "extract the components, and install individually.</p>").arg(constMultipleExtension)); + return false; +} + +bool CKioFonts::getSourceFiles(const KURL &src, QStringList &files) +{ + if(KFI_KIO_FONTS_PROTOCOL==src.protocol()) + { + QValueList<FcPattern *> *entries=getEntries(src); + + if(entries && entries->count()) + { + QValueList<FcPattern *>::Iterator it, + end=entries->end(); + + for(it=entries->begin(); it!=end; ++it) + files.append(CFcEngine::getFcString(*it, FC_FILE)); + } + + if(files.count()) + { + QStringList::Iterator sIt, + sEnd=files.end(); + + for(sIt=files.begin(); sIt!=sEnd; ++sIt) + { + KURL::List urls; + + Misc::getAssociatedUrls(KURL(*sIt), urls); + + if(urls.count()) + { + KURL::List::Iterator uIt, + uEnd=urls.end(); + + for(uIt=urls.begin(); uIt!=uEnd; ++uIt) + if(-1==files.findIndex((*uIt).path())) + files.append((*uIt).path()); + } + } + } + } + else + if(src.isLocalFile()) + if(checkFile(src.path())) + files.append(src.path()); + else + return false; // error logged in checkFile... + + if(files.count()) + { + QStringList::Iterator it, + end=files.end(); + + for(it=files.begin(); it!=end; ++it) + { + QCString realSrc=QFile::encodeName(*it); + KDE_struct_stat buffSrc; + + if (-1==KDE_stat(realSrc.data(), &buffSrc)) + { + error(EACCES==errno ? KIO::ERR_ACCESS_DENIED : KIO::ERR_DOES_NOT_EXIST, src.prettyURL()); + return false; + } + if(S_ISDIR(buffSrc.st_mode)) + { + error(KIO::ERR_IS_DIRECTORY, src.prettyURL()); + return false; + } + if(S_ISFIFO(buffSrc.st_mode) || S_ISSOCK(buffSrc.st_mode)) + { + error(KIO::ERR_CANNOT_OPEN_FOR_READING, src.prettyURL()); + return false; + } + } + } + else + { + error(KIO::ERR_DOES_NOT_EXIST, src.prettyURL()); + return false; + } + + return true; +} + +bool CKioFonts::checkDestFile(const KURL &src, const KURL &dest, EFolder destFolder, bool overwrite) +{ + if(!overwrite && (Misc::fExists(itsFolders[destFolder].location+src.fileName()) || + Misc::fExists(itsFolders[destFolder].location+modifyName(src.fileName())) ) ) + { + error(KIO::ERR_FILE_ALREADY_EXIST, dest.prettyURL()); + return false; + } + + return true; +} + +bool CKioFonts::checkDestFiles(const KURL &src, QMap<QString, QString> &map, const KURL &dest, EFolder destFolder, bool overwrite) +{ + // + // Check whether files exist at destination... + // + if(dest.protocol()==src.protocol() && + dest.directory()==src.directory()) // Check whether confirmUrl changed a "cp fonts:/System fonts:/" + // to "cp fonts:/System fonts:/System" + { + error(KIO::ERR_FILE_ALREADY_EXIST, dest.prettyURL()); + return false; + } + + if(!overwrite) + { + QMap<QString, QString>::Iterator fIt(map.begin()), + fEnd(map.end()); + + for(; fIt!=fEnd; ++fIt) + if(NULL!=getEntry(destFolder, fIt.data()) || NULL!=getEntry(destFolder, modifyName(fIt.data()))) + { + error(KIO::ERR_FILE_ALREADY_EXIST, dest.prettyURL()); + return false; + } + } + + return true; +} + +// +// Gather the number and names of the font faces located in "files". If there is more than 1 face +// (such as there would be for a TTC font), then ask the user for confirmation of the action. +bool CKioFonts::confirmMultiple(const KURL &url, const QStringList &files, EFolder folder, EOp op) +{ + if(KFI_KIO_FONTS_PROTOCOL!=url.protocol()) + return true; + + QStringList::ConstIterator it, + end=files.end(); + QStringList fonts; + + for(it=files.begin(); it!=files.end(); ++it) + { + FcPattern *pat=getEntry(folder, *it, false); + + if(pat) + { + QString name(CFcEngine::createName(pat)); + + if(-1==fonts.findIndex(name)) + fonts.append(name); + } + } + + if(fonts.count()>1) + { + QString out; + QStringList::Iterator it, + end=fonts.end(); + + for(it=fonts.begin(); it!=end; ++it) + out+=QString("<li>")+*it+QString("</li>"); + + if(KMessageBox::No==messageBox(QuestionYesNo, + OP_MOVE==op + ? i18n("<p>This font is located in a file alongside other fonts; in order " + "to proceed with the moving they will all have to be moved. " + "The other affected fonts are:</p><ul>%1</ul><p>\n Do you wish to " + "move all of these?</p>").arg(out) + : OP_COPY==op + ? i18n("<p>This font is located in a file alongside other fonts; in order " + "to proceed with the copying they will all have to be copied. " + "The other affected fonts are:</p><ul>%1</ul><p>\n Do you wish to " + "copy all of these?</p>").arg(out) + : i18n("<p>This font is located in a file alongside other fonts; in order " + "to proceed with the deleting they will all have to be deleted. " + "The other affected fonts are:</p><ul>%1</ul><p>\n Do you wish to " + "delete all of these?</p>").arg(out))) + { + error(KIO::ERR_USER_CANCELED, url.prettyURL()); + return false; + } + } + + return true; +} + +bool CKioFonts::confirmMultiple(const KURL &url, QValueList<FcPattern *> *patterns, EFolder folder, EOp op) +{ + if(KFI_KIO_FONTS_PROTOCOL!=url.protocol()) + return true; + + QStringList files; + + if(patterns && patterns->count()) + { + QValueList<FcPattern *>::Iterator it, + end=patterns->end(); + + for(it=patterns->begin(); it!=end; ++it) + files.append(CFcEngine::getFcString(*it, FC_FILE)); + } + + return confirmMultiple(url, files, folder, op); +} + +bool CKioFonts::checkUrl(const KURL &u, bool rootOk) +{ + if(KFI_KIO_FONTS_PROTOCOL==u.protocol() && (!rootOk || (rootOk && "/"!=u.path()))) + { + QString sect(getSect(u.path())); + + if(itsRoot) + { + if((isSysFolder(sect) || isUserFolder(sect)) && + (itsFolders[FOLDER_SYS].fontMap.end()==itsFolders[FOLDER_SYS].fontMap.find(sect))) +//CPD: TODO: || it has a font specified! e.g. fonts:/System/Times -> even in have a fonts:/System font, redirect +//should still happen + { + redirection(getRedirect(u)); + finished(); + return false; + } + } + else + if(!isSysFolder(sect) && !isUserFolder(sect)) + { + error(KIO::ERR_SLAVE_DEFINED, i18n("Please specify \"%1\" or \"%2\".") + .arg(i18n(KFI_KIO_FONTS_USER)).arg(i18n(KFI_KIO_FONTS_SYS))); + return false; + } + } + + return true; +} + +bool CKioFonts::checkAllowed(const KURL &u) +{ + if (KFI_KIO_FONTS_PROTOCOL==u.protocol()) + { + QString ds(Misc::dirSyntax(u.path())); + + if(ds==QString(QChar('/')+i18n(KFI_KIO_FONTS_USER)+QChar('/')) || + ds==QString(QChar('/')+i18n(KFI_KIO_FONTS_SYS)+QChar('/')) || + ds==QString(QChar('/')+QString::fromLatin1(KFI_KIO_FONTS_USER)+QChar('/')) || + ds==QString(QChar('/')+QString::fromLatin1(KFI_KIO_FONTS_SYS)+QChar('/'))) + { + error(KIO::ERR_SLAVE_DEFINED, i18n("Sorry, you cannot rename, move, copy, or delete either \"%1\" or \"%2\".") + .arg(i18n(KFI_KIO_FONTS_USER)).arg(i18n(KFI_KIO_FONTS_SYS))); \ + return false; + } + } + + return true; +} + +// +// Create an AFM from a Type 1 (pfa/pfb) font and its PFM file... +void CKioFonts::createAfm(const QString &file, bool nrs, const QString &passwd) +{ + if(nrs && passwd.isEmpty()) + return; + + bool type1=isAType1(file), + pfm=!type1 && isAPfm(file); // No point checking if is pfm if its a type1 + + if(type1 || pfm) + { + QString afm=getMatch(file, "afm"); // pf2afm wants files with lowercase extension, so just check for lowercase! + // -- when a font is installed, the extensio is converted to lowercase anyway... + + if(afm.isEmpty()) // No point creating if AFM already exists! + { + QString pfm, + t1; + + if(type1) // Its a Type1, so look for existing PFM + { + pfm=getMatch(file, "pfm"); + t1=file; + } + else // Its a PFM, so look for existing Type1 + { + t1=getMatch(file, "pfa"); + if(t1.isEmpty()) + t1=getMatch(file, "pfb"); + pfm=file; + } + + if(!t1.isEmpty() && !pfm.isEmpty()) // Do we have both Type1 and PFM? + { + QString name(t1.left(t1.length()-4)); // pf2afm wants name without extension... + + if(nrs) + { + QCString cmd("pf2afm "); + cmd+=QFile::encodeName(KProcess::quote(name)); + doRootCmd(cmd, passwd); + } + else + Misc::doCmd("pf2afm", QFile::encodeName(name)); + } + } + } +} + +void CKioFonts::reparseConfig() +{ + KFI_DBUG << "reparseConfig" << endl; + + itsKfiParams[0]=0; + if(!itsRoot) + { + itsNrsKfiParams[0]=0; + itsNrsNonMainKfiParams[0]=0; + } + + if(itsRoot) + { + KConfig cfg(KFI_ROOT_CFG_FILE); + bool doX=cfg.readBoolEntry(KFI_CFG_X_KEY, KFI_DEFAULT_CFG_X), + doGs=cfg.readBoolEntry(KFI_CFG_GS_KEY, KFI_DEFAULT_CFG_GS); + + if(doX || !doGs) + { + strcpy(itsKfiParams, doGs ? "-g" : "-"); + if(doX) + { + if(!itsUsingXfsFpe) + strcat(itsKfiParams, "r"); + + if(!itsUsingFcFpe) + { + strcat(itsKfiParams, itsUsingXfsFpe ? "sx" : "x"); + if(!itsHasSys) + strcat(itsKfiParams, "a"); + } + } + } + } + else + { + KConfig rootCfg(KFI_ROOT_CFG_FILE); + bool rootDoX=rootCfg.readBoolEntry(KFI_CFG_X_KEY, KFI_DEFAULT_CFG_X), + rootDoGs=rootCfg.readBoolEntry(KFI_CFG_GS_KEY, KFI_DEFAULT_CFG_GS); + + strcpy(itsNrsKfiParams, "-"); + + if(rootDoX || rootDoGs) + { + strcpy(itsNrsKfiParams, "-"); + strcpy(itsNrsNonMainKfiParams, "-"); + + if(rootDoGs) + { + strcpy(itsNrsKfiParams, "g"); + strcpy(itsNrsNonMainKfiParams, "g"); + } + + if(rootDoX && !itsUsingFcFpe) + { + strcat(itsNrsKfiParams, itsUsingXfsFpe ? "sx" : "x"); // Can't get root to refresh X, only xfs! + strcat(itsNrsNonMainKfiParams, itsUsingXfsFpe ? "sx" : "x"); + if(!itsHasSys) + strcat(itsNrsKfiParams, "a"); + } + if(0==itsNrsNonMainKfiParams[1]) + itsNrsNonMainKfiParams[0]=0; + } + + if(itsAddToSysFc) + strcpy(itsNrsKfiParams, "f"); + + if(0==itsNrsKfiParams[1]) + itsNrsKfiParams[0]=0; + + KConfig cfg(KFI_CFG_FILE); + bool doX=cfg.readBoolEntry(KFI_CFG_X_KEY, KFI_DEFAULT_CFG_X), + doGs=cfg.readBoolEntry(KFI_CFG_GS_KEY, KFI_DEFAULT_CFG_GS); + + strcpy(itsKfiParams, doGs ? "-g" : "-"); + + if(doX) + strcat(itsKfiParams, itsUsingFcFpe ? "r" : "rx"); + } + + if(0==itsKfiParams[1]) + itsKfiParams[0]=0; +} + +} diff --git a/kcontrol/kfontinst/kio/KioFonts.h b/kcontrol/kfontinst/kio/KioFonts.h new file mode 100644 index 000000000..abfd6b042 --- /dev/null +++ b/kcontrol/kfontinst/kio/KioFonts.h @@ -0,0 +1,161 @@ +#ifndef __KIO_FONTS_H__ +#define __KIO_FONTS_H__ + +//////////////////////////////////////////////////////////////////////////////// +// +// Class Name : KFI::CKioFonts +// Author : Craig Drummond +// Project : K Font Installer +// Creation Date : 05/03/2003 +// Version : $Revision$ $Date$ +// +//////////////////////////////////////////////////////////////////////////////// +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//////////////////////////////////////////////////////////////////////////////// +// (C) Craig Drummond, 2003, 2004 +//////////////////////////////////////////////////////////////////////////////// + +#include <fontconfig/fontconfig.h> +#include <time.h> +#include <kio/slavebase.h> +#include <kurl.h> +#include <klocale.h> +#include <qstring.h> +#include <qcstring.h> +#include <qmap.h> +#include <qvaluelist.h> +#include "Misc.h" +#include "KfiConstants.h" + +namespace KFI +{ + +class CKioFonts : public KIO::SlaveBase +{ + private: + + enum EConstants + { + KFI_PARAMS = 8 + }; + + enum EDest + { + DEST_UNCHANGED, + DEST_SYS, + DEST_USER + }; + + enum EFolder + { + FOLDER_SYS, + FOLDER_USER, + + FOLDER_COUNT + }; + + enum EOp + { + OP_COPY, + OP_MOVE, + OP_DELETE + }; + + class CDirList : public QStringList + { + public: + + CDirList() { } + CDirList(const QString &str) : QStringList(str) { } + + void add(const QString &d) { if (!contains(d)) append(d); } + }; + + struct TFolder + { + QString location; + CDirList modified; + QMap<QString, QValueList<FcPattern *> > fontMap; // Maps from "Times New Roman" -> $HOME/.fonts/times.ttf + }; + + public: + + CKioFonts(const QCString &pool, const QCString &app); + virtual ~CKioFonts(); + + static QString getSect(const QString &f) { return f.section('/', 1, 1); } + + void listDir(const KURL &url); + void stat(const KURL &url); + bool createStatEntry(KIO::UDSEntry &entry, const KURL &url, EFolder folder); + void get(const KURL &url); + void put(const KURL &url, int mode, bool overwrite, bool resume); + void copy(const KURL &src, const KURL &dest, int mode, bool overwrite); + void rename(const KURL &src, const KURL &dest, bool overwrite); + void del(const KURL &url, bool isFile); + + private: + + bool putReal(const QString &destOrig, const QCString &destOrigC, bool origExists, int mode, bool resume); + void modified(EFolder folder, bool clearList=true, const CDirList &dirs=CDirList()); + void special(const QByteArray &a); + void createRootRefreshCmd(QCString &cmd, const CDirList &dirs=CDirList(), bool reparseCfg=true); + void doModified(); + QString getRootPasswd(bool askPasswd=true); + bool doRootCmd(const char *cmd, const QString &passwd); + bool doRootCmd(const char *cmd, bool askPasswd=true) { return doRootCmd(cmd, getRootPasswd(askPasswd)); } + bool confirmUrl(KURL &url); + void clearFontList(); + bool updateFontList(); + EFolder getFolder(const KURL &url); + QMap<QString, QValueList<FcPattern *> >::Iterator getMap(const KURL &url); + QValueList<FcPattern *> * getEntries(const KURL &url); + FcPattern * getEntry(EFolder folder, const QString &file, bool full=false); + bool checkFile(const QString &file); + bool getSourceFiles(const KURL &src, QStringList &files); + bool checkDestFile(const KURL &src, const KURL &dest, EFolder destFolder, bool overwrite); + bool checkDestFiles(const KURL &src, QMap<QString, QString> &map, const KURL &dest, EFolder destFolder, bool overwrite); + bool confirmMultiple(const KURL &url, const QStringList &files, EFolder folder, EOp op); + bool confirmMultiple(const KURL &url, QValueList<FcPattern *> *patterns, EFolder folder, EOp op); + bool checkUrl(const KURL &u, bool rootOk=false); + bool checkAllowed(const KURL &u); + void createAfm(const QString &file, bool nrs=false, const QString &passwd=QString::null); + void reparseConfig(); + + private: + + bool itsRoot, + itsCanStorePasswd, + itsUsingFcFpe, + itsUsingXfsFpe, + itsHasSys, + itsAddToSysFc; + QString itsPasswd; + unsigned int itsFontChanges; + EDest itsLastDest; + time_t itsLastDestTime, + itsLastFcCheckTime; + FcFontSet *itsFontList; + TFolder itsFolders[FOLDER_COUNT]; + char itsNrsKfiParams[KFI_PARAMS], + itsNrsNonMainKfiParams[KFI_PARAMS], + itsKfiParams[KFI_PARAMS]; +}; + +} + +#endif diff --git a/kcontrol/kfontinst/kio/Makefile.am b/kcontrol/kfontinst/kio/Makefile.am new file mode 100644 index 000000000..ad0f2ea8e --- /dev/null +++ b/kcontrol/kfontinst/kio/Makefile.am @@ -0,0 +1,20 @@ +kde_module_LTLIBRARIES = kio_fonts.la +kio_fonts_la_SOURCES = \ +KioFonts.cpp + +kio_fonts_la_LIBADD = ../../fonts/libkxftconfig.la $(LIBFONTCONFIG_LIBS) $(LIBFREETYPE_LIBS) $(LIB_KIO) -lkdesu ../lib/libkfontinst.la +kio_fonts_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries) $(KDE_RPATH) $(LIBFONTCONFIG_RPATH) $(LIBFREETYPE_RPATH) -module -avoid-version -no-undefined +AM_CPPFLAGS= -I$(srcdir)/../lib -I$(srcdir)/../../fonts $(all_includes) $(LIBFREETYPE_CFLAGS) $(LIBFONTCONFIG_CFLAGS) -D_LARGEFILE64_SOURCE + +# The kxftconfig stuf really belongs to kdebase/kcontrol/fonts - here only so that can distribute this as an archive. +noinst_HEADERS = \ +KioFonts.h + +servicesdir = $(kde_servicesdir) +services_DATA = fonts.protocol + +vfsdata_DATA = fonts.desktop +vfsdatadir = $(kde_datadir)/konqsidebartng/virtual_folders/services + +mimetype_DATA = folder.desktop system-folder.desktop package.desktop +mimetypedir = $(kde_mimedir)/fonts diff --git a/kcontrol/kfontinst/kio/folder.desktop b/kcontrol/kfontinst/kio/folder.desktop new file mode 100644 index 000000000..24e328ae8 --- /dev/null +++ b/kcontrol/kfontinst/kio/folder.desktop @@ -0,0 +1,80 @@ +[Desktop Entry] +Comment=Fonts Folder +Comment[af]=Skrif tipe Gids +Comment[ar]=مجلد المحارف +Comment[be]=Тэчка шрыфтоў +Comment[bg]=Директория за шрифтове +Comment[bn]=ফন্ট ফোল্ডার +Comment[br]=Renkell ar fontoù +Comment[bs]=Direktorij sa fontovima +Comment[ca]=Carpeta de lletres +Comment[cs]=Složka písem +Comment[csb]=Katalog fòntów +Comment[da]=Skrifttypemappe +Comment[de]=Ordner für Schriftarten +Comment[el]=Φάκελος γραμματοσειρών +Comment[eo]=Tipardosierujo +Comment[es]=Carpeta de tipos de letra +Comment[et]=Fontide kataloog +Comment[eu]=Letra-tipoen karpeta +Comment[fa]=پوشۀ قلم +Comment[fi]=Kirjasinkansio +Comment[fr]=Dossier de polices de caractères +Comment[fy]=Lettertypenmap +Comment[ga]=Fillteán na gClónna +Comment[gl]=Cartafol das Fontes +Comment[he]=תיקיית גופנים +Comment[hi]=फ़ॉन्ट्स फ़ोल्डर +Comment[hr]=Mapa fontova +Comment[hu]=Betűtípuskönyvtár +Comment[id]=Folder Fonts +Comment[is]=Leturmappa +Comment[it]=Cartella dei caratteri +Comment[ja]=フォントフォルダ +Comment[ka]=ფონტების საქაღალდე +Comment[kk]=Қаріп қапшығы +Comment[km]=ថតពុម្ពអក្សរ +Comment[ko]=홈 폴더 +Comment[lt]=Šriftų aplankas +Comment[lv]=Fontu mape +Comment[mk]=Папка со фонтови +Comment[ms]=Folder Fon +Comment[mt]=Direttorju tal-fonts +Comment[nb]=Mappe for skrifttyper +Comment[nds]=Schriftoorden-Orner +Comment[ne]=फन्ट फोल्डर +Comment[nl]=Lettertypenmap +Comment[nn]=Skriftmappe +Comment[pa]=ਫੋਂਟ ਫੋਲਡਰ +Comment[pl]=Katalog czcionek +Comment[pt]=Pasta de Tipos de Letra +Comment[pt_BR]=Pasta de Fontes +Comment[ro]=Folder de fonturi +Comment[ru]=Папка шрифтов +Comment[rw]=Ububiko bw'Imyandikire +Comment[se]=Fontamáhppa +Comment[sk]=Priečinok písiem +Comment[sl]=Mapa za pisave +Comment[sr]=Фасцикла за фонтове +Comment[sr@Latn]=Fascikla za fontove +Comment[sv]=Teckensnittskatalog +Comment[ta]=எழுத்துருக்கள் அடைவு +Comment[tg]=Феҳристи ҳарфҳо +Comment[th]=โฟลเดอร์แฟ้มแบบอักษร +Comment[tr]=Yazıtipi Klasörü +Comment[tt]=Yazu lar Törgäge +Comment[uk]=Тека шрифтів +Comment[uz]=Shriftlarning jildi +Comment[uz@cyrillic]=Шрифтларнинг жилди +Comment[vi]=Thư mục Phông chữ +Comment[wa]=Ridant di fontes +Comment[zh_CN]=字体文件夹 +Comment[zh_TW]=字型資料夾 +Icon=folder +Type=MimeType +MimeType=fonts/folder +Patterns= + +X-KDE-AutoEmbed=true +X-KDE-IsAlso=inode/directory + diff --git a/kcontrol/kfontinst/kio/fonts.desktop b/kcontrol/kfontinst/kio/fonts.desktop new file mode 100644 index 000000000..0095f49d2 --- /dev/null +++ b/kcontrol/kfontinst/kio/fonts.desktop @@ -0,0 +1,85 @@ +[Desktop Entry] +Type=Link +URL=fonts:/ +Icon=fonts +Name=Fonts +Name[af]=Skriftipes +Name[ar]=المحارف +Name[az]=Yazı növləri +Name[be]=Шрыфты +Name[bg]=Шрифтове +Name[bn]=ফন্ট +Name[br]=Fontoù +Name[bs]=Fontovi +Name[ca]=Lletres +Name[cs]=Písma +Name[csb]=Fòntë +Name[cy]=Ffontiau +Name[da]=Skrifttyper +Name[de]=Schriftarten +Name[el]=Γραμματοσειρές +Name[eo]=Tiparoj +Name[es]=Tipos de letra +Name[et]=Fondid +Name[eu]=Letra-tipoak +Name[fa]=قلمها +Name[fi]=Kirjasimet +Name[fr]=Polices +Name[fy]=Lettertypen +Name[ga]=Clónna +Name[gl]=Fontes +Name[he]=גופנים +Name[hi]=फ़ॉन्ट्स +Name[hr]=Fontovi +Name[hu]=Betűtípusok +Name[is]=Letur +Name[it]=Tipi di carattere +Name[ja]=フォント +Name[ka]=ფონტები +Name[kk]=Қаріптер +Name[km]=ពុម្ពអក្សរ +Name[ko]=글꼴 +Name[lo]=ຮູບແບບຕົວອັກສອນ +Name[lt]=Šriftai +Name[lv]=Fonti +Name[mk]=Фонтови +Name[mn]=Бичгүүд +Name[ms]=Fon +Name[nb]=Skrifttype +Name[nds]=Schriftoorden +Name[ne]=फन्ट +Name[nl]=Lettertypen +Name[nn]=Skrifttypar +Name[nso]=Difonto +Name[pa]=ਫੋਂਟ +Name[pl]=Czcionki +Name[pt]=Tipos de Letra +Name[pt_BR]=Fontes +Name[ro]=Fonturi +Name[ru]=Шрифты +Name[rw]=Imyandikire +Name[se]=Fonttat +Name[sk]=Písma +Name[sl]=Pisave +Name[sr]=Фонтови +Name[sr@Latn]=Fontovi +Name[ss]=Timo tetinhlamvu temagama +Name[sv]=Teckensnitt +Name[ta]=எழுத்துருக்கள் +Name[tg]=Ҳарфҳо +Name[th]=แบบอักษร +Name[tr]=Yazıtipleri +Name[tt]=Yazu +Name[uk]=Шрифти +Name[uz]=Shriftlar +Name[uz@cyrillic]=Шрифтлар +Name[ven]=Fontu +Name[vi]=Phông chữ +Name[wa]=Fontes +Name[xh]=Uhlobo lwamagama +Name[zh_CN]=字体 +Name[zh_TW]=字型 +Name[zu]=Izinhlobo zamagama +Open=false +X-KDE-TreeModule=Directory +X-KDE-KonqSidebarModule=konqsidebar_tree diff --git a/kcontrol/kfontinst/kio/fonts.protocol b/kcontrol/kfontinst/kio/fonts.protocol new file mode 100644 index 000000000..7f54dd0cc --- /dev/null +++ b/kcontrol/kfontinst/kio/fonts.protocol @@ -0,0 +1,18 @@ +[Protocol] +exec=kio_fonts +protocol=fonts +input=none +output=filesystem +listing=Name,Type,Size,Date,AccessDate,Access,Owner,Group,Link +reading=true +writing=true +makedir=false +deleting=true +moving=true +linking=false +copyToFile=false +copyFromFile=true +Icon=fonts +defaultMimetype=application/octet-stream +maxInstances=1 +Class=:local diff --git a/kcontrol/kfontinst/kio/package.desktop b/kcontrol/kfontinst/kio/package.desktop new file mode 100644 index 000000000..4ee3543de --- /dev/null +++ b/kcontrol/kfontinst/kio/package.desktop @@ -0,0 +1,74 @@ +[Desktop Entry] +Type=MimeType +MimeType=fonts/package +Icon=font +Patterns=*.fonts.tar.gz +Comment=Fonts Package +Comment[af]=Skriftipes Paket +Comment[ar]=حزمة المحارف +Comment[be]=Пакет шрыфтоў +Comment[bg]=Пакет с шрифтове +Comment[bn]=ফন্ট প্যাকেজ +Comment[bs]=Paket fontova +Comment[ca]=Paquet de lletres +Comment[cs]=Balík písem +Comment[csb]=Paczét fòntów +Comment[da]=Skrifttypepakke +Comment[de]=Schriftartenpaket +Comment[el]=Πακέτο γραμματοσειρών +Comment[eo]=Tiparpakaĵo +Comment[es]=Paquete de tipos de letra +Comment[et]=Fondipakett +Comment[eu]=Letra-tipoen paketea +Comment[fa]=بستۀ قلمها +Comment[fi]=Kirjasinpaketti +Comment[fr]=Paquet de polices +Comment[fy]=Lettertypenpakket +Comment[ga]=Pacáiste na gClónna +Comment[gl]=Pacote das Fontes +Comment[he]=חבילת גופנים +Comment[hr]=Paket fontova +Comment[hu]=Betűtípuscsomag +Comment[id]=Paket Font +Comment[is]=Leturpakki +Comment[it]=Pacchetto tipi di caratteri +Comment[ja]=フォントパッケージ +Comment[ka]=შრიფტების პაკეტი +Comment[kk]=Қаріп дестесі +Comment[km]=កញ្ចប់ពុម្ពអក្សរ +Comment[lt]=Šriftų paketas +Comment[mk]=Пакет со фонтови +Comment[ms]=Pakej Fon +Comment[nb]=Pakke for skrifttyper +Comment[nds]=Schriftoorden-Paket +Comment[ne]=फन्ट प्याकेज +Comment[nl]=Lettertypenpakket +Comment[nn]=Pakke for skrifter +Comment[pa]=ਫੋਂਟ ਪੈਕੇਜ +Comment[pl]=Pakiet czcionek +Comment[pt]=Pacote de Tipos de Letra +Comment[pt_BR]=Pacote de Fontes +Comment[ro]=Pachet de fonturi +Comment[ru]=Пакет шрифтов +Comment[rw]=Rukomatanya y'Imyandikire +Comment[se]=Fontapáhkka +Comment[sk]=Balík písiem +Comment[sl]=Paket pisav +Comment[sr]=Пакет фонтова +Comment[sr@Latn]=Paket fontova +Comment[sv]=Teckensnittspaket +Comment[tg]=Бастаи ҳарфҳо +Comment[th]=แพคเกจแบบอักษร +Comment[tr]=Yazıtipi Paketi +Comment[tt]=Yazular Tuplaması +Comment[uk]=Пакунок шрифтів +Comment[uz]=Shrift +Comment[uz@cyrillic]=Шрифт +Comment[vi]=Gói Phông chữ +Comment[wa]=Paket di fontes +Comment[zh_CN]=字体包 +Comment[zh_TW]=字型套件 + +[Property::X-KDE-LocalProtocol] +Type=QString +Value=tar diff --git a/kcontrol/kfontinst/kio/system-folder.desktop b/kcontrol/kfontinst/kio/system-folder.desktop new file mode 100644 index 000000000..07bf9d703 --- /dev/null +++ b/kcontrol/kfontinst/kio/system-folder.desktop @@ -0,0 +1,79 @@ +[Desktop Entry] +Comment=System Fonts Folder +Comment[af]=Stelsel Skrif tipe Gids +Comment[ar]=مجلد محارف النظام +Comment[be]=Тэчка сістэмных шрыфтоў +Comment[bg]=Директория за системните шрифтове +Comment[bn]=সিস্টেম ফন্ট ফোল্ডার +Comment[br]=Renkell fontoù ar reizhiad +Comment[bs]=Sistemski direktorij sa fontovima +Comment[ca]=Carpeta de lletres del sistema +Comment[cs]=Složka se systémovými písmy +Comment[csb]=Katalog systemòwëch fòntów +Comment[da]=Mappe til systemets skrifttyper +Comment[de]=Ordner für Systemschriften +Comment[el]=Φάκελος γραμματοσειρών συστήματος +Comment[eo]=Sistema tipardosierujo +Comment[es]=Carpeta de tipos de letra del sistema +Comment[et]=Süsteemi fontide kataloog +Comment[eu]=Sistemaren letra-tipoen karpeta +Comment[fa]=پوشۀ قلمهای سیستم +Comment[fi]=Järjestelmän kirjasinkansio +Comment[fr]=Dossier système de polices de caractères +Comment[fy]=Systeemlettertypenmap +Comment[ga]=Fillteán Clónna an Chórais +Comment[gl]=Cartafol das Fontes do Sistema +Comment[he]=תיקיית גופני מערכת +Comment[hi]=तंत्र फ़ॉन्ट्स फ़ोल्डर +Comment[hr]=Mapa sistemskih fontova +Comment[hu]=A rendszer betűtípusainak könyvtára +Comment[id]=Folder Font Sistem +Comment[is]=Kerfisleturmappa +Comment[it]=Cartella dei caratteri di sistema +Comment[ja]=システムフォントフォルダ +Comment[ka]=სისტემური ფონტების საქაღალდე +Comment[kk]=Жүйелік қаріптер қапшығы +Comment[km]=ថតពុម្ពអក្សរប្រព័ន្ធ +Comment[lt]=Sistemos šriftų aplankas +Comment[lv]=Sistēmas fontu mape +Comment[mk]=Папка со системски фонтови +Comment[ms]=Folder Fon Sistem +Comment[mt]=Direttorju tal-fonts tas-sistema +Comment[nb]=Mappe for systemskrifttyper +Comment[nds]=Orner för de Systeemschriftoorden +Comment[ne]=फन्ट फोल्डर प्रणाली +Comment[nl]=Systeemlettertypenmap +Comment[nn]=Systemskriftmappe +Comment[pa]=ਸਿਸਟਮ ਫੋਂਟ ਫੋਲਡਰ +Comment[pl]=Katalog czcionek systemowych +Comment[pt]=Pasta de Tipos de Letra do Sistema +Comment[pt_BR]=Pasta de Fontes do Sistema +Comment[ro]=Folder fonturi de sistem +Comment[ru]=Папка системных шрифтов +Comment[rw]=Ububiko bw'Imyandikire Sisitemu +Comment[se]=Vuogádatfontamáhppa +Comment[sk]=Priečinok systémových písiem +Comment[sl]=Mapa za sistemske pisave +Comment[sr]=Фасцикла за системске фонтове +Comment[sr@Latn]=Fascikla za sistemske fontove +Comment[sv]=Systemteckensnittskatalog +Comment[ta]=அமைப்பு எழுத்துருக்கள் அடைவு +Comment[tg]=Системаи феҳристи ҳарфҳо +Comment[th]=โฟลเดอร์แฟ้มแบบอักษรของระบบ +Comment[tr]=Sistem Yazı Tipi Klasörü +Comment[tt]=Sistem Yazular Törgäge +Comment[uk]=Тека системних шрифтів +Comment[uz]=Tizim shriftlarining jildi +Comment[uz@cyrillic]=Тизим шрифтларининг жилди +Comment[vi]=Thư mục Phông chữ Hệ thống +Comment[wa]=Ridant di fontes do sistinme +Comment[zh_CN]=系统字体文件夹 +Comment[zh_TW]=系統字型資料夾 +Icon=folder_red +Type=MimeType +MimeType=fonts/system-folder +Patterns= + +X-KDE-AutoEmbed=true +X-KDE-IsAlso=inode/directory + |