kmcupsmanager.cpp

00001 /*
00002  *  This file is part of the KDE libraries
00003  *  Copyright (c) 2001 Michael Goffioul <kdeprint@swing.be>
00004  *
00005  *  This library is free software; you can redistribute it and/or
00006  *  modify it under the terms of the GNU Library General Public
00007  *  License version 2 as published by the Free Software Foundation.
00008  *
00009  *  This library is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  *  Library General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU Library General Public License
00015  *  along with this library; see the file COPYING.LIB.  If not, write to
00016  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  *  Boston, MA 02110-1301, USA.
00018  **/
00019 
00020 #include <config.h>
00021 
00022 #include "kmcupsmanager.h"
00023 #include "kmprinter.h"
00024 #include "ipprequest.h"
00025 #include "cupsinfos.h"
00026 #include "driver.h"
00027 #include "kmfactory.h"
00028 #include "kmdbentry.h"
00029 #include "cupsaddsmb2.h"
00030 #include "ippreportdlg.h"
00031 #include "kpipeprocess.h"
00032 #include "util.h"
00033 #include "foomatic2loader.h"
00034 #include "ppdloader.h"
00035 
00036 #include <qfile.h>
00037 #include <qtextstream.h>
00038 #include <qregexp.h>
00039 #include <qtimer.h>
00040 #include <qsocket.h>
00041 #include <qdatetime.h>
00042 
00043 #include <kdebug.h>
00044 #include <kapplication.h>
00045 #include <klocale.h>
00046 #include <kconfig.h>
00047 #include <kstandarddirs.h>
00048 #include <klibloader.h>
00049 #include <kmessagebox.h>
00050 #include <kaction.h>
00051 #include <kdialogbase.h>
00052 #include <kextendedsocket.h>
00053 #include <kprocess.h>
00054 #include <kfilterdev.h>
00055 #include <cups/cups.h>
00056 #include <cups/ppd.h>
00057 #include <math.h>
00058 
00059 #define ppdi18n(s)  i18n(QString::fromLocal8Bit(s).utf8())
00060 
00061 void extractMaticData(QString& buf, const QString& filename);
00062 QString printerURI(KMPrinter *p, bool useExistingURI = false);
00063 QString downloadDriver(KMPrinter *p);
00064 
00065 static int trials = 5;
00066 
00067 //*****************************************************************************************************
00068 
00069     KMCupsManager::KMCupsManager(QObject *parent, const char *name, const QStringList & /*args*/)
00070 : KMManager(parent,name)
00071 {
00072     // be sure to create the CupsInfos object -> password
00073     // management is handled correctly.
00074     CupsInfos::self();
00075     m_cupsdconf = 0;
00076     m_currentprinter = 0;
00077     m_socket = 0;
00078 
00079     setHasManagement(true);
00080     setPrinterOperationMask(KMManager::PrinterAll);
00081     setServerOperationMask(KMManager::ServerAll);
00082 
00083     // change LANG variable so that CUPS is always using
00084     // english language: translation may only come from the PPD
00085     // itself, or from KDE.
00086     setenv("LANG", "en", 1);
00087 }
00088 
00089 KMCupsManager::~KMCupsManager()
00090 {
00091     //delete m_socket;
00092 }
00093 
00094 QString KMCupsManager::driverDbCreationProgram()
00095 {
00096     return QString::fromLatin1("make_driver_db_cups");
00097 }
00098 
00099 QString KMCupsManager::driverDirectory()
00100 {
00101     QString d = cupsInstallDir();
00102     if (d.isEmpty())
00103         d = "/usr";
00104     d.append("/share/cups/model");
00105     // raw foomatic support
00106     d.append(":/usr/share/foomatic/db/source");
00107     return d;
00108 }
00109 
00110 QString KMCupsManager::cupsInstallDir()
00111 {
00112     KConfig *conf=  KMFactory::self()->printConfig();
00113     conf->setGroup("CUPS");
00114     QString dir = conf->readPathEntry("InstallDir");
00115     return dir;
00116 }
00117 
00118 void KMCupsManager::reportIppError(IppRequest *req)
00119 {
00120     setErrorMsg(req->statusMessage());
00121 }
00122 
00123 bool KMCupsManager::createPrinter(KMPrinter *p)
00124 {
00125     bool isclass = p->isClass(false), result(false);
00126     IppRequest  req;
00127     QString     uri;
00128 
00129     uri = printerURI(p,false);
00130     req.addURI(IPP_TAG_OPERATION,"printer-uri",uri);
00131     // needed to avoid problems when changing printer name
00132     p->setUri(KURL(uri));
00133 
00134     if (isclass)
00135     {
00136         req.setOperation(CUPS_ADD_CLASS);
00137         QStringList members = p->members(), uris;
00138         QString     s = QString::fromLocal8Bit("ipp://%1:%2/printers/").arg(CupsInfos::self()->host()).arg(CupsInfos::self()->port());
00139         for (QStringList::ConstIterator it=members.begin(); it!=members.end(); ++it)
00140             uris.append(s+(*it));
00141         req.addURI(IPP_TAG_PRINTER,"member-uris",uris);
00142     }
00143     else
00144     {
00145         req.setOperation(CUPS_ADD_PRINTER);
00146         // only set the device-uri if needed, otherwise you may loose authentification
00147         // data (login/password in URI's like smb or ipp).
00148         KMPrinter   *otherP = findPrinter(p->printerName());
00149         if (!otherP || otherP->device() != p->device())
00150         {
00156             req.addURI(IPP_TAG_PRINTER,"device-uri",p->device());
00157         }
00158         if (!p->option("kde-banners").isEmpty())
00159         {
00160             QStringList bans = QStringList::split(',',p->option("kde-banners"),false);
00161             while (bans.count() < 2)
00162                 bans.append("none");
00163             req.addName(IPP_TAG_PRINTER,"job-sheets-default",bans);
00164         }
00165         req.addInteger(IPP_TAG_PRINTER,"job-quota-period",p->option("job-quota-period").toInt());
00166         req.addInteger(IPP_TAG_PRINTER,"job-k-limit",p->option("job-k-limit").toInt());
00167         req.addInteger(IPP_TAG_PRINTER,"job-page-limit",p->option("job-page-limit").toInt());
00168         if (!p->option("requesting-user-name-denied").isEmpty())
00169             req.addName(IPP_TAG_PRINTER,"requesting-user-name-denied",QStringList::split(",",p->option("requesting-user-name-denied"),false));
00170         else if (!p->option("requesting-user-name-allowed").isEmpty())
00171             req.addName(IPP_TAG_PRINTER,"requesting-user-name-allowed",QStringList::split(",",p->option("requesting-user-name-allowed"),false));
00172         else
00173             req.addName(IPP_TAG_PRINTER,"requesting-user-name-allowed",QString::fromLatin1("all"));
00174     }
00175     req.addText(IPP_TAG_PRINTER,"printer-info",p->description());
00176     req.addText(IPP_TAG_PRINTER,"printer-location",p->location());
00177 
00178     if (req.doRequest("/admin/"))
00179     {
00180         result = true;
00181         if (p->driver())
00182             result = savePrinterDriver(p,p->driver());
00183         if (result)
00184             upPrinter(p, true);
00185     }
00186     else reportIppError(&req);
00187 
00188     return result;
00189 }
00190 
00191 bool KMCupsManager::removePrinter(KMPrinter *p)
00192 {
00193     bool    result = setPrinterState(p,CUPS_DELETE_PRINTER);
00194     return result;
00195 }
00196 
00197 bool KMCupsManager::enablePrinter(KMPrinter *p, bool state)
00198 {
00199     return setPrinterState(p, (state ? CUPS_ACCEPT_JOBS : CUPS_REJECT_JOBS));
00200 }
00201 
00202 bool KMCupsManager::startPrinter(KMPrinter *p, bool state)
00203 {
00204     return setPrinterState(p, (state ? IPP_RESUME_PRINTER : IPP_PAUSE_PRINTER));
00205 }
00206 
00207 bool KMCupsManager::setDefaultPrinter(KMPrinter *p)
00208 {
00209     return setPrinterState(p,CUPS_SET_DEFAULT);
00210 }
00211 
00212 bool KMCupsManager::setPrinterState(KMPrinter *p, int state)
00213 {
00214     IppRequest  req;
00215     QString     uri;
00216 
00217     req.setOperation(state);
00218     uri = printerURI(p);
00219     req.addURI(IPP_TAG_OPERATION,"printer-uri",uri);
00220     if (req.doRequest("/admin/"))
00221         return true;
00222     reportIppError(&req);
00223     return false;
00224 }
00225 
00226 bool KMCupsManager::completePrinter(KMPrinter *p)
00227 {
00228     if (completePrinterShort(p))
00229     {
00230         // driver informations
00231         QString ppdname = downloadDriver(p);
00232         ppd_file_t  *ppd = (ppdname.isEmpty() ? NULL : ppdOpenFile(ppdname.local8Bit()));
00233         if (ppd)
00234         {
00235             KMDBEntry   entry;
00236             // use the validation mechanism of KMDBEntry to
00237             // fill possible missing entries like manufacturer
00238             // or model.
00239             entry.manufacturer = ppd->manufacturer;
00240             entry.model = ppd->shortnickname;
00241             entry.modelname = ppd->modelname;
00242             // do not check the driver regarding the manager
00243             entry.validate(false);
00244             // update the KMPrinter object
00245             p->setManufacturer(entry.manufacturer);
00246             p->setModel(entry.model);
00247             p->setDriverInfo(QString::fromLocal8Bit(ppd->nickname));
00248             ppdClose(ppd);
00249         }
00250         if (!ppdname.isEmpty())
00251             QFile::remove(ppdname);
00252 
00253         return true;
00254     }
00255     return false;
00256 }
00257 
00258 bool KMCupsManager::completePrinterShort(KMPrinter *p)
00259 {
00260     IppRequest  req;
00261     QStringList keys;
00262     QString     uri;
00263 
00264     req.setOperation(IPP_GET_PRINTER_ATTRIBUTES);
00265     uri = printerURI(p, true);
00266     req.addURI(IPP_TAG_OPERATION,"printer-uri",uri);
00267 
00268     /*
00269     // change host and port for remote stuffs
00270     if (!p->uri().isEmpty())
00271     {
00272     // THIS IS AN UGLY HACK!! FIXME
00273     // This attempts a "pre-connection" to see if the host is
00274     // actually reachable.  It times out after 2 seconds at most,
00275     // preventing application freezes.
00276     m_hostSuccess = false;
00277     m_lookupDone = false;
00278     // Give 2 seconds to connect to the printer, or abort
00279     KExtendedSocket *kes = new KExtendedSocket(p->uri().host(),
00280     p->uri().port());
00281     connect(kes, SIGNAL(connectionSuccess()), this, SLOT(hostPingSlot()));
00282     connect(kes, SIGNAL(connectionFailed(int)), this, SLOT(hostPingFailedSlot()));
00283     if (kes->startAsyncConnect() != 0) {
00284     delete kes;
00285     m_hostSuccess = false;
00286     } else {
00287     QDateTime tm = QDateTime::currentDateTime().addSecs(2);
00288     while (!m_lookupDone && (QDateTime::currentDateTime() < tm))
00289     qApp->processEvents();
00290 
00291     kes->cancelAsyncConnect();
00292 
00293     delete kes;
00294 
00295     if (!m_lookupDone)
00296     m_hostSuccess = false;
00297     }
00298 
00299     if (m_hostSuccess == true) {
00300     req.setHost(p->uri().host());
00301     req.setPort(p->uri().port());
00302     }
00303     }
00304     */
00305 
00306     // disable location as it has been transferred to listing (for filtering)
00307     //keys.append("printer-location");
00308     keys.append("printer-info");
00309     keys.append("printer-make-and-model");
00310     keys.append("job-sheets-default");
00311     keys.append("job-sheets-supported");
00312     keys.append("job-quota-period");
00313     keys.append("job-k-limit");
00314     keys.append("job-page-limit");
00315     keys.append("requesting-user-name-allowed");
00316     keys.append("requesting-user-name-denied");
00317     if (p->isClass(true))
00318     {
00319         keys.append("member-uris");
00320         keys.append("member-names");
00321     }
00322     else
00323         keys.append("device-uri");
00324     req.addKeyword(IPP_TAG_OPERATION,"requested-attributes",keys);
00325 
00326     if (req.doRequest("/printers/"))
00327     {
00328         QString value;
00329         if (req.text("printer-info",value)) p->setDescription(value);
00330         // disabled location
00331         //if (req.text("printer-location",value)) p->setLocation(value);
00332         if (req.text("printer-make-and-model",value)) p->setDriverInfo(value);
00333         if (req.uri("device-uri",value))
00334         {
00339             p->setDevice( value );
00340         }
00341         QStringList values;
00342         /*      if (req.uri("member-uris",values))
00343                 {
00344                 QStringList members;
00345                 for (QStringList::ConstIterator it=values.begin(); it!=values.end(); ++it)
00346                 {
00347                 int p = (*it).findRev('/');
00348                 if (p != -1)
00349                 members.append((*it).right((*it).length()-p-1));
00350                 }
00351                 p->setMembers(members);
00352                 }*/
00353         if (req.name("member-names",values))
00354             p->setMembers(values);
00355         // banners
00356         req.name("job-sheets-default",values);
00357         while (values.count() < 2) values.append("none");
00358         p->setOption("kde-banners",values.join(QString::fromLatin1(",")));
00359         if (req.name("job-sheets-supported",values)) p->setOption("kde-banners-supported",values.join(QString::fromLatin1(",")));
00360 
00361         // quotas
00362         int ival;
00363         if (req.integer("job-quota-period",ival)) p->setOption("job-quota-period",QString::number(ival));
00364         if (req.integer("job-k-limit",ival)) p->setOption("job-k-limit",QString::number(ival));
00365         if (req.integer("job-page-limit",ival)) p->setOption("job-page-limit",QString::number(ival));
00366 
00367         // access permissions (allow and deny are mutually exclusives)
00368         if (req.name("requesting-user-name-allowed",values) && values.count() > 0)
00369         {
00370             p->removeOption("requesting-user-name-denied");
00371             p->setOption("requesting-user-name-allowed",values.join(","));
00372         }
00373         if (req.name("requesting-user-name-denied",values) && values.count() > 0)
00374         {
00375             p->removeOption("requesting-user-name-allowed");
00376             p->setOption("requesting-user-name-denied",values.join(","));
00377         }
00378 
00379         return true;
00380     }
00381 
00382     reportIppError(&req);
00383     return false;
00384 }
00385 
00386 bool KMCupsManager::testPrinter(KMPrinter *p)
00387 {
00388     return KMManager::testPrinter(p);
00389     /*
00390        QString  testpage = testPage();
00391        if (testpage.isEmpty())
00392        {
00393        setErrorMsg(i18n("Unable to locate test page."));
00394        return false;
00395        }
00396 
00397        IppRequest   req;
00398        QString      uri;
00399 
00400        req.setOperation(IPP_PRINT_JOB);
00401        uri = printerURI(p);
00402        req.addURI(IPP_TAG_OPERATION,"printer-uri",uri);
00403        req.addMime(IPP_TAG_OPERATION,"document-format","application/postscript");
00404        if (!CupsInfos::self()->login().isEmpty()) req.addName(IPP_TAG_OPERATION,"requesting-user-name",CupsInfos::self()->login());
00405        req.addName(IPP_TAG_OPERATION,"job-name",QString::fromLatin1("KDE Print Test"));
00406        if (req.doFileRequest("/printers/",testpage))
00407        return true;
00408        reportIppError(&req);
00409        return false;
00410        */
00411 }
00412 
00413 void KMCupsManager::listPrinters()
00414 {
00415     loadServerPrinters();
00416 }
00417 
00418 void KMCupsManager::loadServerPrinters()
00419 {
00420     IppRequest  req;
00421     QStringList keys;
00422 
00423     // get printers
00424     req.setOperation(CUPS_GET_PRINTERS);
00425     keys.append("printer-name");
00426     keys.append("printer-type");
00427     keys.append("printer-state");
00428     // location needed for filtering
00429     keys.append("printer-location");
00430     keys.append("printer-uri-supported");
00431     keys.append("printer-is-accepting-jobs");
00432     req.addKeyword(IPP_TAG_OPERATION,"requested-attributes",keys);
00433 
00434     // filtering by username (hides printers user doesn't have allowance to use)
00435     req.addName(IPP_TAG_OPERATION, "requesting-user-name", QString(cupsUser()));
00436 
00437     if (req.doRequest("/printers/"))
00438     {
00439         processRequest(&req);
00440 
00441         // get classes
00442         req.init();
00443         req.setOperation(CUPS_GET_CLASSES);
00444         req.addKeyword(IPP_TAG_OPERATION,"requested-attributes",keys);
00445 
00446         if (req.doRequest("/classes/"))
00447         {
00448             processRequest(&req);
00449 
00450             // load default
00451             req.init();
00452             req.setOperation(CUPS_GET_DEFAULT);
00453             req.addKeyword(IPP_TAG_OPERATION,"requested-attributes",QString::fromLatin1("printer-name"));
00454             if (req.doRequest("/printers/"))
00455             {
00456                 QString s = QString::null;
00457                 req.name("printer-name",s);
00458                 setHardDefault(findPrinter(s));
00459             }
00460             // This request may fails for example if no printer is defined. Just
00461             // discard the error message. Indeed as we successfully got printers
00462             // and classes, the most probable reason why this request may fail is
00463             // because of no printer defined. The best would be to actually check
00464             // there's no printer (TODO).
00465             return;
00466         }
00467     }
00468 
00469     // something went wrong if we get there, report the error
00470     reportIppError(&req);
00471 }
00472 
00473 void KMCupsManager::processRequest(IppRequest* req)
00474 {
00475     ipp_attribute_t *attr = req->first();
00476     KMPrinter   *printer = new KMPrinter();
00477     while (attr)
00478     {
00479         QString attrname(attr->name);
00480         if (attrname == "printer-name")
00481         {
00482             QString value = QString::fromLocal8Bit(attr->values[0].string.text);
00483             printer->setName(value);
00484             printer->setPrinterName(value);
00485         }
00486         else if (attrname == "printer-type")
00487         {
00488             int value = attr->values[0].integer;
00489             printer->setType(0);
00490             printer->addType(((value & CUPS_PRINTER_CLASS) || (value & CUPS_PRINTER_IMPLICIT) ? KMPrinter::Class : KMPrinter::Printer));
00491             if ((value & CUPS_PRINTER_REMOTE)) printer->addType(KMPrinter::Remote);
00492             if ((value & CUPS_PRINTER_IMPLICIT)) printer->addType(KMPrinter::Implicit);
00493 
00494             // convert printer-type attribute
00495             printer->setPrinterCap( ( value & CUPS_PRINTER_OPTIONS ) >> 2 );
00496         }
00497         else if (attrname == "printer-state")
00498         {
00499             switch (attr->values[0].integer)
00500             {
00501                 case IPP_PRINTER_IDLE: printer->setState(KMPrinter::Idle); break;
00502                 case IPP_PRINTER_PROCESSING: printer->setState(KMPrinter::Processing); break;
00503                 case IPP_PRINTER_STOPPED: printer->setState(KMPrinter::Stopped); break;
00504             }
00505         }
00506         else if (attrname == "printer-uri-supported")
00507         {
00508             printer->setUri(KURL(attr->values[0].string.text));
00509         }
00510         else if (attrname == "printer-location")
00511         {
00512             printer->setLocation(QString::fromLocal8Bit(attr->values[0].string.text));
00513         }
00514         else if (attrname == "printer-is-accepting-jobs")
00515         {
00516             printer->setAcceptJobs(attr->values[0].boolean);
00517         }
00518         if (attrname.isEmpty() || attr == req->last())
00519         {
00520             addPrinter(printer);
00521             printer = new KMPrinter();
00522         }
00523         attr = attr->next;
00524     }
00525     delete printer;
00526 }
00527 
00528 DrMain* KMCupsManager::loadPrinterDriver(KMPrinter *p, bool)
00529 {
00530     if (!p) 
00531         return NULL;
00532 
00533     if (p->isClass(true)) 
00534     {
00535         KMPrinter *first_class_member = NULL;
00536         /* find the first printer in the class */
00537         first_class_member = findPrinter(p->members().first());
00538       
00539         if (first_class_member == NULL) 
00540         {
00541             /* we didn't find a printer in the class */
00542             return NULL;
00543         }
00544         else
00545         {
00546             p = first_class_member;
00547         }
00548     }
00549 
00550     QString fname = downloadDriver(p);
00551     DrMain  *driver(0);
00552     if (!fname.isEmpty())
00553     {
00554         driver = loadDriverFile(fname);
00555         if (driver)
00556             driver->set("temporary",fname);
00557     }
00558 
00559     return driver;
00560 }
00561 
00562 DrMain* KMCupsManager::loadFileDriver(const QString& filename)
00563 {
00564     if (filename.startsWith("ppd:"))
00565         return loadDriverFile(filename.mid(4));
00566     else if (filename.startsWith("foomatic/"))
00567         return loadMaticDriver(filename);
00568     else
00569         return loadDriverFile(filename);
00570 }
00571 
00572 DrMain* KMCupsManager::loadMaticDriver(const QString& drname)
00573 {
00574     QStringList comps = QStringList::split('/', drname, false);
00575     QString tmpFile = locateLocal("tmp", "foomatic_" + kapp->randomString(8));
00576     QString PATH = getenv("PATH") + QString::fromLatin1(":/usr/sbin:/usr/local/sbin:/opt/sbin:/opt/local/sbin");
00577     QString exe = KStandardDirs::findExe("foomatic-datafile", PATH);
00578     if (exe.isEmpty())
00579     {
00580         setErrorMsg(i18n("Unable to find the executable foomatic-datafile "
00581                     "in your PATH. Check that Foomatic is correctly installed."));
00582         return NULL;
00583     }
00584 
00585     KPipeProcess    in;
00586     QFile       out(tmpFile);
00587     QString cmd = KProcess::quote(exe);
00588     cmd += " -t cups -d ";
00589     cmd += KProcess::quote(comps[2]);
00590     cmd += " -p ";
00591     cmd += KProcess::quote(comps[1]);
00592     if (in.open(cmd) && out.open(IO_WriteOnly))
00593     {
00594         QTextStream tin(&in), tout(&out);
00595         QString line;
00596         while (!tin.atEnd())
00597         {
00598             line = tin.readLine();
00599             tout << line << endl;
00600         }
00601         in.close();
00602         out.close();
00603 
00604         DrMain  *driver = loadDriverFile(tmpFile);
00605         if (driver)
00606         {
00607             driver->set("template", tmpFile);
00608             driver->set("temporary", tmpFile);
00609             return driver;
00610         }
00611     }
00612     setErrorMsg(i18n("Unable to create the Foomatic driver [%1,%2]. "
00613                 "Either that driver does not exist, or you don't have "
00614                 "the required permissions to perform that operation.").arg(comps[1]).arg(comps[2]));
00615     QFile::remove(tmpFile);
00616     return NULL;
00617 }
00618 
00619 DrMain* KMCupsManager::loadDriverFile(const QString& fname)
00620 {
00621     if (QFile::exists(fname))
00622     {
00623         QString msg; /* possible error message */
00624         DrMain *driver = PPDLoader::loadDriver( fname, &msg );
00625         if ( driver )
00626         {
00627             driver->set( "template", fname );
00628             // FIXME: should fix option in group "install"
00629         }
00630         else
00631             setErrorMsg( msg );
00632         return driver;
00633     }
00634     return NULL;
00635 }
00636 
00637 void KMCupsManager::saveDriverFile(DrMain *driver, const QString& filename)
00638 {
00639     kdDebug( 500 ) << "Saving PPD file with template=" << driver->get( "template" ) << endl;
00640     QIODevice *in = KFilterDev::deviceForFile( driver->get( "template" ) );
00641     QFile   out(filename);
00642     if (in && in->open(IO_ReadOnly) && out.open(IO_WriteOnly))
00643     {
00644         QTextStream tin(in), tout(&out);
00645         QString     line, keyword;
00646         bool        isnumeric(false);
00647         DrBase      *opt(0);
00648 
00649         while (!tin.eof())
00650         {
00651             line = tin.readLine();
00652             if (line.startsWith("*% COMDATA #"))
00653             {
00654                 int p(-1), q(-1);
00655                 if ((p=line.find("'name'")) != -1)
00656                 {
00657                     p = line.find('\'',p+6)+1;
00658                     q = line.find('\'',p);
00659                     keyword = line.mid(p,q-p);
00660                     opt = driver->findOption(keyword);
00661                     if (opt && (opt->type() == DrBase::Integer || opt->type() == DrBase::Float))
00662                         isnumeric = true;
00663                     else
00664                         isnumeric = false;
00665                 }
00666                 /*else if ((p=line.find("'type'")) != -1)
00667                 {
00668                     p = line.find('\'',p+6)+1;
00669                     if (line.find("float",p) != -1 || line.find("int",p) != -1)
00670                         isnumeric = true;
00671                     else
00672                         isnumeric = false;
00673                 }*/
00674                 else if ((p=line.find("'default'")) != -1 && !keyword.isEmpty() && opt && isnumeric)
00675                 {
00676                     QString prefix = line.left(p+9);
00677                     tout << prefix << " => '" << opt->valueText() << '\'';
00678                     if (line.find(',',p) != -1)
00679                         tout << ',';
00680                     tout << endl;
00681                     continue;
00682                 }
00683                 tout << line << endl;
00684             }
00685             else if (line.startsWith("*Default"))
00686             {
00687                 int p = line.find(':',8);
00688                 keyword = line.mid(8,p-8);
00689                 DrBase *bopt = 0;
00690                 if ( keyword == "PageRegion" || keyword == "ImageableArea" || keyword == "PaperDimension" )
00691                     bopt = driver->findOption( QString::fromLatin1( "PageSize" ) );
00692                 else
00693                     bopt = driver->findOption( keyword );
00694                 if (bopt)
00695                     switch (bopt->type())
00696                     {
00697                         case DrBase::List:
00698                         case DrBase::Boolean:
00699                             {
00700                                 DrListOption    *opt = static_cast<DrListOption*>(bopt);
00701                                 if (opt && opt->currentChoice())
00702                                     tout << "*Default" << keyword << ": " << opt->currentChoice()->name() << endl;
00703                                 else
00704                                     tout << line << endl;
00705                             }
00706                             break;
00707                         case DrBase::Integer:
00708                             {
00709                                 DrIntegerOption *opt = static_cast<DrIntegerOption*>(bopt);
00710                                 tout << "*Default" << keyword << ": " << opt->fixedVal() << endl;
00711                             }
00712                             break;
00713                         case DrBase::Float:
00714                             {
00715                                 DrFloatOption   *opt = static_cast<DrFloatOption*>(bopt);
00716                                 tout << "*Default" << keyword << ": " << opt->fixedVal() << endl;
00717                             }
00718                             break;
00719                         default:
00720                             tout << line << endl;
00721                             break;
00722                     }
00723                 else
00724                     tout << line << endl;
00725             }
00726             else
00727                 tout << line << endl;
00728         }
00729     }
00730     delete in;
00731 }
00732 
00733 bool KMCupsManager::savePrinterDriver(KMPrinter *p, DrMain *d)
00734 {
00735     QString tmpfilename = locateLocal("tmp","print_") + kapp->randomString(8);
00736 
00737     // first save the driver in a temporary file
00738     saveDriverFile(d,tmpfilename);
00739 
00740     // then send a request
00741     IppRequest  req;
00742     QString     uri;
00743     bool        result(false);
00744 
00745     req.setOperation(CUPS_ADD_PRINTER);
00746     uri = printerURI(p, true);
00747     req.addURI(IPP_TAG_OPERATION,"printer-uri",uri);
00748     result = req.doFileRequest("/admin/",tmpfilename);
00749 
00750     // remove temporary file
00751     QFile::remove(tmpfilename);
00752 
00753     if (!result)
00754         reportIppError(&req);
00755     return result;
00756 }
00757 
00758 void* KMCupsManager::loadCupsdConfFunction(const char *name)
00759 {
00760     if (!m_cupsdconf)
00761     {
00762         m_cupsdconf = KLibLoader::self()->library("cupsdconf");
00763         if (!m_cupsdconf)
00764         {
00765             setErrorMsg(i18n("Library cupsdconf not found. Check your installation."));
00766             return NULL;
00767         }
00768     }
00769     void*   func = m_cupsdconf->symbol(name);
00770     if (!func)
00771         setErrorMsg(i18n("Symbol %1 not found in cupsdconf library.").arg(name));
00772     return func;
00773 }
00774 
00775 void KMCupsManager::unloadCupsdConf()
00776 {
00777     if (m_cupsdconf)
00778     {
00779         KLibLoader::self()->unloadLibrary("libcupsdconf");
00780         m_cupsdconf = 0;
00781     }
00782 }
00783 
00784 bool KMCupsManager::restartServer()
00785 {
00786     QString msg;
00787     bool (*f1)(QString&) = (bool(*)(QString&))loadCupsdConfFunction("restartServer");
00788     bool    result(false);
00789     if (f1)
00790     {
00791         result = f1(msg);
00792         if (!result) setErrorMsg(msg);
00793     }
00794     unloadCupsdConf();
00795     return result;
00796 }
00797 
00798 bool KMCupsManager::configureServer(QWidget *parent)
00799 {
00800     QString msg;
00801     bool (*f2)(QWidget*, QString&) = (bool(*)(QWidget*, QString&))loadCupsdConfFunction("configureServer");
00802     bool    result(false);
00803     if (f2)
00804     {
00805         result = f2(parent, msg);
00806         if ( !result )
00807             setErrorMsg( msg );
00808     }
00809     unloadCupsdConf();
00810     return result;
00811 }
00812 
00813 QStringList KMCupsManager::detectLocalPrinters()
00814 {
00815     QStringList list;
00816     IppRequest  req;
00817     req.setOperation(CUPS_GET_DEVICES);
00818     if (req.doRequest("/"))
00819     {
00820         QString desc, uri, printer, cl;
00821         ipp_attribute_t *attr = req.first();
00822         while (attr)
00823         {
00824             QString attrname(attr->name);
00825             if (attrname == "device-info") desc = attr->values[0].string.text;
00826             else if (attrname == "device-make-and-model") printer = attr->values[0].string.text;
00827             else if (attrname == "device-uri") uri = attr->values[0].string.text;
00828             else if ( attrname == "device-class" ) cl = attr->values[ 0 ].string.text;
00829             if (attrname.isEmpty() || attr == req.last())
00830             {
00831                 if (!uri.isEmpty())
00832                 {
00833                     if (printer == "Unknown") printer = QString::null;
00834                     list << cl << uri << desc << printer;
00835                 }
00836                 uri = desc = printer = cl = QString::null;
00837             }
00838             attr = attr->next;
00839         }
00840     }
00841     return list;
00842 }
00843 
00844 void KMCupsManager::createPluginActions(KActionCollection *coll)
00845 {
00846     KAction *act = new KAction(i18n("&Export Driver..."), "kdeprint_uploadsmb", 0, this, SLOT(exportDriver()), coll, "plugin_export_driver");
00847     act->setGroup("plugin");
00848     act = new KAction(i18n("&Printer IPP Report"), "kdeprint_report", 0, this, SLOT(printerIppReport()), coll, "plugin_printer_ipp_report");
00849     act->setGroup("plugin");
00850 }
00851 
00852 void KMCupsManager::validatePluginActions(KActionCollection *coll, KMPrinter *pr)
00853 {
00854     // save selected printer for future use in slots
00855     m_currentprinter = pr;
00856     coll->action("plugin_export_driver")->setEnabled(pr && pr->isLocal() && !pr->isClass(true) && !pr->isSpecial());
00857     coll->action("plugin_printer_ipp_report")->setEnabled(pr && !pr->isSpecial());
00858 }
00859 
00860 void KMCupsManager::exportDriver()
00861 {
00862     if (m_currentprinter && m_currentprinter->isLocal() &&
00863         !m_currentprinter->isClass(true) && !m_currentprinter->isSpecial())
00864     {
00865         QString path = cupsInstallDir();
00866         if (path.isEmpty())
00867             path = "/usr/share/cups";
00868         else
00869             path += "/share/cups";
00870         CupsAddSmb::exportDest(m_currentprinter->printerName(), path);
00871     }
00872 }
00873 
00874 void KMCupsManager::printerIppReport()
00875 {
00876     if (m_currentprinter && !m_currentprinter->isSpecial())
00877     {
00878         IppRequest  req;
00879         QString uri;
00880 
00881         req.setOperation(IPP_GET_PRINTER_ATTRIBUTES);
00882         uri = printerURI(m_currentprinter, true);
00883         req.addURI(IPP_TAG_OPERATION,"printer-uri",uri);
00884         /*
00885         if (!m_currentprinter->uri().isEmpty())
00886         {
00887             req.setHost(m_currentprinter->uri().host());
00888             req.setPort(m_currentprinter->uri().port());
00889         }
00890         */
00891         req.dump(2);
00892         if (req.doRequest("/printers/"))
00893         {
00894             ippReport(req, IPP_TAG_PRINTER, i18n("IPP Report for %1").arg(m_currentprinter->printerName()));
00895         }
00896         else
00897         {
00898             KMessageBox::error(0, "<p>"+i18n("Unable to retrieve printer information. Error received:")+"</p>"+req.statusMessage());
00899         }
00900     }
00901 }
00902 
00903 void KMCupsManager::ippReport(IppRequest& req, int group, const QString& caption)
00904 {
00905     IppReportDlg::report(&req, group, caption);
00906 }
00907 
00908 QString KMCupsManager::stateInformation()
00909 {
00910     return QString("%1: %2:%3")
00911         .arg(i18n("Server"))
00912         .arg(CupsInfos::self()->host())
00913         .arg(CupsInfos::self()->port());
00914 }
00915 
00916 void KMCupsManager::checkUpdatePossibleInternal()
00917 {
00918     kdDebug(500) << "Checking for update possible" << endl;
00919     delete m_socket;
00920     /*m_socket = new KExtendedSocket( CupsInfos::self()->host(), CupsInfos::self()->port() );
00921     connect( m_socket, SIGNAL( connectionSuccess() ), SLOT( slotConnectionSuccess() ) );
00922     connect( m_socket, SIGNAL( connectionFailed( int ) ), SLOT( slotConnectionFailed( int ) ) );
00923     m_socket->setTimeout( 1 );*/
00924     m_socket = new QSocket( this );
00925     connect( m_socket, SIGNAL( connected() ), SLOT( slotConnectionSuccess() ) );
00926     connect( m_socket, SIGNAL( error( int ) ), SLOT( slotConnectionFailed( int ) ) );
00927     trials = 5;
00928     QTimer::singleShot( 1, this, SLOT( slotAsyncConnect() ) );
00929 }
00930 
00931 void KMCupsManager::slotConnectionSuccess()
00932 {
00933     kdDebug(500) << "Connection success, trying to send a request..." << endl;
00934     m_socket->close();
00935 
00936     IppRequest req;
00937     req.setOperation( CUPS_GET_PRINTERS );
00938     req.addKeyword( IPP_TAG_OPERATION, "requested-attributes", QString::fromLatin1( "printer-name" ) );
00939     if ( req.doRequest( "/printers/" ) )
00940         setUpdatePossible( true );
00941     else
00942     {
00943         kdDebug(500) << "Unable to get printer list" << endl;
00944         if ( trials > 0 )
00945         {
00946             trials--;
00947             QTimer::singleShot( 1000, this, SLOT( slotAsyncConnect() ) );
00948         }
00949         else
00950         {
00951             setErrorMsg( i18n( "Connection to CUPS server failed. Check that the CUPS server is correctly installed and running. "
00952                 "Error: %1." ).arg( i18n( "the IPP request failed for an unknown reason" ) ) );
00953             setUpdatePossible( false );
00954         }
00955     }
00956 }
00957 
00958 void KMCupsManager::slotAsyncConnect()
00959 {
00960     kdDebug(500) << "Starting async connect" << endl;
00961     //m_socket->startAsyncConnect();
00962     m_socket->connectToHost( CupsInfos::self()->host(), CupsInfos::self()->port() );
00963 }
00964 
00965 void KMCupsManager::slotConnectionFailed( int errcode )
00966 {
00967     kdDebug(500) << "Connection failed trials=" << trials << endl;
00968     if ( trials > 0 )
00969     {
00970         //m_socket->setTimeout( ++to );
00971         //m_socket->cancelAsyncConnect();
00972         trials--;
00973         m_socket->close();
00974         QTimer::singleShot( 1000, this, SLOT( slotAsyncConnect() ) );
00975         return;
00976     }
00977 
00978     setErrorMsg( i18n( "Connection to CUPS server failed. Check that the CUPS server is correctly installed and running. "
00979                 "Error: %1." ).arg( errcode == QSocket::ErrConnectionRefused ? i18n( "connection refused" ) : i18n( "host not found" ) ) );
00980     setUpdatePossible( false );
00981 }
00982 
00983 void KMCupsManager::hostPingSlot() {
00984     m_hostSuccess = true;
00985     m_lookupDone = true;
00986 }
00987 
00988 void KMCupsManager::hostPingFailedSlot() {
00989     m_hostSuccess = false;
00990     m_lookupDone = true;
00991 }
00992 
00993 //*****************************************************************************************************
00994 
00995 void extractMaticData(QString& buf, const QString& filename)
00996 {
00997     QFile   f(filename);
00998     if (f.exists() && f.open(IO_ReadOnly))
00999     {
01000         QTextStream t(&f);
01001         QString     line;
01002         while (!t.eof())
01003         {
01004             line = t.readLine();
01005             if (line.startsWith("*% COMDATA #"))
01006                 buf.append(line.right(line.length()-12)).append('\n');
01007         }
01008     }
01009 }
01010 
01011 QString printerURI(KMPrinter *p, bool use)
01012 {
01013     QString uri;
01014     if (use && !p->uri().isEmpty())
01015         uri = p->uri().prettyURL();
01016     else
01017         uri = QString("ipp://%1:%2/%4/%3").arg(CupsInfos::self()->host()).arg(CupsInfos::self()->port()).arg(p->printerName()).arg((p->isClass(false) ? "classes" : "printers"));
01018     return uri;
01019 }
01020 
01021 QString downloadDriver(KMPrinter *p)
01022 {
01023     QString driverfile, prname = p->printerName();
01024     bool    changed(false);
01025 
01026     /*
01027     if (!p->uri().isEmpty())
01028     {
01029         // try to load the driver from the host:port
01030         // specified in its URI. Doing so may also change
01031         // the printer name to use. Note that for remote
01032         // printer, this operation is read-only, no counterpart
01033         // for saving operation.
01034         cupsSetServer(p->uri().host().local8Bit());
01035         ippSetPort(p->uri().port());
01036         // strip any "@..." from the printer name
01037         prname = prname.replace(QRegExp("@.*"), "");
01038         changed = true;
01039     }
01040     */
01041 
01042     // download driver
01043     driverfile = cupsGetPPD(prname.local8Bit());
01044 
01045     // restore host:port (if they have changed)
01046     if (changed)
01047     {
01048         cupsSetServer(CupsInfos::self()->host().local8Bit());
01049         ippSetPort(CupsInfos::self()->port());
01050     }
01051 
01052     return driverfile;
01053 }
01054 
01055 #include "kmcupsmanager.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys