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", 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"