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