matichandler.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 "matichandler.h" 00021 #include "printcapentry.h" 00022 #include "kmprinter.h" 00023 #include "matichelper.h" 00024 #include "driver.h" 00025 #include "kpipeprocess.h" 00026 #include "kmmanager.h" 00027 #include "kprinter.h" 00028 #include "lprsettings.h" 00029 #include "util.h" 00030 #include "foomatic2loader.h" 00031 00032 #include <klocale.h> 00033 #include <kstandarddirs.h> 00034 #include <kapplication.h> 00035 #include <kdebug.h> 00036 #include <kprocess.h> 00037 #include <qfile.h> 00038 #include <qtextstream.h> 00039 #include <qregexp.h> 00040 00041 #include <stdlib.h> 00042 #include <sys/wait.h> 00043 00044 MaticHandler::MaticHandler(KMManager *mgr) 00045 : LprHandler("foomatic", mgr) 00046 { 00047 QString PATH = getenv("PATH"); 00048 PATH.append(":/usr/sbin:/usr/local/sbin:/opt/sbin:/opt/local/sbin"); 00049 m_exematicpath = KStandardDirs::findExe("lpdomatic", PATH); 00050 m_ncpath = KStandardDirs::findExe("nc"); 00051 m_smbpath = KStandardDirs::findExe("smbclient"); 00052 m_rlprpath = KStandardDirs::findExe("rlpr"); 00053 } 00054 00055 bool MaticHandler::validate(PrintcapEntry *entry) 00056 { 00057 if (entry) 00058 return (entry->field("if").right(9) == "lpdomatic"); 00059 return false; 00060 } 00061 00062 KMPrinter* MaticHandler::createPrinter(PrintcapEntry *entry) 00063 { 00064 if (entry && validate(entry)) 00065 { 00066 KMPrinter *prt = new KMPrinter; 00067 prt->setName(entry->name); 00068 prt->setPrinterName(entry->name); 00069 prt->setType(KMPrinter::Printer); 00070 //if (entry->field("lp") == "/dev/null" || entry->field("lp").isEmpty()) 00071 // prt->addType(KMPrinter::Remote); 00072 return prt; 00073 } 00074 return NULL; 00075 } 00076 00077 bool MaticHandler::completePrinter(KMPrinter *prt, PrintcapEntry *entry, bool shortmode) 00078 { 00079 QString val = entry->field("lp"); 00080 if (val == "/dev/null" || val.isEmpty()) 00081 { 00082 prt->setLocation(i18n("Network printer")); 00083 } 00084 else 00085 { 00086 prt->setLocation(i18n("Local printer on %1").arg(val)); 00087 KURL url(val); 00088 if (val.find("usb") != -1) 00089 url.setProtocol("usb"); 00090 else 00091 url.setProtocol("parallel"); 00092 prt->setDevice(url.url()); 00093 } 00094 prt->setDescription(entry->aliases.join(", ")); 00095 00096 if (!shortmode) 00097 { 00098 Foomatic2Loader loader; 00099 if ( loader.readFromFile( maticFile( entry ) ) ) 00100 { 00101 QString postpipe = loader.data()[ "POSTPIPE" ].toString(); 00102 if (!postpipe.isEmpty()) 00103 { 00104 KURL url ( parsePostpipe(postpipe) ); 00105 if (!url.isEmpty()) 00106 { 00107 QString ds = QString::fromLatin1("%1 (%2)").arg(prt->location()).arg(url.protocol()); 00108 prt->setDevice(url.url()); 00109 prt->setLocation(ds); 00110 } 00111 } 00112 00113 QMap<QString,QVariant> m = loader.data()[ "VAR" ].toMap(); 00114 if ( !m.isEmpty() ) 00115 { 00116 prt->setManufacturer(m["make"].toString()); 00117 prt->setModel(m["model"].toString()); 00118 prt->setDriverInfo(QString::fromLatin1("%1 %2 (%3)").arg(prt->manufacturer()).arg(prt->model()).arg(m["driver"].toString())); 00119 } 00120 } 00121 } 00122 00123 return true; 00124 } 00125 00126 QString MaticHandler::parsePostpipe(const QString& s) 00127 { 00128 QString url; 00129 int p = s.findRev('|'); 00130 QStringList args = QStringList::split(" ", s.right(s.length()-p-1)); 00131 00132 if (args.count() != 0) 00133 { 00134 // socket printer 00135 if (args[0].right(3) == "/nc") 00136 { 00137 url = "socket://" + args[ 1 ]; 00138 if ( args.count() > 2 ) 00139 url += ":" + args[ 2 ]; 00140 else 00141 url += ":9100"; 00142 } 00143 // smb printer 00144 else if (args[0].right(10) == "/smbclient") 00145 { 00146 QStringList host_components = QStringList::split(QRegExp("/|\\\\\""), args[1], false); 00147 QString workgrp, user, pass; 00148 for (uint i=2; i<args.count(); i++) 00149 { 00150 if (args[i] == "-U") 00151 user = args[++i]; 00152 else if (args[i] == "-W") 00153 workgrp = args[++i]; 00154 else if (args[i][0] != '-' && i == 2) 00155 pass = args[i]; 00156 } 00157 url = buildSmbURI( workgrp, host_components[ 0 ], host_components[ 1 ], user, pass ); 00158 } 00159 // remote printer 00160 else if (args[0].right(5) == "/rlpr") 00161 { 00162 uint i=1; 00163 while (i < args.count()) 00164 { 00165 if (args[i].left(2) != "-P") 00166 i++; 00167 else 00168 { 00169 QString host = (args[i].length() == 2 ? args[i+1] : args[i].right(args[i].length()-2)); 00170 int p = host.find("\\@"); 00171 if (p != -1) 00172 { 00173 url = "lpd://" + host.right(host.length()-p-2) + "/" + host.left(p); 00174 } 00175 break; 00176 } 00177 } 00178 } 00179 } 00180 00181 return url; 00182 } 00183 00184 QString MaticHandler::createPostpipe(const QString& _url) 00185 { 00186 KURL url( _url ); 00187 QString prot = url.protocol(); 00188 QString str; 00189 if (prot == "socket") 00190 { 00191 str += ("| " + m_ncpath); 00192 str += (" " + url.host()); 00193 if (url.port() != 0) 00194 str += (" " + QString::number(url.port())); 00195 } 00196 else if (prot == "lpd") 00197 { 00198 str += ("| " + m_rlprpath + " -q -h"); 00199 QString h = url.host(), p = url.path().mid(1); 00200 str += (" -P " + p + "\\@" + h); 00201 } 00202 else if (prot == "smb") 00203 { 00204 QString work, server, printer, user, passwd; 00205 if ( splitSmbURI( _url, work, server, printer, user, passwd ) ) 00206 { 00207 str += ("| (\\n echo \\\"print -\\\"\\n cat \\n) | " + m_smbpath); 00208 str += (" \\\"//" + server + "/" + printer + "\\\""); 00209 if (!passwd.isEmpty()) 00210 str += (" " + passwd); 00211 if (!user.isEmpty()) 00212 str += (" -U " + user); 00213 if (!work.isEmpty()) 00214 str += (" -W " + work); 00215 str += " -N -P"; 00216 } 00217 } 00218 return str; 00219 } 00220 00221 DrMain* MaticHandler::loadDriver(KMPrinter*, PrintcapEntry *entry, bool) 00222 { 00223 // we need to use a copy of the driver, as the driver 00224 // is not self-contained. If the printer is removed (when 00225 // changing printer name), the template would be also removed 00226 QString origfilename = maticFile(entry); 00227 QString filename = locateLocal("tmp", "foomatic_" + kapp->randomString(8)); 00228 ::system(QFile::encodeName("cp " + KProcess::quote(origfilename) + " " + KProcess::quote(filename))); 00229 DrMain *driver = Foomatic2Loader::loadDriver(filename); 00230 if (driver) 00231 { 00232 driver->set("template", filename); 00233 driver->set("temporary", "true"); 00234 return driver; 00235 } 00236 else 00237 return NULL; 00238 } 00239 00240 DrMain* MaticHandler::loadDbDriver(const QString& path) 00241 { 00242 QStringList comps = QStringList::split('/', path, false); 00243 if (comps.count() < 3 || comps[0] != "foomatic") 00244 { 00245 manager()->setErrorMsg(i18n("Internal error.")); 00246 return NULL; 00247 } 00248 00249 QString tmpFile = locateLocal("tmp", "foomatic_" + kapp->randomString(8)); 00250 QString PATH = getenv("PATH") + QString::fromLatin1(":/usr/sbin:/usr/local/sbin:/opt/sbin:/opt/local/sbin"); 00251 QString exe = KStandardDirs::findExe("foomatic-datafile", PATH); 00252 if (exe.isEmpty()) 00253 { 00254 manager()->setErrorMsg(i18n("Unable to find the executable foomatic-datafile " 00255 "in your PATH. Check that Foomatic is correctly installed.")); 00256 return NULL; 00257 } 00258 00259 KPipeProcess in; 00260 QFile out(tmpFile); 00261 QString cmd = KProcess::quote(exe); 00262 cmd += " -t lpd -d "; 00263 cmd += KProcess::quote(comps[2]); 00264 cmd += " -p "; 00265 cmd += KProcess::quote(comps[1]); 00266 if (in.open(cmd) && out.open(IO_WriteOnly)) 00267 { 00268 QTextStream tin(&in), tout(&out); 00269 QString line; 00270 while (!tin.atEnd()) 00271 { 00272 line = tin.readLine(); 00273 tout << line << endl; 00274 } 00275 in.close(); 00276 out.close(); 00277 00278 DrMain *driver = Foomatic2Loader::loadDriver(tmpFile); 00279 if (driver) 00280 { 00281 driver->set("template", tmpFile); 00282 driver->set("temporary", tmpFile); 00283 return driver; 00284 } 00285 } 00286 manager()->setErrorMsg(i18n("Unable to create the Foomatic driver [%1,%2]. " 00287 "Either that driver does not exist, or you don't have " 00288 "the required permissions to perform that operation.").arg(comps[1]).arg(comps[2])); 00289 return NULL; 00290 } 00291 00292 bool MaticHandler::savePrinterDriver(KMPrinter *prt, PrintcapEntry *entry, DrMain *driver, bool*) 00293 { 00294 QFile tmpFile(locateLocal("tmp", "foomatic_" + kapp->randomString(8))); 00295 QFile inFile(driver->get("template")); 00296 QString outFile = maticFile(entry); 00297 bool result(false); 00298 QString postpipe = createPostpipe(prt->device()); 00299 00300 if (inFile.open(IO_ReadOnly) && tmpFile.open(IO_WriteOnly)) 00301 { 00302 QTextStream tin(&inFile), tout(&tmpFile); 00303 QString line, optname; 00304 int p(-1), q(-1); 00305 if (!postpipe.isEmpty()) 00306 tout << "$postpipe = \"" << postpipe << "\";" << endl; 00307 while (!tin.atEnd()) 00308 { 00309 line = tin.readLine(); 00310 if (line.stripWhiteSpace().startsWith("$postpipe")) 00311 continue; 00312 else if ((p = line.find("'name'")) != -1) 00313 { 00314 p = line.find('\'', p+6)+1; 00315 q = line.find('\'', p); 00316 optname = line.mid(p, q-p); 00317 } 00318 else if ((p = line.find("'default'")) != -1) 00319 { 00320 DrBase *opt = driver->findOption(optname); 00321 if (opt) 00322 { 00323 tout << line.left(p+9) << " => '" << opt->valueText() << "'," << endl; 00324 continue; 00325 } 00326 } 00327 tout << line << endl; 00328 } 00329 inFile.close(); 00330 tmpFile.close(); 00331 00332 QString cmd = "mv " + KProcess::quote(tmpFile.name()) + " " + KProcess::quote(outFile); 00333 int status = ::system(QFile::encodeName(cmd).data()); 00334 QFile::remove(tmpFile.name()); 00335 result = (status != -1 && WEXITSTATUS(status) == 0); 00336 } 00337 00338 if (!result) 00339 manager()->setErrorMsg(i18n("You probably don't have the required permissions " 00340 "to perform that operation.")); 00341 QFile::remove(tmpFile.name()); 00342 if (!result || entry->field("ppdfile").isEmpty()) 00343 return result; 00344 else 00345 return savePpdFile(driver, entry->field("ppdfile")); 00346 } 00347 00348 bool MaticHandler::savePpdFile(DrMain *driver, const QString& filename) 00349 { 00350 QString mdriver(driver->get("matic_driver")), mprinter(driver->get("matic_printer")); 00351 if (mdriver.isEmpty() || mprinter.isEmpty()) 00352 return true; 00353 00354 QString PATH = getenv("PATH") + QString::fromLatin1(":/usr/sbin:/usr/local/sbin:/opt/sbin:/opt/local/sbin"); 00355 QString exe = KStandardDirs::findExe("foomatic-datafile", PATH); 00356 if (exe.isEmpty()) 00357 { 00358 manager()->setErrorMsg(i18n("Unable to find the executable foomatic-datafile " 00359 "in your PATH. Check that Foomatic is correctly installed.")); 00360 return false; 00361 } 00362 00363 KPipeProcess in; 00364 QFile out(filename); 00365 if (in.open(exe + " -t cups -d " + mdriver + " -p " + mprinter) && out.open(IO_WriteOnly)) 00366 { 00367 QTextStream tin(&in), tout(&out); 00368 QString line, optname; 00369 QRegExp re("^\\*Default(\\w+):"), foo("'name'\\s+=>\\s+'(\\w+)'"), foo2("'\\w+'\\s*,\\s*$"); 00370 while (!tin.atEnd()) 00371 { 00372 line = tin.readLine(); 00373 if (line.startsWith("*% COMDATA #")) 00374 { 00375 if (line.find("'default'") != -1) 00376 { 00377 DrBase *opt = (optname.isEmpty() ? NULL : driver->findOption(optname)); 00378 if (opt) 00379 { 00380 line.replace(foo2, "'"+opt->valueText()+"',"); 00381 } 00382 } 00383 else if (foo.search(line) != -1) 00384 optname = foo.cap(1); 00385 } 00386 else if (re.search(line) != -1) 00387 { 00388 DrBase *opt = driver->findOption(re.cap(1)); 00389 if (opt) 00390 { 00391 QString val = opt->valueText(); 00392 if (opt->type() == DrBase::Boolean) 00393 val = (val == "1" ? "True" : "False"); 00394 tout << "*Default" << opt->name() << ": " << val << endl; 00395 continue; 00396 } 00397 } 00398 tout << line << endl; 00399 } 00400 in.close(); 00401 out.close(); 00402 00403 return true; 00404 } 00405 manager()->setErrorMsg(i18n("Unable to create the Foomatic driver [%1,%2]. " 00406 "Either that driver does not exist, or you don't have " 00407 "the required permissions to perform that operation.").arg(mdriver).arg(mprinter)); 00408 00409 return false; 00410 } 00411 00412 PrintcapEntry* MaticHandler::createEntry(KMPrinter *prt) 00413 { 00414 KURL url( prt->device() ); 00415 QString prot = url.protocol(); 00416 if ((prot != "lpd" || m_rlprpath.isEmpty()) && 00417 (prot != "socket" || m_ncpath.isEmpty()) && 00418 (prot != "smb" || m_smbpath.isEmpty()) && 00419 prot != "parallel") 00420 { 00421 manager()->setErrorMsg(i18n("Unsupported backend: %1.").arg(prot)); 00422 return NULL; 00423 } 00424 if (m_exematicpath.isEmpty()) 00425 { 00426 manager()->setErrorMsg(i18n("Unable to find executable lpdomatic. " 00427 "Check that Foomatic is correctly installed " 00428 "and that lpdomatic is installed in a standard " 00429 "location.")); 00430 return NULL; 00431 } 00432 PrintcapEntry *entry = new PrintcapEntry; 00433 entry->addField("lf", Field::String, "/var/log/lp-errs"); 00434 entry->addField("lp", Field::String, (prot != "parallel" ? "/dev/null" : url.path())); 00435 entry->addField("if", Field::String, m_exematicpath); 00436 if (LprSettings::self()->mode() == LprSettings::LPRng) 00437 { 00438 entry->addField("filter_options", Field::String, " --lprng $Z /etc/foomatic/lpd/"+prt->printerName()+".lom"); 00439 entry->addField("force_localhost", Field::Boolean); 00440 entry->addField("ppdfile", Field::String, "/etc/foomatic/"+prt->printerName()+".ppd"); 00441 } 00442 else 00443 entry->addField("af", Field::String, "/etc/foomatic/lpd/"+prt->printerName()+".lom"); 00444 if (!prt->description().isEmpty()) 00445 entry->aliases << prt->description(); 00446 return entry; 00447 } 00448 00449 bool MaticHandler::removePrinter(KMPrinter *prt, PrintcapEntry *entry) 00450 { 00451 // remove Foomatic driver 00452 QString af = entry->field("af"); 00453 if (af.isEmpty()) 00454 return true; 00455 if (!QFile::remove(af)) 00456 { 00457 manager()->setErrorMsg(i18n("Unable to remove driver file %1.").arg(af)); 00458 return false; 00459 } 00460 return true; 00461 } 00462 00463 QString MaticHandler::printOptions(KPrinter *printer) 00464 { 00465 QMap<QString,QString> opts = printer->options(); 00466 QString str; 00467 for (QMap<QString,QString>::Iterator it=opts.begin(); it!=opts.end(); ++it) 00468 { 00469 if (it.key().startsWith("kde-") || it.key().startsWith("_kde-") || it.key().startsWith( "app-" )) 00470 continue; 00471 str += (" " + it.key() + "=" + (*it)); 00472 } 00473 if (!str.isEmpty()) 00474 str.prepend("-J '").append("'"); 00475 return str; 00476 } 00477 00478 QString MaticHandler::driverDirInternal() 00479 { 00480 return locateDir("foomatic/db/source", "/usr/share:/usr/local/share:/opt/share"); 00481 }