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