kmcupsjobmanager.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 "kmcupsjobmanager.h" 00021 #include "kmcupsmanager.h" 00022 #include "kmjob.h" 00023 #include "cupsinfos.h" 00024 #include "ipprequest.h" 00025 #include "pluginaction.h" 00026 #include "kprinter.h" 00027 #include "kprinterpropertydialog.h" 00028 #include "kmuimanager.h" 00029 #include "kmfactory.h" 00030 #include "kpdriverpage.h" 00031 #include "kpschedulepage.h" 00032 #include "kpcopiespage.h" 00033 #include "kptagspage.h" 00034 00035 #include <klocale.h> 00036 #include <kdebug.h> 00037 #include <kurl.h> 00038 00039 #include "config.h" 00040 00041 KMCupsJobManager::KMCupsJobManager(TQObject *parent, const char *name, const TQStringList & /*args*/) 00042 : KMJobManager(parent,name) 00043 { 00044 } 00045 00046 KMCupsJobManager::~KMCupsJobManager() 00047 { 00048 } 00049 00050 int KMCupsJobManager::actions() 00051 { 00052 return KMJob::All; 00053 } 00054 00055 bool KMCupsJobManager::sendCommandSystemJob(const TQPtrList<KMJob>& jobs, int action, const TQString& argstr) 00056 { 00057 IppRequest req; 00058 TQString uri; 00059 bool value(true); 00060 00061 TQPtrListIterator<KMJob> it(jobs); 00062 for (;it.current() && value;++it) 00063 { 00064 // hypothesis: job operation are always done on local jobs. The only operation 00065 // allowed on remote jobs is listing (done elsewhere). 00066 00067 req.addURI(IPP_TAG_OPERATION,"job-uri",it.current()->uri()); 00068 req.addName(IPP_TAG_OPERATION,"requesting-user-name",CupsInfos::self()->login()); 00069 /* 00070 TQString jobHost; 00071 if (!it.current()->uri().isEmpty()) 00072 { 00073 KURL url(it.current()->uri()); 00074 req.setHost(url.host()); 00075 req.setPort(url.port()); 00076 jobHost = url.host(); 00077 } 00078 */ 00079 00080 switch (action) 00081 { 00082 case KMJob::Remove: 00083 req.setOperation(IPP_CANCEL_JOB); 00084 break; 00085 case KMJob::Hold: 00086 req.setOperation(IPP_HOLD_JOB); 00087 break; 00088 case KMJob::Resume: 00089 req.setOperation(IPP_RELEASE_JOB); 00090 break; 00091 case KMJob::Restart: 00092 req.setOperation(IPP_RESTART_JOB); 00093 break; 00094 case KMJob::Move: 00095 if (argstr.isEmpty()) return false; 00096 req.setOperation(CUPS_MOVE_JOB); 00097 uri = 00098 TQString::fromLatin1("ipp://%1/printers/%2").arg(CupsInfos::self()->hostaddr(), 00099 argstr); 00100 req.addURI(IPP_TAG_OPERATION, "job-printer-uri", uri); 00101 break; 00102 default: 00103 return false; 00104 } 00105 00106 if (!(value = req.doRequest("/jobs/"))) 00107 KMManager::self()->setErrorMsg(req.statusMessage()); 00108 } 00109 00110 return value; 00111 } 00112 00113 bool KMCupsJobManager::listJobs(const TQString& prname, KMJobManager::JobType type, int limit) 00114 { 00115 IppRequest req; 00116 TQStringList keys; 00117 CupsInfos *infos = CupsInfos::self(); 00118 00119 // wanted attributes 00120 keys.append("job-id"); 00121 keys.append("job-uri"); 00122 keys.append("job-name"); 00123 keys.append("job-state"); 00124 keys.append("job-printer-uri"); 00125 keys.append("job-k-octets"); 00126 keys.append("job-originating-user-name"); 00127 keys.append("job-k-octets-completed"); 00128 keys.append("job-media-sheets"); 00129 keys.append("job-media-sheets-completed"); 00130 keys.append("job-priority"); 00131 keys.append("job-billing"); 00132 00133 req.setOperation(IPP_GET_JOBS); 00134 00135 // add printer-uri 00136 KMPrinter *mp = KMManager::self()->findPrinter(prname); 00137 if (!mp) 00138 return false; 00139 00140 if (!mp->uri().isEmpty()) 00141 { 00142 req.addURI(IPP_TAG_OPERATION, "printer-uri", mp->uri().prettyURL()); 00143 /* 00144 req.setHost(mp->uri().host()); 00145 req.setPort(mp->uri().port()); 00146 */ 00147 } 00148 else 00149 req.addURI(IPP_TAG_OPERATION, "printer-uri", TQString("ipp://%1/%2/%3").arg(infos->hostaddr(), 00150 (mp&&mp->isClass())?"classes":"printers", prname)); 00151 00152 // other attributes 00153 req.addKeyword(IPP_TAG_OPERATION, "requested-attributes", keys); 00154 if (type == KMJobManager::CompletedJobs) 00155 req.addKeyword(IPP_TAG_OPERATION,"which-jobs",TQString::fromLatin1("completed")); 00156 if (limit > 0) 00157 req.addInteger(IPP_TAG_OPERATION,"limit",limit); 00158 00159 // send request 00160 if (req.doRequest("/")) 00161 parseListAnswer(req, mp); 00162 else 00163 return false; 00164 00165 return true; 00166 } 00167 00168 void KMCupsJobManager::parseListAnswer(IppRequest& req, KMPrinter *pr) 00169 { 00170 ipp_attribute_t *attr = req.first(); 00171 ipp_attribute_t *nextAttr; 00172 KMJob *job = new KMJob(); 00173 TQString uri; 00174 while (attr) 00175 { 00176 #ifdef HAVE_CUPS_1_6 00177 TQString name(ippGetName(attr)); 00178 if (name == "job-id") job->setId(ippGetInteger(attr, 0)); 00179 else if (name == "job-uri") job->setUri(TQString::fromLocal8Bit(ippGetString(attr, 0, NULL))); 00180 else if (name == "job-name") job->setName(TQString::fromLocal8Bit(ippGetString(attr, 0, NULL))); 00181 else if (name == "job-state") 00182 { 00183 switch (ippGetInteger(attr, 0)) 00184 { 00185 case IPP_JOB_PENDING: 00186 job->setState(KMJob::Queued); 00187 break; 00188 case IPP_JOB_HELD: 00189 job->setState(KMJob::Held); 00190 break; 00191 case IPP_JOB_PROCESSING: 00192 job->setState(KMJob::Printing); 00193 break; 00194 case IPP_JOB_STOPPED: 00195 job->setState(KMJob::Error); 00196 break; 00197 case IPP_JOB_CANCELLED: 00198 job->setState(KMJob::Cancelled); 00199 break; 00200 case IPP_JOB_ABORTED: 00201 job->setState(KMJob::Aborted); 00202 break; 00203 case IPP_JOB_COMPLETED: 00204 job->setState(KMJob::Completed); 00205 break; 00206 default: 00207 job->setState(KMJob::Unknown); 00208 break; 00209 } 00210 } 00211 else if (name == "job-k-octets") job->setSize(ippGetInteger(attr, 0)); 00212 else if (name == "job-originating-user-name") job->setOwner(TQString::fromLocal8Bit(ippGetString(attr, 0, NULL))); 00213 else if (name == "job-k-octets-completed") job->setProcessedSize(ippGetInteger(attr, 0)); 00214 else if (name == "job-media-sheets") job->setPages(ippGetInteger(attr, 0)); 00215 else if (name == "job-media-sheets-completed") job->setProcessedPages(ippGetInteger(attr, 0)); 00216 else if (name == "job-printer-uri" && !pr->isRemote()) 00217 { 00218 TQString str(ippGetString(attr, 0, NULL)); 00219 int p = str.findRev('/'); 00220 if (p != -1) 00221 job->setPrinter(str.mid(p+1)); 00222 } 00223 else if (name == "job-priority") 00224 { 00225 job->setAttribute(0, TQString::fromLatin1("%1").arg(ippGetInteger(attr, 0), 3)); 00226 } 00227 else if (name == "job-billing") 00228 { 00229 job->setAttributeCount(2); 00230 job->setAttribute(1, TQString::fromLocal8Bit(ippGetString(attr, 0, NULL))); 00231 } 00232 00233 nextAttr = ippNextAttribute(req.request()); 00234 if (name.isEmpty() || (!nextAttr)) 00235 { 00236 if (job->printer().isEmpty()) 00237 job->setPrinter(pr->printerName()); 00238 job->setRemote(pr->isRemote()); 00239 addJob(job); // don't use job after this call !!! 00240 job = new KMJob(); 00241 } 00242 attr = nextAttr; 00243 #else // HAVE_CUPS_1_6 00244 TQString name(attr->name); 00245 if (name == "job-id") job->setId(attr->values[0].integer); 00246 else if (name == "job-uri") job->setUri(TQString::fromLocal8Bit(attr->values[0].string.text)); 00247 else if (name == "job-name") job->setName(TQString::fromLocal8Bit(attr->values[0].string.text)); 00248 else if (name == "job-state") 00249 { 00250 switch (attr->values[0].integer) 00251 { 00252 case IPP_JOB_PENDING: 00253 job->setState(KMJob::Queued); 00254 break; 00255 case IPP_JOB_HELD: 00256 job->setState(KMJob::Held); 00257 break; 00258 case IPP_JOB_PROCESSING: 00259 job->setState(KMJob::Printing); 00260 break; 00261 case IPP_JOB_STOPPED: 00262 job->setState(KMJob::Error); 00263 break; 00264 case IPP_JOB_CANCELLED: 00265 job->setState(KMJob::Cancelled); 00266 break; 00267 case IPP_JOB_ABORTED: 00268 job->setState(KMJob::Aborted); 00269 break; 00270 case IPP_JOB_COMPLETED: 00271 job->setState(KMJob::Completed); 00272 break; 00273 default: 00274 job->setState(KMJob::Unknown); 00275 break; 00276 } 00277 } 00278 else if (name == "job-k-octets") job->setSize(attr->values[0].integer); 00279 else if (name == "job-originating-user-name") job->setOwner(TQString::fromLocal8Bit(attr->values[0].string.text)); 00280 else if (name == "job-k-octets-completed") job->setProcessedSize(attr->values[0].integer); 00281 else if (name == "job-media-sheets") job->setPages(attr->values[0].integer); 00282 else if (name == "job-media-sheets-completed") job->setProcessedPages(attr->values[0].integer); 00283 else if (name == "job-printer-uri" && !pr->isRemote()) 00284 { 00285 TQString str(attr->values[0].string.text); 00286 int p = str.findRev('/'); 00287 if (p != -1) 00288 job->setPrinter(str.mid(p+1)); 00289 } 00290 else if (name == "job-priority") 00291 { 00292 job->setAttribute(0, TQString::fromLatin1("%1").arg(attr->values[0].integer, 3)); 00293 } 00294 else if (name == "job-billing") 00295 { 00296 job->setAttributeCount(2); 00297 job->setAttribute(1, TQString::fromLocal8Bit(attr->values[0].string.text)); 00298 } 00299 00300 if (name.isEmpty() || attr == req.last()) 00301 { 00302 if (job->printer().isEmpty()) 00303 job->setPrinter(pr->printerName()); 00304 job->setRemote(pr->isRemote()); 00305 addJob(job); // don't use job after this call !!! 00306 job = new KMJob(); 00307 } 00308 00309 attr = attr->next; 00310 #endif // HAVE_CUPS_1_6 00311 } 00312 delete job; 00313 } 00314 00315 bool KMCupsJobManager::doPluginAction(int ID, const TQPtrList<KMJob>& jobs) 00316 { 00317 switch (ID) 00318 { 00319 case 0: 00320 if (jobs.count() == 1) 00321 return jobIppReport(jobs.getFirst()); 00322 break; 00323 case 1: 00324 return changePriority(jobs, true); 00325 case 2: 00326 return changePriority(jobs, false); 00327 case 3: 00328 return editJobAttributes(jobs.getFirst()); 00329 } 00330 return false; 00331 } 00332 00333 bool KMCupsJobManager::jobIppReport(KMJob *j) 00334 { 00335 IppRequest req; 00336 00337 req.setOperation(IPP_GET_JOB_ATTRIBUTES); 00338 req.addURI(IPP_TAG_OPERATION, "job-uri", j->uri()); 00339 bool result(true); 00340 /* 00341 if (!j->uri().isEmpty()) 00342 { 00343 KURL url(j->uri()); 00344 req.setHost(url.host()); 00345 req.setPort(url.port()); 00346 } 00347 */ 00348 if ((result=req.doRequest("/"))) 00349 static_cast<KMCupsManager*>(KMManager::self())->ippReport(req, IPP_TAG_JOB, i18n("Job Report")); 00350 else 00351 KMManager::self()->setErrorMsg(i18n("Unable to retrieve job information: ")+req.statusMessage()); 00352 return result; 00353 } 00354 00355 TQValueList<KAction*> KMCupsJobManager::createPluginActions(KActionCollection *coll) 00356 { 00357 TQValueList<KAction*> list; 00358 KAction *act(0); 00359 00360 list << (act = new PluginAction(0, i18n("&Job IPP Report"), "kdeprint_report", 0, coll, "plugin_ipp")); 00361 act->setGroup("plugin"); 00362 list << (act = new PluginAction(1, i18n("&Increase Priority"), "up", 0, coll, "plugin_prioup")); 00363 act->setGroup("plugin"); 00364 list << (act = new PluginAction(2, i18n("&Decrease Priority"), "down", 0, coll, "plugin_priodown")); 00365 act->setGroup("plugin"); 00366 list << (act = new PluginAction(3, i18n("&Edit Attributes..."), "edit", 0, coll, "plugin_editjob")); 00367 act->setGroup("plugin"); 00368 00369 return list; 00370 } 00371 00372 void KMCupsJobManager::validatePluginActions(KActionCollection *coll, const TQPtrList<KMJob>& joblist) 00373 { 00374 TQPtrListIterator<KMJob> it(joblist); 00375 bool flag(true); 00376 for (; it.current(); ++it) 00377 { 00378 flag = (flag && it.current()->type() == KMJob::System 00379 && (it.current()->state() == KMJob::Queued || it.current()->state() == KMJob::Held) 00380 /*&& !it.current()->isRemote()*/); 00381 } 00382 flag = (flag && joblist.count() > 0); 00383 KAction *a; 00384 if ( ( a = coll->action( "plugin_ipp" ) ) ) 00385 a->setEnabled( joblist.count() == 1 ); 00386 if ( ( a = coll->action( "plugin_prioup" ) ) ) 00387 a->setEnabled( flag ); 00388 if ( ( a = coll->action( "plugin_priodown" ) ) ) 00389 a->setEnabled( flag ); 00390 if ( ( a = coll->action( "plugin_editjob" ) ) ) 00391 a->setEnabled( flag && ( joblist.count() == 1 ) ); 00392 } 00393 00394 bool KMCupsJobManager::changePriority(const TQPtrList<KMJob>& jobs, bool up) 00395 { 00396 TQPtrListIterator<KMJob> it(jobs); 00397 bool result(true); 00398 for (; it.current() && result; ++it) 00399 { 00400 int value = it.current()->attribute(0).toInt(); 00401 if (up) value = TQMIN(value+10, 100); 00402 else value = TQMAX(value-10, 1); 00403 00404 IppRequest req; 00405 /* 00406 if (!it.current()->uri().isEmpty()) 00407 { 00408 KURL url(it.current()->uri()); 00409 req.setHost(url.host()); 00410 req.setPort(url.port()); 00411 } 00412 */ 00413 req.setOperation(IPP_SET_JOB_ATTRIBUTES); 00414 req.addURI(IPP_TAG_OPERATION, "job-uri", it.current()->uri()); 00415 req.addName(IPP_TAG_OPERATION, "requesting-user-name", CupsInfos::self()->login()); 00416 req.addInteger(IPP_TAG_JOB, "job-priority", value); 00417 00418 if (!(result = req.doRequest("/jobs/"))) 00419 KMManager::self()->setErrorMsg(i18n("Unable to change job priority: ")+req.statusMessage()); 00420 } 00421 return result; 00422 } 00423 00424 static TQString processRange(const TQString& range) 00425 { 00426 TQStringList l = TQStringList::split(',', range, false); 00427 TQString s; 00428 for (TQStringList::ConstIterator it=l.begin(); it!=l.end(); ++it) 00429 { 00430 s.append(*it); 00431 if ((*it).find('-') == -1) 00432 s.append("-").append(*it); 00433 s.append(","); 00434 } 00435 if (!s.isEmpty()) 00436 s.truncate(s.length()-1); 00437 return s; 00438 } 00439 00440 bool KMCupsJobManager::editJobAttributes(KMJob *j) 00441 { 00442 IppRequest req; 00443 00444 req.setOperation(IPP_GET_JOB_ATTRIBUTES); 00445 req.addURI(IPP_TAG_OPERATION, "job-uri", j->uri()); 00446 /* 00447 if (!j->uri().isEmpty()) 00448 { 00449 KURL url(j->uri()); 00450 req.setHost(url.host()); 00451 req.setPort(url.port()); 00452 } 00453 */ 00454 if (!req.doRequest("/")) 00455 { 00456 KMManager::self()->setErrorMsg(i18n("Unable to retrieve job information: ")+req.statusMessage()); 00457 return false; 00458 } 00459 00460 TQMap<TQString,TQString> opts = req.toMap(IPP_TAG_JOB); 00461 // translate the "Copies" option to non-CUPS syntax 00462 if (opts.contains("copies")) 00463 opts["kde-copies"] = opts["copies"]; 00464 if (opts.contains("page-set")) 00465 opts["kde-pageset"] = (opts["page-set"] == "even" ? "2" : (opts["page-set"] == "odd" ? "1" : "0")); 00466 if (opts.contains("OutputOrder")) 00467 opts["kde-pageorder"] = opts["OutputOrder"]; 00468 if (opts.contains("multiple-document-handling")) 00469 opts["kde-collate"] = (opts["multiple-document-handling"] == "separate-documents-collated-copies" ? "Collate" : "Uncollate"); 00470 if (opts.contains("page-ranges")) 00471 opts["kde-range"] = opts["page-ranges"]; 00472 00473 // find printer and construct dialog 00474 KMPrinter *prt = KMManager::self()->findPrinter(j->printer()); 00475 if (!prt) 00476 { 00477 KMManager::self()->setErrorMsg(i18n("Unable to find printer %1.").arg(j->printer())); 00478 return false; 00479 } 00480 KMManager::self()->completePrinterShort(prt); 00481 KPrinter::ApplicationType oldAppType = KPrinter::applicationType(); 00482 KPrinter::setApplicationType(KPrinter::StandAlone); 00483 KPrinterPropertyDialog dlg(prt); 00484 dlg.setDriver(KMManager::self()->loadPrinterDriver(prt)); 00485 KMFactory::self()->uiManager()->setupPrinterPropertyDialog(&dlg); 00486 KPrinter::setApplicationType( oldAppType ); 00487 if (dlg.driver()) 00488 dlg.addPage(new KPDriverPage(prt, dlg.driver(), &dlg)); 00489 dlg.addPage(new KPCopiesPage(0, &dlg)); 00490 dlg.addPage(new KPSchedulePage(&dlg)); 00491 dlg.addPage(new KPTagsPage(true, &dlg)); 00492 dlg.setOptions(opts); 00493 dlg.enableSaveButton(false); 00494 dlg.setCaption(i18n("Attributes of Job %1@%2 (%3)").arg(j->id()).arg(j->printer()).arg(j->name())); 00495 if (dlg.exec()) 00496 { 00497 opts.clear(); 00498 // include default values to override non-default values 00499 dlg.getOptions(opts, true); 00500 // translate the "Copies" options from non-CUPS syntax 00501 opts["copies"] = opts["kde-copies"]; 00502 opts["OutputOrder"] = opts["kde-pageorder"]; 00503 opts["multiple-document-handling"] = (opts["kde-collate"] == "Collate" ? "separate-documents-collated-copies" : "separate-documents-uncollated-copies"); 00504 opts["page-set"] = (opts["kde-pageset"] == "1" ? "odd" : (opts["kde-pageset"] == "2" ? "even" : "all")); 00505 // it seems CUPS is buggy. Disable page-ranges modification, otherwise nothing gets printed 00506 opts["page-ranges"] = processRange(opts["kde-range"]); 00507 00508 req.init(); 00509 req.setOperation(IPP_SET_JOB_ATTRIBUTES); 00510 req.addURI(IPP_TAG_OPERATION, "job-uri", j->uri()); 00511 req.addName(IPP_TAG_OPERATION, "requesting-user-name", CupsInfos::self()->login()); 00512 req.setMap(opts); 00513 //req.dump(1); 00514 if (!req.doRequest("/jobs/")) 00515 { 00516 KMManager::self()->setErrorMsg(i18n("Unable to set job attributes: ")+req.statusMessage()); 00517 return false; 00518 } 00519 } 00520 00521 return true; 00522 } 00523 00524 #include "kmcupsjobmanager.moc"