00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
00071
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
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
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
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
00224
00225
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
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 }