////////////////////////////////////////////////////////////////////////////////
//
// Class Name    : KFI::CKioFonts
// Author        : Craig Drummond
// Project       : K Font Installer
// Creation Date : 05/03/2003
// Version       : $Revision: 820166 $ $Date: 2008-06-13 14:14:08 +0200 (Fr, 13. Jun 2008) $
//
////////////////////////////////////////////////////////////////////////////////
//
// 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 &sect)
{
    return i18n(KFI_KIO_FONTS_SYS)==sect || KFI_KIO_FONTS_SYS==sect;
}

inline bool isUserFolder(const QString &sect)
{
    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;
}

}
