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("\\@");
if (p != -1)
{
url = "lpd://" + host.right(host.length()-p-2) + "/" + host.left(p);
00171 }
00172 break;
00173 }
00174 }
00175 }
00176 }
00177
00178 return url;
00179 }
00180
00181 QString MaticHandler::createPostpipe(const QString& _url)
00182 {
00183 KURL url( _url );
00184 QString prot = url.protocol();
00185 QString str;
00186 if (prot == "socket")
00187 {
00188 str += ("| " + m_ncpath);
00189 str += (" " + url.host());
00190 if (url.port() != 0)
00191 str += (" " + QString::number(url.port()));
00192 }
00193 else if (prot == "lpd")
00194 {
00195 str += ("| " + m_rlprpath + " -q -h");
00196 QString h = url.host(), p = url.path().mid(1);
00197 str += (" -P " + p + "\\@" + h);
}
else if (prot == "smb")
00198 {
00199 QString work, server, printer, user, passwd;
00200 if ( splitSmbURI( _url, work, server, printer, user, passwd ) )
00201 {
00202 str += ("| (\\n echo \\\"print -\\\"\\n cat \\n) | " + m_smbpath);
00203 str += (" \\\"//" + server + "/" + printer + "\\\"");
00204 if (!passwd.isEmpty())
00205 str += (" " + passwd);
00206 if (!user.isEmpty())
00207 str += (" -U " + user);
00208 if (!work.isEmpty())
00209 str += (" -W " + work);
00210 str += " -N -P";
00211 }
00212 }
00213 return str;
00214 }
00215
00216 DrMain* MaticHandler::loadDriver(KMPrinter*, PrintcapEntry *entry, bool)
00217 {
00218
00219
00220
00221 QString origfilename = maticFile(entry);
00222 QString filename = locateLocal("tmp", "foomatic_" + kapp->randomString(8));
00223 ::system(QFile::encodeName("cp " + KProcess::quote(origfilename) + " " + KProcess::quote(filename)));
00224 DrMain *driver = Foomatic2Loader::loadDriver(filename);
00225 if (driver)
00226 {
00227 driver->set("template", filename);
00228 driver->set("temporary", "true");
00229 return driver;
00230 }
00231 else
00232 return NULL;
00233 }
00234
00235 DrMain* MaticHandler::loadDbDriver(const QString& path)
00236 {
00237 QStringList comps = QStringList::split('/', path, false);
00238 if (comps.count() < 3 || comps[0] != "foomatic")
00239 {
00240 manager()->setErrorMsg(i18n("Internal error."));
00241 return NULL;
00242 }
00243
00244 QString tmpFile = locateLocal("tmp", "foomatic_" + kapp->randomString(8));
00245 QString PATH = getenv("PATH") + QString::fromLatin1(":/usr/sbin:/usr/local/sbin:/opt/sbin:/opt/local/sbin");
00246 QString exe = KStandardDirs::findExe("foomatic-datafile", PATH);
00247 if (exe.isEmpty())
00248 {
00249 manager()->setErrorMsg(i18n("Unable to find the executable foomatic-datafile "
00250 "in your PATH. Check that Foomatic is correctly installed."));
00251 return NULL;
00252 }
00253
00254 KPipeProcess in;
00255 QFile out(tmpFile);
00256 QString cmd = KProcess::quote(exe);
00257 cmd += " -t lpd -d ";
00258 cmd += KProcess::quote(comps[2]);
00259 cmd += " -p ";
00260 cmd += KProcess::quote(comps[1]);
00261 if (in.open(cmd) && out.open(IO_WriteOnly))
00262 {
00263 QTextStream tin(&in), tout(&out);
00264 QString line;
00265 while (!tin.atEnd())
00266 {
00267 line = tin.readLine();
00268 tout << line << endl;
00269 }
00270 in.close();
00271 out.close();
00272
00273 DrMain *driver = Foomatic2Loader::loadDriver(tmpFile);
00274 if (driver)
00275 {
00276 driver->set("template", tmpFile);
00277 driver->set("temporary", tmpFile);
00278 return driver;
00279 }
00280 }
00281 manager()->setErrorMsg(i18n("Unable to create the Foomatic driver [%1,%2]. "
00282 "Either that driver does not exist, or you don't have "
00283 "the required permissions to perform that operation.").arg(comps[1]).arg(comps[2]));
00284 return NULL;
00285 }
00286
00287 bool MaticHandler::savePrinterDriver(KMPrinter *prt, PrintcapEntry *entry, DrMain *driver, bool*)
00288 {
00289 QFile tmpFile(locateLocal("tmp", "foomatic_" + kapp->randomString(8)));
00290 QFile inFile(driver->get("template"));
00291 QString outFile = maticFile(entry);
00292 bool result(false);
00293 QString postpipe = createPostpipe(prt->device());
00294
00295 if (inFile.open(IO_ReadOnly) && tmpFile.open(IO_WriteOnly))
00296 {
00297 QTextStream tin(&inFile), tout(&tmpFile);
00298 QString line, optname;
00299 int p(-1), q(-1);
00300 if (!postpipe.isEmpty())
00301 tout << "$postpipe = \"" << postpipe << "\";" << endl;
00302 while (!tin.atEnd())
00303 {
00304 line = tin.readLine();
00305 if (line.stripWhiteSpace().startsWith("$postpipe"))
00306 continue;
00307 else if ((p = line.find("'name'")) != -1)
00308 {
00309 p = line.find('\'', p+6)+1;
00310 q = line.find('\'', p);
00311 optname = line.mid(p, q-p);
00312 }
00313 else if ((p = line.find("'default'")) != -1)
00314 {
00315 DrBase *opt = driver->findOption(optname);
00316 if (opt)
00317 {
00318 tout << line.left(p+9) << " => '" << opt->valueText() << "'," << endl;
00319 continue;
00320 }
00321 }
00322 tout << line << endl;
00323 }
00324 inFile.close();
00325 tmpFile.close();
00326
00327 QString cmd = "mv " + KProcess::quote(tmpFile.name()) + " " + KProcess::quote(outFile);
00328 int status = ::system(QFile::encodeName(cmd).data());
00329 QFile::remove(tmpFile.name());
00330 result = (status != -1 && WEXITSTATUS(status) == 0);
00331 }
00332
00333 if (!result)
00334 manager()->setErrorMsg(i18n("You probably don't have the required permissions "
00335 "to perform that operation."));
00336 QFile::remove(tmpFile.name());
00337 if (!result || entry->field("ppdfile").isEmpty())
00338 return result;
00339 else
00340 return savePpdFile(driver, entry->field("ppdfile"));
00341 }
00342
00343 bool MaticHandler::savePpdFile(DrMain *driver, const QString& filename)
00344 {
00345 QString mdriver(driver->get("matic_driver")), mprinter(driver->get("matic_printer"));
00346 if (mdriver.isEmpty() || mprinter.isEmpty())
00347 return true;
00348
00349 QString PATH = getenv("PATH") + QString::fromLatin1(":/usr/sbin:/usr/local/sbin:/opt/sbin:/opt/local/sbin");
00350 QString exe = KStandardDirs::findExe("foomatic-datafile", PATH);
00351 if (exe.isEmpty())
00352 {
00353 manager()->setErrorMsg(i18n("Unable to find the executable foomatic-datafile "
00354 "in your PATH. Check that Foomatic is correctly installed."));
00355 return false;
00356 }
00357
00358 KPipeProcess in;
00359 QFile out(filename);
00360 if (in.open(exe + " -t cups -d " + mdriver + " -p " + mprinter) && out.open(IO_WriteOnly))
00361 {
00362 QTextStream tin(&in), tout(&out);
00363 QString line, optname;
00364 QRegExp re("^\\*Default(\\w+):"), foo("'name'\\s+=>\\s+'(\\w+)'"), foo2("'\\w+'\\s*,\\s*$");
00365 while (!tin.atEnd())
00366 {
00367 line = tin.readLine();
00368 if (line.startsWith("*% COMDATA #"))
00369 {
00370 if (line.find("'default'") != -1)
00371 {
00372 DrBase *opt = (optname.isEmpty() ? NULL : driver->findOption(optname));
00373 if (opt)
00374 {
00375 line.replace(foo2, "'"+opt->valueText()+"',");
00376 }
00377 }
00378 else if (foo.search(line) != -1)
00379 optname = foo.cap(1);
00380 }
00381 else if (re.search(line) != -1)
00382 {
00383 DrBase *opt = driver->findOption(re.cap(1));
00384 if (opt)
00385 {
00386 QString val = opt->valueText();
00387 if (opt->type() == DrBase::Boolean)
00388 val = (val == "1" ? "True" : "False");
00389 tout << "*Default" << opt->name() << ": " << val << endl;
00390 continue;
00391 }
00392 }
00393 tout << line << endl;
00394 }
00395 in.close();
00396 out.close();
00397
00398 return true;
00399 }
00400 manager()->setErrorMsg(i18n("Unable to create the Foomatic driver [%1,%2]. "
00401 "Either that driver does not exist, or you don't have "
00402 "the required permissions to perform that operation.").arg(mdriver).arg(mprinter));
00403
00404 return false;
00405 }
00406
00407 PrintcapEntry* MaticHandler::createEntry(KMPrinter *prt)
00408 {
00409 KURL url( prt->device() );
00410 QString prot = url.protocol();
00411 if ((prot != "lpd" || m_rlprpath.isEmpty()) &&
00412 (prot != "socket" || m_ncpath.isEmpty()) &&
00413 (prot != "smb" || m_smbpath.isEmpty()) &&
00414 prot != "parallel")
00415 {
00416 manager()->setErrorMsg(i18n("Unsupported backend: %1.").arg(prot));
00417 return NULL;
00418 }
00419 if (m_exematicpath.isEmpty())
00420 {
00421 manager()->setErrorMsg(i18n("Unable to find executable lpdomatic. "
00422 "Check that Foomatic is correctly installed "
00423 "and that lpdomatic is installed in a standard "
00424 "location."));
00425 return NULL;
00426 }
00427 PrintcapEntry *entry = new PrintcapEntry;
00428 entry->addField("lf", Field::String, "/var/log/lp-errs");
00429 entry->addField("lp", Field::String, (prot != "parallel" ? "/dev/null" : url.path()));
00430 entry->addField("if", Field::String, m_exematicpath);
00431 if (LprSettings::self()->mode() == LprSettings::LPRng)
00432 {
00433 entry->addField("filter_options", Field::String, " --lprng $Z /etc/foomatic/lpd/"+prt->printerName()+".lom");
00434 entry->addField("force_localhost", Field::Boolean);
00435 entry->addField("ppdfile", Field::String, "/etc/foomatic/"+prt->printerName()+".ppd");
00436 }
00437 else
00438 entry->addField("af", Field::String, "/etc/foomatic/lpd/"+prt->printerName()+".lom");
00439 if (!prt->description().isEmpty())
00440 entry->aliases << prt->description();
00441 return entry;
00442 }
00443
00444 bool MaticHandler::removePrinter(KMPrinter *prt, PrintcapEntry *entry)
00445 {
00446
00447 QString af = entry->field("af");
00448 if (af.isEmpty())
00449 return true;
00450 if (!QFile::remove(af))
00451 {
00452 manager()->setErrorMsg(i18n("Unable to remove driver file %1.").arg(af));
00453 return false;
00454 }
00455 return true;
00456 }
00457
00458 QString MaticHandler::printOptions(KPrinter *printer)
00459 {
00460 QMap<QString,QString> opts = printer->options();
00461 QString str;
00462 for (QMap<QString,QString>::Iterator it=opts.begin(); it!=opts.end(); ++it)
00463 {
00464 if (it.key().startsWith("kde-") || it.key().startsWith("_kde-") || it.key().startsWith( "app-" ))
00465 continue;
00466 str += (" " + it.key() + "=" + (*it));
00467 }
00468 if (!str.isEmpty())
00469 str.prepend("-J '").append("'");
00470 return str;
00471 }
00472
00473 QString MaticHandler::driverDirInternal()
00474 {
00475 return locateDir("foomatic/db/source", "/usr/share:/usr/local/share:/opt/share");
00476 }
00477