HEAD: remove ridiculous dependency of wvmapi (actually wvtnef) on evolution
[wvapps.git] / wvprint / wvprint.cc
blob7e3a80d5e6f93d169890cb9497d6e779ddb78c7e
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2004 Net Integration Technologies, Inc.
5 * This is the WvPrint intelligent print server class.
6 */
8 #include <linux/lp.h>
10 #include "uniconf.h"
12 #include <pwd.h>
13 #include <grp.h>
14 #include <strutils.h>
15 #include <wvstringtable.h>
16 #include <wvaddr.h>
17 #include <wvfile.h>
18 #include <wvtcp.h>
19 #include <sys/ioctl.h>
20 #include <cups/cups.h>
21 #include <uniconfdaemon.h>
22 #include <unitempgen.h>
23 #include <uniunwrapgen.h>
24 #include "wvprint.h"
25 #include "wvzeroconfbrowser.h"
26 #include "wvprintconfwatcher.h"
27 #include "wvprintcupsunigen.h"
28 #include "wvremotecontrol.h"
30 #define LOCAL_QUEUE_NAME "local"
31 #define REMOTE_QUEUE_NAME "remote"
33 #define DEFAULT_USER "lp"
34 #define DEFAULT_GROUP "lp"
36 WvPrint::WvPrint(const UniConf _cfg, UniConf *_db, WvStringParm _log)
37 : log(_log, WvLog::Debug5), cfg(_cfg["WvPrint"]),
38 db(_db), aliases(16), cupsgen(NULL)
40 uses_continue_select = true;
41 needs_autoconfigure_remote = false;
43 watches.add(cfg["lpdprobe"],
44 UniConfCallback(this, &WvPrint::lpdprobe_callback), false);
46 conf_watcher = new WvPrintConfWatcher(log, cfg);
47 conf_watcher->set_cupsmanager(WvCUPSManager::get_instance());
49 /* This is a temporary sol'n and proves to have redundant checks
50 * It is used to check that when the aliases section has an alias
51 * added, it reconfigures all aliases, which then calls the callback
52 * AGAIN, and we want to stop it from calling the callback all the time
54 need_alias_config = true;
56 usbprobe = new WvStream;
57 usbprobe->setcallback(usbprobe_callback, this);
58 append(usbprobe, true);
60 WvFile *usbwatcher = new WvFile(WVPRINT_USBDEVICES, O_RDONLY);
61 if (!usbwatcher->isok())
63 log(WvLog::Debug, "Error appending usbwatcher\n");
66 /* Get rid of initial select() returning true */
67 usbwatcher->select(0, true, true);
68 usbwatcher->setcallback(usbwatcher_callback, usbprobe);
69 append(usbwatcher, true);
72 WvZeroconfBrowser *rendezvouswatcher = new WvZeroconfBrowser("_printer._tcp");
73 rendezvouswatcher->setcallback(rendezvouswatcher_callback, this);
74 append(rendezvouswatcher, true);
76 /* create and mount the CUPSUniGen */
77 cupsgen = new WvPrintCUPSUniGen(conf_watcher);
78 cupsgen->set_remote_controller(new WvPrintRemoteControl(this));
79 cfg["status"].mountgen(cupsgen);
80 /* we'll mount the already loaded UniConf */
81 uniconf["/"].mountgen(new UniUnwrapGen(cfg["status"]));
83 UniConfDaemon *daemon = new UniConfDaemon(uniconf, false, NULL);
84 daemon->setupunixsocket("/tmp/wvprintd-uniconf", 0700);
85 WvIStreamList::globallist.append(daemon, true);
87 conf_watcher->init_config();
90 WvPrint::~WvPrint()
92 terminate_continue_select();
93 delete conf_watcher;
97 void WvPrint::configure_aliases()
99 #if 0
100 need_alias_config = false;
102 /* easier to iterate through the keys and make a new alias table
103 in case some have changed */
104 aliases.zap();
105 PrintQueueNameDict::Iter qit(queues);
107 WvString defaultQueue(cfg["/aliases/lp"].getme(""));
109 /* Setup default queue */
110 if (!defaultQueue || !queues[defaultQueue])
112 WvString newlpqueue("");
113 cfg["/aliases/lp"].remove();
115 /* Ensure that if a local queue exists that the lp default
116 * queue points to it, else default to the remote queue */
117 bool remoteQueue = true;
119 for (qit.rewind(); qit.next(); )
121 /* prefer a local queue to a remote one */
122 UniConf section = cfg["queues"][qit->name];
124 if (section.exists() &&
125 !!section.getme(""))
127 newlpqueue = qit->name;
128 remoteQueue = false;
129 break;
131 else if (section.exists() &&
132 section["networkprinter"].getmeint(0))
134 if (remoteQueue)
135 newlpqueue = qit->name;
138 /* otherwise prefer first in lexicographical order,
139 * don't reference another alias by default */
140 if ((! newlpqueue || strcasecmp(qit->name, newlpqueue) < 0)
141 && !qit->alias)
142 newlpqueue = qit->name;
145 if (!!newlpqueue)
147 cfg["/aliases/lp"].setme(newlpqueue);
148 aliases.add(new AliasMap("lp", newlpqueue), true);
151 else
152 aliases.add(new AliasMap("lp", defaultQueue), true);
154 /* Map aliases to entries */
155 UniConf::Iter cfgit(cfg["/aliases"]);
156 WvStringList removeList; // FIXME : Workaround for uniconf bug, deleting
158 for (cfgit.rewind(); cfgit.next(); )
160 WvString aliasEnt = cfgit().key().printable();
161 WvString queuename = cfgit().getme("");
163 /* Don't bother dealing with the default queue */
164 if (!!queuename)
166 /* Don't map to unexisting queues */
167 if (queues[queuename])
168 aliases.add(new AliasMap(aliasEnt, queuename), true);
169 else
171 log(WvLog::Debug1,
172 "Found an alias for which no queue exists (%s->%s)!\n",
173 aliasEnt, queuename);
174 cfg["aliases"][aliasEnt].remove();
177 else
179 log(WvLog::Debug, "No alias entry set for (%s->%s)\n",
180 aliasEnt, queuename);
184 AliasMapDict::Iter aliasit(aliases);
185 for (aliasit.rewind(); aliasit.next(); )
187 PrintQueueName* queue = queues[aliasit().queue];
188 if (queue && ! queues[aliasit().alias])
190 log(WvLog::Debug, "Setting up alias %s to %s\n",
191 aliasit().alias, queue->name);
192 queues.add(new PrintQueueName(aliasit().alias,
193 queue->queue, true), true);
197 need_alias_config = true;
198 #endif
201 void WvPrint::start_queues()
206 bool WvPrint::isok() const
208 return !(isempty());
211 bool WvPrint::give_up_root()
213 WvString user;
214 WvString group;
215 struct passwd *pwd;
216 struct group *grp;
218 group = cfg["group"].getme(DEFAULT_GROUP);
219 grp = getgrnam(group);
221 if (!grp)
223 log(WvLog::Warning, "Unknown group %s\n", group);
224 return false;
227 if (setregid(grp->gr_gid, grp->gr_gid) == -1)
228 log(WvLog::Warning, "Unable to set group to \"%s\": %s\n",
229 grp->gr_name, strerror(errno));
231 user = cfg["user"].getme(DEFAULT_USER);
232 pwd = getpwnam(user);
234 if (!pwd)
236 log(WvLog::Warning, "Unknown user %s\n", user);
237 return false;
240 if (setreuid(pwd->pw_uid, pwd->pw_uid) == -1)
241 log(WvLog::Warning, "Unable to set user to \"%s\": %s\n",
242 pwd->pw_name, strerror(errno));
244 return true;
248 void WvPrint::zap_configuration(bool only_empty_sections)
250 UniConf::Iter i(cfg["/queues"]);
251 WvStringList removeQueues;
253 for (i.rewind(); i.next(); )
255 if (i().key().isempty() ||
256 (i()["autoprobed"].getmeint(0) && !only_empty_sections))
258 removeQueues.append(new WvString(i().key().printable()), true);
262 /* Just remove all the bloody aliases */
263 /* FIXME : Another major one */
264 //UniConf::Iter j(cfg[cfgRoot "/aliases"]);
265 //for (j.rewind(); j.next(); )
266 // j().key().remove();
269 void WvPrint::autoconfigure()
271 zap_configuration(true);
273 autoconfigure_local();
274 begin_autoconfigure_remote();
277 void WvPrint::autoconfigure_local()
279 log(WvLog::Debug, "Attempting to autoconfigure local printers\n");
281 /* Probe for parallel printers */
282 probe_parallel();
284 /* Probe for USB printers */
285 probe_usb();
288 bool WvPrint::probe_parallel()
290 WvString device;
292 for (int i=0; i<1; i++)
294 device = WvString("/dev/lp%s", i);
295 if (probe_local_device(device) != -1)
296 autoconfigure_local_parallel(device, i);
299 return true;
303 bool WvPrint::probe_lpd()
305 configure_remote_lpd(cfg["netprobe/netprintip"].getme(""),
306 cfg["netprobe/netprintdesc"].getme(""),
307 "Network Printer", cfg["netprobe/netprintqueue"].getme(""));
308 return true;
311 void WvPrint::usbprobe_callback(WvStream &s, void *userdata)
313 WvPrint *print = static_cast<WvPrint *>(userdata);
314 print->probe_usb();
318 void WvPrint::usbwatcher_callback(WvStream &s, void *userdata)
320 WvStream *probe = static_cast<WvStream *>(userdata);
322 /* FIXME:
323 * Invoke a USB probe in 1 second. After the /proc/bus/usb/devices file
324 * is updated it takes time for /dev/usblp[x] to become attached (or
325 * detached as the case may be).
327 probe->alarm(2000);
331 Callback to watch for Zeroconf events.
333 void WvPrint::rendezvouswatcher_callback(WvStream &s, void *userdata)
335 WvPrint *print = static_cast<WvPrint *>(userdata);
336 WvZeroconfBrowser *watcher = static_cast<WvZeroconfBrowser*>(&s);
338 print->log(WvLog::Debug1, "rendezvouswatcher_callback(), %s\n", watcher->isempty());
340 if (!watcher->isempty())
342 print->probe_zeroconf(watcher);
347 bool WvPrint::probe_usb()
349 WvString device;
350 WvString device_fmt = "/dev/usblp%s";
353 WvFile devfile(WvString(device_fmt, 0), O_RDONLY);
354 if (!devfile.isok())
356 device_fmt = "/dev/usb/lp%s";
357 WvFile devfile(WvString(device_fmt, 0), O_RDONLY);
358 if(!devfile.isok())
360 device_fmt = "";
365 // currently limit to the first 16 USB devices found
366 // TODO: check this value somewhere, I know we can do
367 // that.
368 if (!!device_fmt)
370 for (int i=0; i<16; i++)
372 device = WvString(device_fmt, i);
373 if (probe_local_device(device) == -1)
375 continue;
377 autoconfigure_local_usb(device, i);
381 kill_dead_queues();
383 return true;
386 void WvPrint::kill_dead_queues()
388 UniConf::Iter i(cfg["/queues"]);
389 WvString device;
390 WvString lpdserver;
391 WvStringList deadqueue;
393 for (i.rewind(); i.next(); )
395 device = i()["device"].getme(WvString::null);
396 lpdserver = i()["lpdserver"].getme(WvString::null);
398 if (!!device)
400 if (probe_local_device(device) == -1)
401 deadqueue.append(new WvString(i().key().printable()), true);
403 else if (!!lpdserver)
405 // FIXME: Figure out what whoever wrote this meant
406 // to do, and didn't.
408 else
410 log(WvLog::Debug, "Removing: %s", i().key().printable());
411 /* What the hell is this connected to? */
412 deadqueue.append(new WvString(i().key().printable()), true);
416 // FIXME:
417 // Eventually, when the UniTempGen Iterators don't explode
418 // when you delete the keys while iterating, this won't be necessary
419 WvStringList::Iter i2(deadqueue);
420 for (i2.rewind(); i2.next(); )
421 remove_queue(*i2);
424 int WvPrint::probe_local_device(WvStringParm device)
426 int fd;
428 fd = open(device, O_RDONLY|O_NOCTTY|O_TRUNC|O_NONBLOCK);
429 if (fd == -1)
431 log(WvLog::Debug, "Unable to open %s for probing: %s\n",
432 device, strerror(errno));
433 return -1;
436 int status = 0;
437 if (ioctl(fd, LPGETSTATUS, &status) == -1)
439 log(WvLog::Debug, "Unable to get printer status: %s\n",
440 strerror(errno));
441 ::close(fd);
442 return -1;
445 ::close(fd);
447 /* Parallel printers have LP_PBUSY set when they're ready and USB ones
448 * have LP_PACK unset.
450 if (!(status & LP_PBUSY) && (status & LP_PACK))
452 log(WvLog::Info,
453 "No device on printer port, is your printer turned on?\n");
454 return -1;
457 return 0;
460 void WvPrint::autoconfigure_local_parallel(WvStringParm device, int index)
462 WvFile* file;
463 WvString ieee1284_info;
464 WvStringList lines;
465 WvString ieee_class;
466 WvString ieee_manufacturer;
467 WvString ieee_model;
468 WvString ieee_description;
469 WvStringList ieee_commandset;
470 WvString printertype;
471 WvStringList::Iter j(lines);
472 char autoprobe_file[256];
474 log(WvLog::Debug, "Configuring local parallel printer on %s\n", device);
476 sprintf(autoprobe_file, WVPRINT_IEEE1284, index);
477 file = new WvFile(autoprobe_file, O_RDONLY);
479 if (!file->isok())
481 log(WvLog::Info, "Could not read IEEE 1284 device information\n");
482 return;
485 while (file->isok())
487 char* str;
489 str = file->getline();
490 if (str)
492 str = trim_string(str);
493 ieee1284_info.append(str);
497 WVRELEASE(file);
499 ieee_commandset.zap();
501 lines.split(ieee1284_info, ";\n");
502 j.rewind();
503 while (j.next())
505 WvStringList info;
507 if (!j())
508 continue;
510 info.split(j(), ":", 2);
512 if (info.count() != 2)
513 log(WvLog::Debug, "Malformed IEEE 1284 device information line\n");
515 if (*(info.first()) == "CLASS")
516 ieee_class = *(info.last());
518 if (*(info.first()) == "MANUFACTURER")
519 ieee_manufacturer = *(info.last());
521 if (*(info.first()) == "MODEL")
522 ieee_model = *(info.last());
524 if (*(info.first()) == "DESCRIPTION")
525 ieee_description = *(info.last());
527 if (*(info.first()) == "COMMAND SET")
529 ieee_commandset.split(*(info.last()), ", ");
533 if (ieee1284_info && !!ieee_class && ieee_class != "PRINTER")
535 log(WvLog::Debug2,
536 "IEEE-1284 autodetection found a non-printer device\n");
537 return;
540 if (!!ieee_manufacturer && !!ieee_model)
541 printertype = WvString("%s %s", ieee_manufacturer, ieee_model);
543 if (!ieee_description)
544 ieee_description = printertype;
546 if (!!printertype)
547 ieee_commandset.prepend(&printertype, false);
549 if (!!ieee_manufacturer)
550 ieee_commandset.append(&ieee_manufacturer, false);
552 printertype = ieee_commandset.join(";");
554 write_local_config("parallel", device, printertype, ieee_description);
557 void WvPrint::autoconfigure_local_usb(WvStringParm device, int index)
559 WvString printertype;
560 WvString description;
561 char *line, *ptr;
562 WvString vendor, prodID, rev;
563 WvString manufacturer, product, serialnumber;
564 bool device_found = false;
565 int num_device_found = 0;
567 log(WvLog::Debug, "Configuring local USB printer for device %s\n", device);
569 WvFile file(WVPRINT_USBDEVICES, O_RDONLY);
571 /* Skip select because it's a proc file */
572 file.skip_select = true;
574 if (!file.isok())
576 log(WvLog::Info, "Could not read USB device information\n");
577 return;
580 /* This working loop depends on the fact that the USB devices file is
581 * formatted like this for each printer entry (which it should be unless
582 * some big changes happen to the way USB printers are handled):
583 * ...
584 * P: Vendor=xxx ProdID=xxx Rev=xxx
585 * S: Manufacturer=xxx
586 * S: Product=xxx
587 * S: SerialNumber=xxx
588 * ...
589 * I: ... Driver=usblp
590 * ...
592 while (file.isok())
594 line = file.blocking_getline(-1);
595 if (!line)
596 continue;
598 if (!strncmp(line, "T:", 2))
600 // device has been found.
601 device_found = true;
602 log(WvLog::Debug, "Found a T: line\n");
603 // re-init internal state.
604 vendor = prodID = rev = "";
605 manufacturer = product = serialnumber = "";
606 description = printertype = "";
608 else if (device_found)
610 if (!strncmp(line, "P:", 2))
612 char *tmp;
614 ptr = strstr(line, "Rev=");
615 if (ptr) ptr += sizeof("Rev=")-1;
616 rev = ptr;
618 ptr = strstr(line, "ProdID=");
619 if (ptr) ptr += sizeof("ProdID=")-1;
620 tmp = strchr(ptr, ' ');
621 *tmp = '\0';
622 prodID = ptr;
624 ptr = strstr(line, "Vendor=");
625 if (ptr) ptr += sizeof("Vendor=")-1;
626 tmp = strchr(ptr, ' ');
627 *tmp = '\0';
628 vendor = ptr;
630 else if (!strncmp(line, "S:", 2))
632 if (strstr(line, "Manufacturer="))
634 ptr = strstr(line, "Manufacturer=");
635 ptr += sizeof("Manufacturer=")-1;
636 manufacturer = ptr;
638 else if (strstr(line, "Product="))
640 ptr = strstr(line, "Product=");
641 ptr += sizeof("Product=")-1;
642 product = ptr;
644 else if (strstr(line, "SerialNumber="))
646 ptr = strstr(line, "SerialNumber=");
647 ptr += sizeof("SerialNumber=")-1;
648 serialnumber = ptr;
651 else if (!strncmp(line, "I:", 2))
653 log(WvLog::Debug, "Found a I: line %s\n", line);
655 if (strstr(line, "Driver=usblp") || strstr(line, "Driver=printer"))
657 if (num_device_found == index)
659 printertype = WvString("%s %s", manufacturer, product);
660 description = printertype;
661 printertype.append(";", false);
662 printertype.append(manufacturer, false);
664 // we must look up for the device
666 log(WvLog::Debug, "write local config for printer %s on device %s\n",
667 printertype, device);
669 write_local_config("usb", device, printertype, description);
671 num_device_found++;
673 // we handled the device info. reset until the next ^T: line.
674 device_found = false;
680 void WvPrint::write_local_config(WvStringParm port, WvStringParm device, WvStringParm printer,
681 WvStringParm description)
683 UniConf::Iter i(cfg["queues"]);
684 UniConf unisect;
685 WvString queuename;
686 unsigned int queuenumber;
687 bool reconfig;
689 for (i.rewind(); i.next(); )
691 if (strcmp(i()["device"].getme(""), device) == 0)
693 queuename = i().key().printable();
694 break;
698 if (!queuename)
700 queuenumber = 0;
701 queuename = WvString(LOCAL_QUEUE_NAME "%s", queuenumber);
702 UniConf section = cfg["queues"][queuename];
704 while (section.exists())
706 queuename = WvString(LOCAL_QUEUE_NAME "%s", queuenumber++);
707 section = cfg["queues"][queuename];
712 unisect = cfg["queues"][queuename];
714 reconfig = false;
715 reconfig |= !(unisect.exists());
716 reconfig |= !!printer
717 && (unisect["printer"].getme(WvString::null) != printer);
719 unisect["device"].setme(device);
720 unisect["port"].setme(port);
722 if (reconfig)
724 log(WvLog::Debug, "Got reconfig\n");
725 configure_queue(queuename, printer, description);
727 /* FIXME: this is a temporary fix for some /dev/lp0 silliness. */
728 unisect["filter"].setme("/bin/cat");
730 /* We will auto create/delete this queue */
731 unisect["autoprobed"].setmeint(1);
736 * Although remote queues already work, only NET Integrator forwarding is
737 * supported we now want remote queues to be added as well with a prefix
738 * of REMOTE_QUEUE_NAME to be added, as long as the name resolves, we'll
739 * assume that this printer is ready to go
741 void WvPrint::configure_remote_lpd(WvStringParm _serveraddr,
742 WvStringParm _description,
743 WvStringParm _printertype,
744 WvStringParm _remotequeue)
746 WvTCPConn* conn;
747 WvString serveraddr = _serveraddr;
749 log(WvLog::Debug, "DEBUG: configure_remote_lpd(%s, %s, %s, %s)\n",
750 _serveraddr, _description, _printertype, _remotequeue);
752 if (!strchr(_serveraddr, ':'))
753 serveraddr.append(":515");
755 log(WvLog::Debug, "DEBUG: connecting to %s\n", serveraddr);
756 #if 1
757 conn = new WvTCPConn(serveraddr);
759 log(WvLog::Debug, "DEBUG: conn created\n");
761 append(conn, true);
762 log(WvLog::Debug, "DEBUG: conn append\n");
763 conn->alarm(10000); /* negotiation takes awhile when the server is bogged */
764 log(WvLog::Debug, "DEBUG: conn->alarm()\n");
765 printerInfo *pInfo = new printerInfo(serveraddr, _description, _printertype, _remotequeue);
767 conn->setcallback(WvStreamCallback(this,
768 &WvPrint::state_remotelpd_resolve), pInfo);
770 /* this state will only be called on a connection error, if the
771 * connection completes we will erase this callback */
772 conn->setclosecallback(WvBoundCallback<IWvStreamCallback, printerInfo*>(
773 this, &WvPrint::state_connect_error, pInfo));
774 #endif
778 * State only encountered when a TCP Connection to a remote printer dies.
779 * Used so that we can append the failed connection to a list of errors
780 * for webconfig to periodically check on
782 void WvPrint::state_connect_error(printerInfo *pInfo, WvStream &s)
784 log(WvLog::Debug, "DEBUG: state_connect_error\n");
786 remoteLpdErrors.append(new WvString("Error connecting to printer '%s'",
787 pInfo->description), true);
789 if (pInfo)
790 delete pInfo;
793 void WvPrint::state_remotelpd_resolve(WvStream &s, void *userdata)
795 unsigned int queuenumber;
796 WvString queuename;
797 UniConf unisect;
798 bool reconfigure;
800 log(WvLog::Debug, "DEBUG: state_remotelpd_resolve\n");
802 printerInfo *pInfo = (printerInfo*)userdata;
804 /* no longer need to get to the now declared 'error' state when it closes. */
805 s.setclosecallback(0);
808 * If we haven't established a connection, quit, this should also
809 * be changed to include else if (the printer doesn't talk lpd)
811 if (s.alarm_was_ticking)
813 log(WvLog::Error, "Error adding Network Printer %s\n", pInfo->address);
814 s.close();
815 return;
818 s.close();
820 /* Server appears to be responding, find a suitable queuename for it
821 check for the existance of this queue, possibly for a writeover */
822 UniConf::Iter i(cfg["queues"]);
823 for (i.rewind(); i.next(); )
825 if (strcmp(i()["remotehost"].getme(""), pInfo->address) == 0)
827 queuename = i().key().printable();
828 break;
832 log(WvLog::Debug, "DEBUG: state_remotelpd_resolve found queue: %s\n", queuename);
834 if (!queuename)
836 queuenumber = 0;
837 queuename = WvString(REMOTE_QUEUE_NAME "%s", queuenumber);
839 UniConf section(cfg["queues"][queuename]);
841 while (section.exists())
843 queuename = WvString(REMOTE_QUEUE_NAME "%s", queuenumber++);
844 section = cfg["queues"][queuename];
848 reconfigure = false;
849 unisect = cfg["queues"][queuename];
851 if (unisect.exists())
853 configure_queue(queuename, unisect["printer"].getme(""),
854 pInfo->description);
856 else
857 configure_queue(queuename, pInfo->type, pInfo->description);
859 /* As before we'll use lpdserver to indicate the server,
860 * but we'll use a network printer flag to indicate it's
861 * not a Net Integrator bounce
863 unisect["autoprobed"].setmeint(0);
864 unisect["networkprinter"].setmeint(1);
865 unisect["lpdserver"].setme(pInfo->address);
866 unisect["queue"].setme(pInfo->remotequeue);
868 unisect["device"].setme(WvString("//%s/%s", pInfo->address, pInfo->remotequeue));
869 unisect["port"].setme("lpd");
871 log(WvLog::Debug5, "state_remotelpd_resolve(): lpd URI is lpd://%s/%s", pInfo->address, pInfo->remotequeue);
873 /* Again hiding semantics of the filters we use, a definite fix-me
874 Apparantely wvprint.ids is there, but not used. ah, ah, YET! */
875 unisect["filter"].setme("/bin/cat");
877 if (pInfo)
878 delete pInfo;
882 void WvPrint::autoconfigure_lpd(WvStringParm _serveraddr)
884 WvStringList queuelist;
885 WvStringList::Iter i(queuelist);
886 WvStringList queueinfo;
887 WvString sectionname = "";
888 WvString lserveraddr = "";
889 WvString lqueuename = "";
890 WvString queuename = "";
891 WvString remotequeue = "";
892 WvString printertype = "";
893 WvString description = "";
894 WvString serveraddr = _serveraddr;
895 WvTCPConn* conn;
896 bool reconfig;
898 log(WvLog::Debug,
899 "Attempting to autoconfigure from WvPrint LPD server %s\n",
900 serveraddr);
902 if (!strchr(serveraddr, ':'))
903 serveraddr.append(":printer");
905 conn = new WvTCPConn(serveraddr);
907 conn->print("\1"WVPRINT_MAGIC_PROBE"\n");
908 append(conn, false);
910 while (isok() && conn->isok())
912 WvString str = conn->getline();
914 if (!!str)
915 queuelist.append(new WvString("%s", trim_string(str.edit())), true);
916 else
918 // FIXME: got to be a better way than using an explicit timeout.
919 // (No, letting getline handle it isn't a better way: you'd need to
920 // set uses_continue_select on the WvTCPConn, which then crashes
921 // since it's not in an execute loop.)
922 // FIXME: Also, this crashes on exit (call_ctx assertion)
923 continue_select(1000);
924 WvIStreamList::execute();
928 unlink(conn);
930 if (conn->geterr())
932 log(WvLog::Notice, "Error connecting to %s: %s\n", _serveraddr,
933 conn->errstr());
934 conn->close();
935 WVRELEASE(conn);
936 return;
939 conn->close();
940 WVRELEASE(conn);
942 if (!queuelist.isempty() && *queuelist.first() == WVPRINT_MAGIC_PROBE)
943 queuelist.unlink_first();
944 else
946 log(WvLog::Notice, "Server %s is not a WvPrint LPD server\n",
947 _serveraddr);
948 return;
951 i.rewind();
952 while (i.next())
954 queueinfo.zap();
956 /* FIXME: if you have a colon in a queue name or printer type,
957 * *kaboom*. */
958 queueinfo.split(i(), ":", 3);
960 if (queueinfo.isempty())
961 break;
963 remotequeue = trim_string(queueinfo.first()->edit());
964 queuename = WvString("%s-%s", _serveraddr, remotequeue);
965 queueinfo.unlink_first();
967 if (!queueinfo.isempty())
969 printertype = trim_string(queueinfo.first()->edit());
970 queueinfo.unlink_first();
972 if (!printertype)
973 printertype = WvString::null;
975 else
976 printertype = WvString::null;
978 if (!queueinfo.isempty())
980 description = trim_string(queueinfo.first()->edit());
981 queueinfo.unlink_first();
982 if (!description)
983 description = WvString::null;
985 else
986 description = WvString::null;
988 if (!description)
989 description = printertype;
991 sectionname = WvString("queues/%s", queuename);
993 if (!cfg[sectionname].isnull())
995 lserveraddr = cfg[sectionname]["lpdserver"].getme(WvString::null);
996 lqueuename = cfg[sectionname]["queue"].getme(remotequeue);
998 if (!lserveraddr
999 || lserveraddr != serveraddr
1000 || lqueuename != queuename)
1002 break;
1006 reconfig = false;
1007 reconfig |= cfg[sectionname].exists();
1008 if (printertype)
1009 reconfig |= cfg[sectionname]["printer"].getme(WvString::null) != printertype;
1011 if (reconfig)
1013 configure_queue(queuename, printertype, description);
1015 if (serveraddr != _serveraddr)
1016 cfg[sectionname]["lpdserver"].setme(_serveraddr);
1017 else
1018 cfg[sectionname]["lpdserver"].setme(serveraddr);
1020 cfg[sectionname]["queue"].setme(remotequeue);
1022 /* We will auto create/delete this queue */
1023 cfg[sectionname]["autoprobed"].setmeint(1);
1025 cfg[sectionname]["device"].setme(WvString("//%s/%s", _serveraddr, remotequeue));
1026 cfg[sectionname]["port"].setme("lpd");
1028 log(WvLog::Debug5, "autoconfigure_lpd(): lpd URI is lpd://%s/%s", _serveraddr, remotequeue);
1033 void WvPrint::configure_queue(WvStringParm queue,
1034 WvStringParm printertype,
1035 WvString description)
1037 UniConf config;
1038 WvStringList types;
1039 WvString sectionname("queues/%s", queue);
1041 types.split(printertype, ";");
1043 if (db)
1045 UniConf pdb = *db;
1046 WvStringList::Iter i(types);
1048 i.rewind();
1049 while( i.next() )
1051 if( pdb[i()].getme() )
1053 log("found config for type %s\n", i());
1054 config = pdb[i()];
1055 break;
1059 if (!config.isnull())
1061 description = config["description"].getme(description);
1065 if (!!description)
1066 log(WvLog::Notice, "Configuring %s as a %s\n", queue,
1067 description);
1068 else
1069 log(WvLog::Warning,
1070 "Configuring %s as an UNRECOGNIZED printer\n", queue);
1072 if (!!printertype && config.isnull() && db)
1073 log(WvLog::Warning, "Printer type %s is unknown\n",
1074 description);
1076 if (cfg[sectionname].exists())
1077 cfg[sectionname].remove();
1079 cfg[sectionname]["printer"].setme(printertype);
1080 cfg[sectionname]["description"].setme(description);
1082 log(WvLog::Notice, "Getting printer type as %s (%s)", printertype,
1083 cfg[sectionname]["printer"].getme(""));
1085 if (!config.isnull())
1087 UniConf::Iter i(config);
1089 i.rewind();
1090 while (i.next())
1091 cfg[sectionname][i().key()].setme(i().getme());
1094 log(WvLog::Debug, "Done configuring queues.\n");
1097 void WvPrint::advertise_queues(const AdvertiseQueueCallback& cb)
1099 UniConf::Iter i(cfg["aliases"]);
1100 UniConf::Iter j(cfg["queues"]);
1102 /* Advertise aliases */
1103 for (i.rewind(); i.next(); )
1105 WvString alias = i().key().printable();
1106 WvString sect("aliases/%s", alias);
1107 WvString realprinter = cfg[sect].getme("");
1109 UniConf s = cfg[WvString("queues/%s", realprinter)];
1110 if (s.exists() && !!alias)
1112 cb(alias,
1113 WvString(s["advertise printer"].getme(s["printer"].getme(WvString::null))),
1114 WvString(s["description"].getme(WvString::null)));
1116 else
1118 // UniConf fixme, cannot remove while iterating
1119 log(WvLog::Warning, "Invalid alias, removing %s\n", alias);
1123 /* Advertise Queues */
1124 for (j.rewind(); j.next(); )
1126 WvString queue(j().key().printable());
1127 WvString sect("queues/%s", queue);
1129 UniConf queuename = cfg[sect];
1130 cb(queue,
1131 WvString(queuename["advertise printer"].getme(queuename["printer"].getme(WvString::null))),
1132 WvString(queuename["description"].getme(WvString::null)));
1135 cb(WvString(), WvString(), WvString());
1138 void WvPrint::begin_autoconfigure_remote()
1140 needs_autoconfigure_remote = true;
1141 alarm(0); /* trigger on next select */
1144 void WvPrint::execute_autoconfigure_remote()
1146 WvStringList l;
1147 l.split(cfg["lpdprobe"].getme(""));
1148 WvStringList::Iter it(l);
1150 /* add new lpd queues */
1151 for (it.rewind(); it.next(); )
1153 if (!!it())
1154 autoconfigure_lpd(it());
1158 void WvPrint::execute()
1160 WvIStreamList::execute();
1162 /* may do a continue_select */
1163 if (needs_autoconfigure_remote)
1164 execute_autoconfigure_remote();
1165 needs_autoconfigure_remote = false;
1168 void WvPrint::lpdprobe_callback(const UniConf &cfg, const UniConfKey &key)
1170 begin_autoconfigure_remote();
1174 bool WvPrint::is_alias(WvStringParm queuename)
1176 if (cfg[WvString("aliases/%s", queuename)].exists())
1178 return true;
1180 return false;
1183 void WvPrint::remove_queue(WvStringParm queuename)
1185 if (!!queuename)
1187 cfg["queues"][queuename].setme(WvString::null);
1191 #warning REMOVE
1192 #if 0
1193 PrintQueue* WvPrint::get_queue(WvStringParm aQueue)
1195 if (queues[aQueue])
1196 return queues[aQueue]->queue;
1197 else
1198 return 0;
1200 #endif
1204 bool WvPrint::probe_zeroconf(WvZeroconfBrowser* browser)
1206 if (browser->isok())
1208 WvZeroconfService *service;
1210 while (!browser->isempty())
1212 service = browser->pop_first_service();
1213 if (service)
1215 log("Found service %s of type %s\n", service->name, service->type);
1217 delete service;
1221 return true;