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