1  /***
2   This file is part of cups-filters.
3 
4   This file is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8 
9   This file is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12   Public License for more details.
13 
14   You should have received a copy of the GNU Lesser General Public
15   License along with avahi; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
17   USA.
18 ***/
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <ctype.h>
25 #include <errno.h>
26 #if defined(__OpenBSD__) || defined(__FreeBSD__)
27 #include <sys/socket.h>
28 #endif /* __OpenBSD__ */
29 #include <sys/types.h>
30 #include <net/if.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <resolv.h>
34 #include <stdio.h>
35 #include <sys/stat.h>
36 #include <ifaddrs.h>
37 #include <stdlib.h>
38 #include <time.h>
39 #include <signal.h>
40 #include <regex.h>
41 #include <pthread.h>
42 
43 #include <glib.h>
44 
45 #ifdef HAVE_AVAHI
46 #include <avahi-client/client.h>
47 #include <avahi-client/lookup.h>
48 
49 #include <avahi-glib/glib-watch.h>
50 #include <avahi-common/malloc.h>
51 #include <avahi-common/error.h>
52 #endif /* HAVE_AVAHI */
53 
54 #include <gio/gio.h>
55 
56 
57 #ifdef HAVE_LDAP
58 #  ifdef __sun
59 #    include <lber.h>
60 #  endif /* __sun */
61 #  include <ldap.h>
62 #  ifdef HAVE_LDAP_SSL_H
63 #    include <ldap_ssl.h>
64 #  endif /* HAVE_LDAP_SSL_H */
65 #endif /* HAVE_LDAP */
66 
67 
68 #ifdef HAVE_LDAP
69 LDAP    *BrowseLDAPHandle = NULL;
70           /* Handle to LDAP server */
71 char    *BrowseLDAPBindDN = NULL,
72           /* LDAP login DN */
73       *BrowseLDAPDN = NULL,
74           /* LDAP search DN */
75       *BrowseLDAPPassword = NULL,
76           /* LDAP login password */
77       *BrowseLDAPServer = NULL,
78           /* LDAP server to use */
79       *BrowseLDAPFilter = NULL;
80           /* LDAP query filter */
81 int     BrowseLDAPUpdate = TRUE,
82           /* enables LDAP updates */
83       BrowseLDAPInitialised = FALSE;
84           /* the init stuff has been done */
85 #  ifdef HAVE_LDAP_SSL
86 char    *BrowseLDAPCACertFile = NULL;
87           /* LDAP CA CERT file to use */
88 #  endif /* HAVE_LDAP_SSL */
89 #endif /* HAVE_LDAP */
90 
91 
92 #ifdef HAVE_LDAP
93 #define LDAP_BROWSE_FILTER "(objectclass=cupsPrinter)"
94 static LDAP *ldap_new_connection(void);
95 static LDAP *ldap_reconnect(void);
96 static void ldap_disconnect(LDAP *ld);
97 static int  ldap_search_rec(LDAP *ld, char *base, int scope,
98 			    char *filter, char *attrs[],
99 			    int attrsonly, LDAPMessage **res);
100 static int  ldap_getval_firststring(LDAP *ld, LDAPMessage *entry,
101 				    char *attr, char *retval,
102 				    unsigned long maxsize);
103 static void ldap_freeres(LDAPMessage *entry);
104 #  ifdef HAVE_LDAP_REBIND_PROC
105 #    if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
106 static int  ldap_rebind_proc(LDAP *RebindLDAPHandle,
107 			     LDAP_CONST char *refsp,
108 			     ber_tag_t request,
109 			     ber_int_t msgid,
110 			     void *params);
111 #    else
112 static int  ldap_rebind_proc(LDAP *RebindLDAPHandle,
113 			     char **dnp,
114 			     char **passwdp,
115 			     int *authmethodp,
116 			     int freeit,
117                                  void *arg);
118 #    endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
119 #  endif /* HAVE_LDAP_REBIND_PROC */
120 #endif /* HAVE_LDAP */
121 
122 #include <cups/cups.h>
123 #include <cups/ppd.h>
124 #include <cups/raster.h>
125 #include <cupsfilters/ipp.h>
126 #include <cupsfilters/ppdgenerator.h>
127 
128 #include "cups-notifier.h"
129 
130 /* Attribute to mark a CUPS queue as created by us */
131 #define CUPS_BROWSED_MARK "cups-browsed"
132 #define AUTO_OPTION "auto"
133 
134 /* Attribute to tell the implicitclass backend the destination queue for
135    the current job */
136 #define CUPS_BROWSED_DEST_PRINTER "cups-browsed-dest-printer"
137 
138 /* Timeout values in sec */
139 #define TIMEOUT_IMMEDIATELY -1
140 #define TIMEOUT_CONFIRM     10
141 #define TIMEOUT_RETRY       10
142 #define TIMEOUT_REMOVE      -1
143 #define TIMEOUT_CHECK_LIST   2
144 
145 #define CUPS_DBUS_NAME "org.cups.cupsd.Notifier"
146 #define CUPS_DBUS_PATH "/org/cups/cupsd/Notifier"
147 #define CUPS_DBUS_INTERFACE "org.cups.cupsd.Notifier"
148 
149 #define DEFAULT_CACHEDIR "/var/cache/cups"
150 #define DEFAULT_LOGDIR "/var/log/cups"
151 #define LOCAL_DEFAULT_PRINTER_FILE "/cups-browsed-local-default-printer"
152 #define REMOTE_DEFAULT_PRINTER_FILE "/cups-browsed-remote-default-printer"
153 #define SAVE_OPTIONS_FILE "/cups-browsed-options-%s"
154 #define DEBUG_LOG_FILE "/cups-browsed_log"
155 #define DEBUG_LOG_FILE_2 "/cups-browsed_previous_logs"
156 
157 /* Status of remote printer */
158 typedef enum printer_status_e {
159   STATUS_UNCONFIRMED = 0, /* Generated in a previous session */
160   STATUS_CONFIRMED,   /* Avahi confirms UNCONFIRMED printer */
161   STATUS_TO_BE_CREATED,   /* Scheduled for creation */
162   STATUS_DISAPPEARED,   /* Scheduled for removal */
163   STATUS_TO_BE_RELEASED   /* Scheduled for release from cups-browsed */
164 } printer_status_t;
165 
166 /* Data structure for taking note of each time the remote printer
167    appears as a discovered IPP service */
168 typedef struct ipp_discovery_s {
169   char *interface;
170   char *type;
171   int family;
172 } ipp_discovery_t;
173 
174 /* Data structure for remote printers */
175 typedef struct remote_printer_s {
176   char *queue_name;
177   char *location;
178   char *info;
179   char *uri;
180   char *make_model;
181   char *pdl;
182   int color;
183   int duplex;
184   ipp_t *prattrs;
185   char *nickname;
186   int num_options;
187   cups_option_t *options;
188   printer_status_t status;
189   time_t timeout;
190   void *slave_of;
191   int last_printer;
192   char *host;
193   char *ip;
194   int port;
195   char *resource;
196   char *service_name;
197   char *type;
198   char *domain;
199   cups_array_t *ipp_discoveries;
200   int no_autosave;
201   int overwritten;
202   int netprinter;
203   int is_legacy;
204   int timeouted;
205 } remote_printer_t;
206 
207 /* Data structure for network interfaces */
208 typedef struct netif_s {
209   char *address;
210   http_addr_t broadcast;
211 } netif_t;
212 
213 /* Data structures for browse allow/deny rules */
214 typedef enum browse_order_e {
215   ORDER_ALLOW_DENY,
216   ORDER_DENY_ALLOW
217 } browse_order_t;
218 typedef enum allow_type_e {
219   ALLOW_IP,
220   ALLOW_NET,
221   ALLOW_INVALID
222 } allow_type_t;
223 typedef enum allow_sense_e {
224   ALLOW_ALLOW,
225   ALLOW_DENY
226 } allow_sense_t;
227 typedef struct allow_s {
228   allow_type_t type;
229   allow_sense_t sense;
230   http_addr_t addr;
231   http_addr_t mask;
232 } allow_t;
233 
234 /* Data structures for browse filter rules */
235 typedef enum filter_sense_s {
236   FILTER_MATCH,
237   FILTER_NOT_MATCH
238 } filter_sense_t;
239 typedef struct browse_filter_s {
240   filter_sense_t sense;
241   char *field;
242   char *regexp;
243   regex_t *cregexp;
244 } browse_filter_t;
245 
246 /* Data structure for a printer discovered using BrowsePoll */
247 typedef struct browsepoll_printer_s {
248   char *uri_supported;
249   char *location;
250   char *info;
251 } browsepoll_printer_t;
252 
253 /* Data structure for a BrowsePoll server */
254 typedef struct browsepoll_s {
255   char *server;
256   int port;
257   int major;
258   int minor;
259   gboolean can_subscribe;
260   int subscription_id;
261   int sequence_number;
262 
263   /* Remember which printers we discovered. This way we can just ask
264    * if anything has changed, and if not we know these printers are
265    * still there. */
266   GList *printers; /* of browsepoll_printer_t */
267 } browsepoll_t;
268 
269 /* Data structure for destination list obtained with cupsEnumDests() */
270 typedef struct dest_list_s {
271   int num_dests;
272   cups_dest_t *dests;
273 } dest_list_t;
274 
275 /* Local printer (key is name) */
276 typedef struct local_printer_s {
277   char *device_uri;
278   char *uuid;
279   gboolean cups_browsed_controlled;
280 } local_printer_t;
281 
282 /* Browse data to send for local printer */
283 typedef struct browse_data_s {
284   int type;
285   int state;
286   char *uri;
287   char *location;
288   char *info;
289   char *make_model;
290   char *browse_options;
291 } browse_data_t;
292 
293 /* Data structure for manual definition of load-balancing clusters */
294 typedef struct cluster_s {
295   char *local_queue_name;
296   cups_array_t *members;
297 } cluster_t;
298 
299 /* Ways how to represent the remote printer's IP in the device URI */
300 typedef enum ip_based_uris_e {
301   IP_BASED_URIS_NO,
302   IP_BASED_URIS_ANY,
303   IP_BASED_URIS_IPV4_ONLY,
304   IP_BASED_URIS_IPV6_ONLY
305 } ip_based_uris_t;
306 
307 /* Ways how to name local queues for remote printers */
308 typedef enum local_queue_naming_e {
309   LOCAL_QUEUE_NAMING_DNSSD,
310   LOCAL_QUEUE_NAMING_MAKE_MODEL,
311   LOCAL_QUEUE_NAMING_REMOTE_NAME
312 } local_queue_naming_t;
313 
314 /* Automatically create queues for IPP network printers: No, only for
315    IPP printers, for all printers */
316 typedef enum create_ipp_printer_queues_e {
317   IPP_PRINTERS_NO,
318   IPP_PRINTERS_LOCAL_ONLY,
319   IPP_PRINTERS_PWGRASTER,
320   IPP_PRINTERS_APPLERASTER,
321   IPP_PRINTERS_PCLM,
322   IPP_PRINTERS_PDF,
323   IPP_PRINTERS_DRIVERLESS,
324   IPP_PRINTERS_ALL
325 } create_ipp_printer_queues_t;
326 
327 /* Ways how to set up a queue for an IPP network printer */
328 typedef enum ipp_queue_type_e {
329   PPD_YES,
330   PPD_NO
331 } ipp_queue_type_t;
332 
333 /* Ways how we can do load balancing on remote queues with the same name */
334 typedef enum load_balancing_type_e {
335   QUEUE_ON_CLIENT,
336   QUEUE_ON_SERVERS
337 } load_balancing_type_t;
338 
339 /* Ways how inactivity for auto-shutdown is defined */
340 typedef enum autoshutdown_inactivity_type_e {
341   NO_QUEUES,
342   NO_JOBS
343 } autoshutdown_inactivity_type_t;
344 
345 typedef struct media_size_s{
346   int x;
347   int y;
348 }media_size_t;
349 
350 typedef struct pagesize_range_s{
351   int x_dim_min;
352   int x_dim_max;
353   int y_dim_min;
354   int y_dim_max;
355 }pagesize_range_t;
356 
357 typedef struct media_col_s{
358   int x,y,top_margin,bottom_margin,left_margin,right_margin;
359   char *media_source,*media_type;
360 }media_col_t;
361 
362 typedef struct default_str_attribute_s{
363   char* value;
364   int count;
365 }default_str_attribute_t;
366 
367 typedef struct resolution_count_s{
368   res_t *res;
369   int count;
370 }resolution_count_t;
371 
372 typedef struct mediacol_count_s{
373   media_col_t   *data;
374   int count;
375 }mediacol_count_t;
376 
377 typedef struct pagesize_count_s{
378   char* pagesize;
379   int   count;
380 }pagesize_count_t;
381 
382 
383 cups_array_t *remote_printers;
384 static char *alt_config_file = NULL;
385 static cups_array_t *command_line_config;
386 static cups_array_t *netifs;
387 static cups_array_t *local_hostnames;
388 static cups_array_t *browseallow;
389 static gboolean browseallow_all = FALSE;
390 static gboolean browsedeny_all = FALSE;
391 static browse_order_t browse_order;
392 static cups_array_t *browsefilter;
393 
394 static GHashTable *local_printers;
395 static GHashTable *cups_supported_remote_printers;
396 static browsepoll_t *local_printers_context = NULL;
397 static http_t *local_conn = NULL;
398 static gboolean inhibit_local_printers_update = FALSE;
399 
400 static GList *browse_data = NULL;
401 
402 static CupsNotifier *cups_notifier = NULL;
403 
404 static GMainLoop *gmainloop = NULL;
405 #ifdef HAVE_AVAHI
406 static AvahiGLibPoll *glib_poll = NULL;
407 static AvahiClient *client = NULL;
408 static AvahiServiceBrowser *sb1 = NULL, *sb2 = NULL;
409 static int avahi_present = 0;
410 #endif /* HAVE_AVAHI */
411 #ifdef HAVE_LDAP
412 static const char * const ldap_attrs[] =/* CUPS LDAP attributes */
413     {
414       "printerDescription",
415       "printerLocation",
416       "printerMakeAndModel",
417       "printerType",
418       "printerURI",
419       NULL
420     };
421 #endif /* HAVE_LDAP */
422 static guint queues_timer_id = 0;
423 static int browsesocket = -1;
424 
425 #define BROWSE_DNSSD (1<<0)
426 #define BROWSE_CUPS  (1<<1)
427 #define BROWSE_LDAP  (1<<2)
428 static unsigned int BrowseLocalProtocols = 0;
429 static unsigned int BrowseRemoteProtocols = BROWSE_DNSSD;
430 static unsigned int BrowseInterval = 60;
431 static unsigned int BrowseTimeout = 300;
432 static uint16_t BrowsePort = 631;
433 static browsepoll_t **BrowsePoll = NULL;
434 static unsigned int NewBrowsePollQueuesShared = 0;
435 static unsigned int AllowResharingRemoteCUPSPrinters = 0;
436 static unsigned int DebugLogFileSize = 300;
437 static size_t NumBrowsePoll = 0;
438 static guint update_netifs_sourceid = 0;
439 static char local_server_str[1024];
440 static char *DomainSocket = NULL;
441 static unsigned int HttpLocalTimeout = 5;
442 static unsigned int HttpRemoteTimeout = 10;
443 static unsigned int HttpMaxRetries = 5;
444 static unsigned int DNSSDBasedDeviceURIs = 1;
445 static ip_based_uris_t IPBasedDeviceURIs = IP_BASED_URIS_NO;
446 #ifdef NAMING_MAKE_MODEL
447 static local_queue_naming_t LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_MAKE_MODEL;
448 #else
449 # ifdef NAMING_REMOTE_NAME
450 static local_queue_naming_t LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_REMOTE_NAME;
451 # else
452 static local_queue_naming_t LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_DNSSD;
453 # endif
454 #endif
455 static local_queue_naming_t LocalQueueNamingIPPPrinter=LOCAL_QUEUE_NAMING_DNSSD;
456 static unsigned int OnlyUnsupportedByCUPS = 0;
457 static unsigned int UseCUPSGeneratedPPDs = 0;
458 static unsigned int CreateRemoteRawPrinterQueues = 0;
459 static unsigned int CreateRemoteCUPSPrinterQueues = 1;
460 #ifdef ONLY_LOCAL_IPP_PRINTERS_AUTO_SETUP
461 static create_ipp_printer_queues_t CreateIPPPrinterQueues = IPP_PRINTERS_LOCAL_ONLY;
462 #else
463 #ifdef ONLY_DRIVERLESS_IPP_PRINTERS_AUTO_SETUP
464 static create_ipp_printer_queues_t CreateIPPPrinterQueues = IPP_PRINTERS_DRIVERLESS;
465 #else
466 static create_ipp_printer_queues_t CreateIPPPrinterQueues = IPP_PRINTERS_ALL;
467 #endif
468 #endif
469 #ifdef SAVING_CREATED_QUEUES
470 static unsigned int KeepGeneratedQueuesOnShutdown = 1;
471 #else
472 static unsigned int KeepGeneratedQueuesOnShutdown = 0;
473 #endif
474 static ipp_queue_type_t IPPPrinterQueueType = PPD_YES;
475 static int NewIPPPrinterQueuesShared = 0;
476 static int AutoClustering = 1;
477 static cups_array_t *clusters;
478 static load_balancing_type_t LoadBalancingType = QUEUE_ON_CLIENT;
479 static char *DefaultOptions = NULL;
480 static int update_cups_queues_max_per_call = 10;
481 static int pause_between_cups_queue_updates = 1;
482 static remote_printer_t *deleted_master = NULL;
483 static int terminating = 0; /* received SIGTERM, ignore callbacks,
484              break loops */
485 static int in_shutdown = 0;
486 static int autoshutdown = 0;
487 static int autoshutdown_avahi = 0;
488 static int autoshutdown_timeout = 30;
489 static autoshutdown_inactivity_type_t autoshutdown_on = NO_QUEUES;
490 static guint autoshutdown_exec_id = 0;
491 static const char *default_printer = NULL;
492 static unsigned int notify_lease_duration = 86400;
493 
494 static int debug_stderr = 0;
495 static int debug_logfile = 0;
496 static FILE *lfp = NULL;
497 
498 static char cachedir[1024];
499 static char logdir[1024];
500 static char local_default_printer_file[2048];
501 static char remote_default_printer_file[2048];
502 static char save_options_file[2048];
503 static char debug_log_file[2048];
504 static char debug_log_file_bckp[2048];
505 
506 /*Contains ppd keywords which are written by ppdgenerator.c in the ppd file.*/
507 static char* ppd_keywords[] =
508     {
509       "PageSize",
510       "PageRegion",
511       "InputSlot",
512       "MediaType",
513       "ColorModel",
514       "Duplex",
515       "OutputBin",
516       "StapleLocation",
517       "FoldType",
518       "PunchMedia",
519       "Booklet",
520       "cupsFinishingTemplate",
521       "cupsPrintQuality",
522       "print-content-optimize",
523       "print-rendering-intent",
524       "print-scaling",
525     };
526 
527 /* Static global variable for indicating we have reached the HTTP timeout */
528 static int timeout_reached = 0;
529 
530 static void recheck_timer (void);
531 static void browse_poll_create_subscription (browsepoll_t *context,
532 					     http_t *conn);
533 static gboolean browse_poll_get_notifications (browsepoll_t *context,
534 					       http_t *conn);
535 static remote_printer_t
536 *examine_discovered_printer_record(const char *host,
537 				   const char *ip,
538 				   uint16_t port,
539 				   char *resource,
540 				   const char *service_name,
541 				   const char *location,
542 				   const char *info,
543 				   const char *type,
544 				   const char *domain,
545 				   const char *interface,
546 				   int family,
547 				   void *txt);
548 
549 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5)
550 #define HAVE_CUPS_1_6 1
551 #endif
552 
553 /*
554  * Option 'printer-is-shared' cannot be set on remote CUPS
555  * queue and requests for setting it ends with error since
556  * 2.1.1. Define HAVE_CUPS_2_2 to do not send IPP request
557  * for setting 'printer-is-shared' option on remote CUPS queues
558  * for newer versions of CUPS.
559  */
560 #if (CUPS_VERSION_MAJOR > 2) || (CUPS_VERSION_MINOR > 1)
561 #define HAVE_CUPS_2_2 1
562 #else
563 #define HAVE_CUPS_2_2 0
564 #endif
565 
566 /*
567  * CUPS 1.6 makes various structures private and
568  * introduces these ippGet and ippSet functions
569  * for all of the fields in these structures.
570  * http://www.cups.org/str.php?L3928
571  * We define (same signatures) our own accessors when CUPS < 1.6.
572  */
573 #ifndef HAVE_CUPS_1_6
574 const char *
ippGetName(ipp_attribute_t * attr)575 ippGetName(ipp_attribute_t *attr)
576 {
577   return (attr->name);
578 }
579 
580 ipp_op_t
ippGetOperation(ipp_t * ipp)581 ippGetOperation(ipp_t *ipp)
582 {
583   return (ipp->request.op.operation_id);
584 }
585 
586 ipp_status_t
ippGetStatusCode(ipp_t * ipp)587 ippGetStatusCode(ipp_t *ipp)
588 {
589   return (ipp->request.status.status_code);
590 }
591 
592 ipp_tag_t
ippGetGroupTag(ipp_attribute_t * attr)593 ippGetGroupTag(ipp_attribute_t *attr)
594 {
595   return (attr->group_tag);
596 }
597 
598 ipp_tag_t
ippGetValueTag(ipp_attribute_t * attr)599 ippGetValueTag(ipp_attribute_t *attr)
600 {
601   return (attr->value_tag);
602 }
603 
604 int
ippGetCount(ipp_attribute_t * attr)605 ippGetCount(ipp_attribute_t *attr)
606 {
607   return (attr->num_values);
608 }
609 
610 int
ippGetInteger(ipp_attribute_t * attr,int element)611 ippGetInteger(ipp_attribute_t *attr,
612               int             element)
613 {
614   return (attr->values[element].integer);
615 }
616 
617 int
ippGetBoolean(ipp_attribute_t * attr,int element)618 ippGetBoolean(ipp_attribute_t *attr,
619               int             element)
620 {
621   return (attr->values[element].boolean);
622 }
623 
624 const char *
ippGetString(ipp_attribute_t * attr,int element,const char ** language)625 ippGetString(ipp_attribute_t *attr,
626              int             element,
627              const char      **language)
628 {
629   return (attr->values[element].string.text);
630 }
631 
632 ipp_attribute_t *
ippFirstAttribute(ipp_t * ipp)633 ippFirstAttribute(ipp_t *ipp)
634 {
635   if (!ipp)
636     return (NULL);
637   return (ipp->current = ipp->attrs);
638 }
639 
640 ipp_attribute_t *
ippNextAttribute(ipp_t * ipp)641 ippNextAttribute(ipp_t *ipp)
642 {
643   if (!ipp || !ipp->current)
644     return (NULL);
645   return (ipp->current = ipp->current->next);
646 }
647 
648 int
ippSetVersion(ipp_t * ipp,int major,int minor)649 ippSetVersion(ipp_t *ipp, int major, int minor)
650 {
651   if (!ipp || major < 0 || minor < 0)
652     return (0);
653   ipp->request.any.version[0] = major;
654   ipp->request.any.version[1] = minor;
655   return (1);
656 }
657 #endif
658 
659 
660 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 6)
661 #define HAVE_CUPS_1_7 1
662 #endif
663 
664 /*
665  * The httpAddrPort() function was only introduced in CUPS 1.7.x
666  */
667 #ifndef HAVE_CUPS_1_7
668 int                                     /* O - Port number */
httpAddrPort(http_addr_t * addr)669 httpAddrPort(http_addr_t *addr)         /* I - Address */
670 {
671   if (!addr)
672     return (-1);
673 #ifdef AF_INET6
674   else if (addr->addr.sa_family == AF_INET6)
675     return (ntohs(addr->ipv6.sin6_port));
676 #endif /* AF_INET6 */
677   else if (addr->addr.sa_family == AF_INET)
678     return (ntohs(addr->ipv4.sin_port));
679   else
680     return (0);
681 }
682 #endif
683 
684 
685 #if (CUPS_VERSION_MAJOR > 1)
686 #define HAVE_CUPS_2_0 1
687 #endif
688 
689 
690 void
start_debug_logging()691 start_debug_logging()
692 {
693   if (debug_log_file[0] == '\0')
694     return;
695   if (lfp == NULL)
696     lfp = fopen(debug_log_file, "a+");
697   if (lfp == NULL) {
698     fprintf(stderr, "cups-browsed: ERROR: Failed creating debug log file %s\n",
699       debug_log_file);
700     exit(1);
701   }
702 }
703 
704 void
stop_debug_logging()705 stop_debug_logging()
706 {
707   debug_logfile = 0;
708   if (lfp)
709     fclose(lfp);
710   lfp = NULL;
711 }
712 
713 // returns the size of debug log file
findLogFileSize()714 long int findLogFileSize()
715 {
716     FILE* fp = fopen(debug_log_file, "r");
717     if (fp == NULL) {
718         return -1;
719     }
720     fseek(fp, 0L, SEEK_END);
721     long int res = ftell(fp);
722     fclose(fp);
723     return res;
724 }
725 
copyToFile(FILE ** fp1,FILE ** fp2)726 void copyToFile(FILE **fp1, FILE **fp2){
727   int buffer_size = 2048;
728   char *buf = (char*) malloc(sizeof(char)*buffer_size);
729   if(!buf){
730     fprintf(stderr,"Error creating buffer for debug logging\n");
731     return;
732   }
733   fseek(*fp1, 0, SEEK_SET);
734   size_t r;
735   do {
736     r = fread(buf, sizeof(char), buffer_size, *fp1);
737     fwrite(buf, sizeof(char), r, *fp2);
738   } while(r==buffer_size);
739 
740 }
741 
742 void
debug_printf(const char * format,...)743 debug_printf(const char *format, ...) {
744   if (debug_stderr || debug_logfile) {
745     time_t curtime = time(NULL);
746     char buf[64];
747     ctime_r(&curtime, buf);
748     while(isspace(buf[strlen(buf)-1])) buf[strlen(buf)-1] = '\0';
749     va_list arglist;
750     if (debug_stderr) {
751       va_start(arglist, format);
752       fprintf(stderr, "%s ", buf);
753       vfprintf(stderr, format, arglist);
754       fflush(stderr);
755       va_end(arglist);
756     }
757     if (debug_logfile && lfp) {
758       va_start(arglist, format);
759       fprintf(lfp, "%s ", buf);
760       vfprintf(lfp, format, arglist);
761       fflush(lfp);
762       va_end(arglist);
763     }
764 
765     long int log_file_size = findLogFileSize();
766     if(DebugLogFileSize>0 && log_file_size>(long int)DebugLogFileSize*1024){
767       fclose(lfp);
768       FILE *fp1 = fopen(debug_log_file, "r");
769       FILE *fp2 = fopen(debug_log_file_bckp, "w");
770       copyToFile(&fp1,&fp2);
771       fclose(fp1);
772       fclose(fp2);
773       lfp = fopen(debug_log_file, "w");
774     }
775 }
776 }
777 
778 void
debug_log_out(char * log)779 debug_log_out(char *log) {
780   if (debug_stderr || debug_logfile) {
781     time_t curtime = time(NULL);
782     char buf[64];
783     char *ptr1, *ptr2;
784     ctime_r(&curtime, buf);
785     while(isspace(buf[strlen(buf)-1])) buf[strlen(buf)-1] = '\0';
786     ptr1 = log;
787     while(ptr1) {
788       ptr2 = strchr(ptr1, '\n');
789       if (ptr2) *ptr2 = '\0';
790       if (debug_stderr)
791 	fprintf(stderr, "%s %s\n", buf, ptr1);
792       if (debug_logfile && lfp)
793 	fprintf(lfp, "%s %s\n", buf, ptr1);
794       if (ptr2) *ptr2 = '\n';
795       ptr1 = ptr2 ? (ptr2 + 1) : NULL;
796     }
797   }
798 }
799 
800 
801 /*
802  * 'create_media_size()' - Create a media-size value.
803  */
804 
805 static ipp_t *                  /* O - media-col collection */
create_media_size(int width,int length)806 create_media_size(int width,    /* I - x-dimension in 2540ths */
807 		  int length)   /* I - y-dimension in 2540ths */
808 {
809   ipp_t *media_size = ippNew();   /* media-size value */
810 
811   ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension",
812                 width);
813   ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension",
814                 length);
815 
816   return (media_size);
817 }
818 
819 /*
820  * 'create_media_range()' - Create a pagesize-range value.
821  */
822 
823 static ipp_t *
create_media_range(int x_dim_min_width,int x_dim_max_width,int y_dim_min_height,int y_dim_max_height)824 create_media_range(int x_dim_min_width,
825 		   int x_dim_max_width,
826 		   int y_dim_min_height,
827 		   int y_dim_max_height)
828 {
829   ipp_t *media_size = ippNew();
830   ippAddRange(media_size, IPP_TAG_PRINTER, "x-dimension",
831 	      x_dim_min_width, x_dim_max_width);
832   ippAddRange(media_size, IPP_TAG_PRINTER, "y-dimension",
833 	      y_dim_min_height, y_dim_max_height);
834   return (media_size);
835 }
836 
837 void *
copy_media_size(void * size,void * user_data)838 copy_media_size(void *size, void *user_data)
839 {
840   media_size_t *data = (media_size_t *)size;
841   media_size_t *copy;
842 
843   copy = (media_size_t *)calloc(1, sizeof(media_size_t));
844   if (copy) {
845     copy->x = data->x;
846     copy->y = data->y;
847   }
848   return copy;
849 }
850 
851 void*
copy_range_size(void * range,void * user_data)852 copy_range_size(void *range, void* user_data)
853 {
854   pagesize_range_t *data = (pagesize_range_t *)range;
855   pagesize_range_t *copy;
856 
857   copy = (pagesize_range_t *)calloc(1,sizeof(pagesize_range_t));
858   if (copy) {
859     copy->x_dim_min = data->x_dim_min;
860     copy->x_dim_max = data->x_dim_max;
861     copy->y_dim_min = data->y_dim_min;
862     copy->y_dim_max = data->y_dim_max;
863   }
864   return copy;
865 }
866 
867 void *
copy_media(void * media,void * user_data)868 copy_media(void *media, void *user_data)
869 {
870   media_col_t *data = (media_col_t *)media;
871   media_col_t *copy;
872 
873   copy = (media_col_t *)calloc(1, sizeof(media_col_t));
874   if (copy) {
875     copy->x = data->x;
876     copy->y = data->y;
877     copy->left_margin=data->left_margin;
878     copy->right_margin=data->right_margin;
879     copy->top_margin=data->top_margin;
880     copy->bottom_margin=data->bottom_margin;
881     copy->media_source = NULL;
882     copy->media_type = NULL;
883     if (data->media_source != NULL) {
884       copy->media_source = (char *)malloc(sizeof(char)*32);
885       strcpy(copy->media_source,data->media_source);
886     }
887     if (data->media_type != NULL) {
888       copy->media_type = (char *)malloc(sizeof(char)*32);;
889       strcpy(copy->media_type,data->media_type);
890     }
891   }
892   return copy;
893 }
894 
copy_media_count(void * media,void * user_data)895 void* copy_media_count(void *media, void* user_data)
896 {
897   mediacol_count_t *prev = (mediacol_count_t *)media;
898   mediacol_count_t *copy;
899 
900   copy = (mediacol_count_t*)calloc(1,sizeof(mediacol_count_t));
901   if (copy) {
902     copy->data = copy_media(prev->data,NULL);
903     copy->count = prev->count;
904   }
905   return copy;
906 }
907 
copy_pagesize_count(void * pagesize_count,void * user_data)908 void* copy_pagesize_count(void *pagesize_count, void* user_data)
909 {
910   pagesize_count_t *prev = (pagesize_count_t *)pagesize_count;
911   pagesize_count_t *copy;
912 
913   copy = (pagesize_count_t*)calloc(1,sizeof(pagesize_count_t));
914   copy->pagesize = malloc(sizeof(char)*32);
915   if (copy) {
916     strcpy(copy->pagesize,prev->pagesize);
917     copy->count = prev->count;
918   }
919   return copy;
920 }
921 
compare_pagesize_count(void * pagesize_a,void * pagesize_b,void * user_data)922 int compare_pagesize_count(void* pagesize_a, void* pagesize_b,void* user_data)
923 {
924   pagesize_count_t *a = (pagesize_count_t *) pagesize_a;
925   pagesize_count_t *b = (pagesize_count_t *) pagesize_b;
926 
927   if (!strcmp(a->pagesize,b->pagesize))
928     return 0;
929   return 1;
930 }
931 
932 /*
933  * 'create_media_col()' - Create a media-col value.
934  */
935 
936 static ipp_t *
create_media_col(int width,int length,int left_margin,int right_margin,int top_margin,int bottom_margin,char * media_source,char * media_type)937 create_media_col(int  width,
938 		 int  length,
939 		 int  left_margin,
940 		 int  right_margin,
941 		 int  top_margin,
942 		 int  bottom_margin,
943 		 char *media_source,
944 		 char *media_type)
945 {
946   ipp_t *media_col = ippNew(),    /* media-col value */
947         *media_size = create_media_size(width, length);
948 
949   ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
950   ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
951                 "media-bottom-margin",bottom_margin);
952   ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
953                 "media-left-margin", left_margin);
954   ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
955                 "media-right-margin",right_margin);
956   ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
957                 "media-top-margin", top_margin);
958   if (media_source != NULL)
959     ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
960                  "media-source", NULL,media_source);
961   if (media_type != NULL)
962     ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
963                  "media-type", NULL,media_type);
964   ippDelete(media_size);
965 
966   return (media_col);
967 }
968 
969 int
compare_mediasize(void * media_a,void * media_b,void * user_data)970 compare_mediasize(void *media_a, void *media_b,
971 		  void *user_data)
972 {
973   media_size_t  *a=(media_size_t *)media_a;
974   media_size_t  *b=(media_size_t *)media_b;
975 
976   if (a->x < b->x)
977     return -1;
978   else if (a->x > b->x)
979     return 1;
980   else{
981     if (a->y == b->y)
982       return 0;
983     else if (a->y < b->y)
984       return -1;
985     return 1;
986   }
987 }
988 
compare_int(int a,int b)989 int compare_int(int a, int b)
990 {
991   if (a < b)
992     return -1;
993   else if (a > b)
994     return 1;
995   return 0;
996 }
997 
compare_rangesize(void * range_a,void * range_b,void * user_data)998 int compare_rangesize(void *range_a,void *range_b,
999 		      void *user_data)
1000 {
1001   pagesize_range_t *a = (pagesize_range_t *)range_a;
1002   pagesize_range_t *b = (pagesize_range_t *)range_b;
1003   int value;
1004 
1005   if ((value = compare_int(a->x_dim_min, b->x_dim_min)) == 0) {
1006     if ((value = compare_int(a->x_dim_max, b->x_dim_max)) == 0) {
1007       if ((value = compare_int(a->y_dim_min, b->y_dim_min)) == 0) {
1008         if ((value = compare_int(a->y_dim_max, b->y_dim_max)) == 0) {
1009           return 0;
1010         }
1011       }
1012     }
1013   }
1014   return value;
1015 }
1016 
compare_media(void * media_a,void * media_b,void * user_data)1017 int compare_media(void *media_a, void *media_b,
1018 		  void *user_data)
1019 {
1020   media_col_t  *a=(media_col_t *)media_a;
1021   media_col_t  *b=(media_col_t *)media_b;
1022   int value;
1023 
1024   if ((value = compare_int(a->x, b->x)) == 0) {
1025     if ((value = compare_int(a->y, b->y)) == 0) {
1026       if ((value = compare_int(a->top_margin, b->top_margin)) == 0) {
1027         if ((value = compare_int(a->bottom_margin, b->bottom_margin)) == 0) {
1028           if ((value = compare_int(a->right_margin, b->right_margin)) == 0) {
1029             if ((value = compare_int(a->left_margin, b->left_margin)) == 0) {
1030               if (a->media_source == NULL && b->media_source == NULL) {
1031                 if (a->media_type == NULL && b->media_type == NULL)
1032                   return 0;
1033                 if (a->media_type == NULL)
1034                   return -1;
1035                 if (b->media_type == NULL)
1036                   return 1;
1037                 return strcmp(a->media_type, b->media_type);
1038               }
1039               if (a->media_source == NULL)
1040                 return -1;
1041               if (b->media_source == NULL)
1042                 return 1;
1043               if (!strcmp(a->media_source, b->media_source)) {
1044                 if (a->media_type==NULL && b->media_type == NULL)
1045                   return 0;
1046                 if (a->media_type==NULL)
1047                   return -1;
1048                 if (b->media_type==NULL)
1049                   return 1;
1050                 return strcmp(a->media_type, b->media_type);
1051               }
1052               else
1053                 return strcmp(a->media_source, b->media_source);
1054             }
1055           }
1056         }
1057       }
1058     }
1059   }
1060   return value;
1061 }
1062 
compare_media_count(void * media_a,void * media_b,void * user_data)1063 int compare_media_count(void* media_a, void* media_b,void* user_data)
1064 {
1065   mediacol_count_t *a = (mediacol_count_t*) media_a;
1066   mediacol_count_t *b = (mediacol_count_t*) media_b;
1067 
1068   return (compare_media(a->data, b->data, NULL));
1069 }
1070 
1071 void *
copy_default_str(void * data,void * user_data)1072 copy_default_str(void *data, void *user_data)
1073 {
1074   default_str_attribute_t *prev = (default_str_attribute_t *)data;
1075   default_str_attribute_t *copy;
1076 
1077   copy = (default_str_attribute_t *)calloc(1, sizeof(default_str_attribute_t));
1078   if (copy) {
1079     copy->value = (char *)malloc(sizeof(char)*100);
1080     copy->value = strdup(prev->value);
1081     copy->count = prev->count;
1082   }
1083   return copy;
1084 }
1085 
strstrip(char * s)1086 char *strstrip(char *s)
1087 {
1088   size_t size;
1089   char *end;
1090 
1091   size = strlen(s);
1092 
1093   if (!size)
1094     return s;
1095 
1096   end = s + size - 1;
1097   while (end >= s && isspace(*end))
1098     end--;
1099   *(end + 1) = '\0';
1100 
1101   while (*s && isspace(*s))
1102     s++;
1103 
1104   return s;
1105 }
1106 
1107 int
compare_default_str(void * defstr_a,void * defstr_b,void * user_data)1108 compare_default_str(void *defstr_a, void *defstr_b,
1109 		    void *user_data)
1110 {
1111   default_str_attribute_t  *a=(default_str_attribute_t *)defstr_a;
1112   default_str_attribute_t  *b=(default_str_attribute_t *)defstr_b;
1113 
1114   return strcmp(a->value, b->value);
1115 }
1116 
1117 void *
copy_counted_res(void * data,void * user_data)1118 copy_counted_res(void *data, void *user_data)
1119 {
1120   resolution_count_t *prev = (resolution_count_t *)data;
1121   resolution_count_t *copy;
1122 
1123   copy = (resolution_count_t *)calloc(1, sizeof(resolution_count_t));
1124   if (copy) {
1125     copy->res = (res_t *)malloc(sizeof(res_t));
1126     copy->res->x = prev->res->x;
1127     copy->res->y = prev->res->y;
1128     copy->count = prev->count;
1129   }
1130   return copy;
1131 }
1132 
1133 
1134 int
compare_counted_res(void * defres_a,void * defres_b,void * user_data)1135 compare_counted_res(void *defres_a, void *defres_b,
1136 		    void *user_data)
1137 {
1138   resolution_count_t  *a=(resolution_count_t *)defres_a;
1139   resolution_count_t  *b=(resolution_count_t *)defres_b;
1140 
1141   return compare_resolutions(a->res, b->res, NULL);
1142 }
1143 
1144 /*
1145  * 'pwg_compare_sizes()' - Compare two media sizes...
1146  */
1147 
1148 static int        /* O - Result of comparison */
pwg_compare_sizes(cups_size_t * a,cups_size_t * b)1149 pwg_compare_sizes(cups_size_t *a, /* I - First media size */
1150                   cups_size_t *b) /* I - Second media size */
1151 {
1152   return (strcmp(a->media, b->media));
1153 }
1154 
1155 
1156 /*
1157  * 'pwg_copy_size()' - Copy a media size.
1158  */
1159 
1160 static cups_size_t *      /* O - New media size */
pwg_copy_size(cups_size_t * size)1161 pwg_copy_size(cups_size_t *size)  /* I - Media size to copy */
1162 {
1163   cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t));
1164           /* New media size */
1165 
1166   if (newsize)
1167     memcpy(newsize, size, sizeof(cups_size_t));
1168 
1169   return (newsize);
1170 }
1171 
1172 /* Function returns number of jobs queued on printer*/
1173 int         /* O - Number of jobs */
get_number_of_jobs(http_t * http,const char * uri,int myjobs,int whichjobs)1174 get_number_of_jobs(http_t       *http,      /* I - Connection to server */
1175                    const char   *uri,       /* I - uri of printer */
1176                    int          myjobs,     /* I - 0 = all users, 1 = mine */
1177                    int          whichjobs)  /* I - CUPS_WHICHJOBS_ALL,
1178                                                    CUPS_WHICHJOBS_ACTIVE, or
1179                                                    CUPS_WHICHJOBS_COMPLETED */
1180 {
1181   int     n;                              /* Number of jobs */
1182   ipp_t   *request,                       /* IPP Request */
1183           *response;                      /* IPP Response */
1184   ipp_attribute_t *attr;                  /* Current attribute */
1185   int   id;                               /* job-id */
1186   static const char * const attrs[] =     /* Requested attributes */
1187     {
1188       "job-id"
1189     };
1190 
1191   httpReconnect2(http, 30000, NULL);
1192 
1193  /*
1194   * Build an IPP_GET_JOBS request, which requires the following
1195   * attributes:
1196   *
1197   *    attributes-charset
1198   *    attributes-natural-language
1199   *    printer-uri
1200   *    requesting-user-name
1201   *    which-jobs
1202   *    my-jobs
1203   *    requested-attributes
1204   */
1205 
1206 
1207   /* Generating IPP Request */
1208   request = ippNewRequest(IPP_OP_GET_JOBS);
1209   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1210                "printer-uri", NULL, uri);
1211   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1212                "requesting-user-name", NULL, cupsUser());
1213   if (myjobs)
1214     ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
1215   if (whichjobs == CUPS_WHICHJOBS_COMPLETED)
1216     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1217                  "which-jobs", NULL, "completed");
1218   else if (whichjobs == CUPS_WHICHJOBS_ALL)
1219     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1220                  "which-jobs", NULL, "all");
1221   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1222                 "requested-attributes", sizeof(attrs) / sizeof(attrs[0]),
1223 		NULL, attrs);
1224 
1225   /* Do the request and get back a response... */
1226   n = 0;
1227   if ((response = cupsDoRequest(http, request, "/")) != NULL) {
1228     for (attr = ippFirstAttribute(response); attr;
1229 	 attr = ippNextAttribute(response)){
1230       /* Skip leading attributes until we hit a job... */
1231       while (attr && ippGetGroupTag(attr) != IPP_TAG_JOB)
1232 	attr = ippNextAttribute(response);
1233 
1234       if (!attr)
1235 	break;
1236       /* Pull the needed attributes from this job */
1237       id = 0;
1238       while (attr && ippGetGroupTag(attr) == IPP_TAG_JOB) {
1239 	if (!strcmp(ippGetName(attr), "job-id") &&
1240 	    ippGetValueTag(attr) == IPP_TAG_INTEGER)
1241 	  id = ippGetInteger(attr,0);
1242 	attr = ippNextAttribute(response);
1243       }
1244 
1245       /* See if we have everything needed */
1246       if (!id){
1247 	if (!attr)
1248 	  break;
1249 	else
1250 	  continue;
1251       }
1252       /* Incrementing number of jobs*/
1253       n ++;
1254       if (!attr)
1255 	break;
1256     }
1257 
1258     ippDelete(response);
1259   }
1260 
1261   if (n == 0)
1262     return (-1);
1263   else
1264     return (n);
1265 }
1266 
1267 static const char *
password_callback(const char * prompt,http_t * http,const char * method,const char * resource,void * user_data)1268 password_callback (const char *prompt,
1269 		   http_t *http,
1270 		   const char *method,
1271 		   const char *resource,
1272 		   void *user_data)
1273 {
1274   return NULL;
1275 }
1276 
1277 http_t *
httpConnectEncryptShortTimeout(const char * host,int port,http_encryption_t encryption)1278 httpConnectEncryptShortTimeout(const char *host, int port,
1279 			       http_encryption_t encryption)
1280 {
1281   return (httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 3000,
1282                        NULL));
1283 }
1284 
1285 int
http_timeout_cb(http_t * http,void * user_data)1286 http_timeout_cb(http_t *http, void *user_data)
1287 {
1288   debug_printf("HTTP timeout! (consider increasing HttpLocalTimeout/HttpRemoteTimeout value)\n");
1289   timeout_reached = 1;
1290   return 0;
1291 }
1292 
1293 static http_t *
http_connect_local(void)1294 http_connect_local (void)
1295 {
1296   const char *server = cupsServer();
1297   int port = ippPort();
1298 
1299   if (!local_conn) {
1300     if (server[0] == '/')
1301       debug_printf("cups-browsed: Creating http connection to local CUPS daemon via domain socket: %s\n",
1302 		   server);
1303     else
1304       debug_printf("cups-browsed: Creating http connection to local CUPS daemon: %s:%d\n",
1305 		   server, port);
1306     local_conn = httpConnectEncryptShortTimeout(server, port,
1307 						cupsEncryption());
1308   }
1309   if (local_conn)
1310     httpSetTimeout(local_conn, HttpLocalTimeout, http_timeout_cb, NULL);
1311   else {
1312     if (server[0] == '/')
1313       debug_printf("cups-browsed: Failed creating http connection to local CUPS daemon via domain socket: %s\n",
1314 		   server);
1315     else
1316       debug_printf("cups-browsed: Failed creating http connection to local CUPS daemon: %s:%d\n",
1317 		   server, port);
1318   }
1319 
1320   return local_conn;
1321 }
1322 
1323 static void
http_close_local(void)1324 http_close_local (void)
1325 {
1326   if (local_conn) {
1327     httpClose (local_conn);
1328     local_conn = NULL;
1329   }
1330 }
1331 
1332 int     /* O - 1 on match, 0 otherwise */
_cups_isalpha(int ch)1333 _cups_isalpha(int ch)     /* I - Character to test */
1334 {
1335   return ((ch >= 'A' && ch <= 'Z') ||
1336           (ch >= 'a' && ch <= 'z'));
1337 }
1338 
1339 int     /* O - 1 on match, 0 otherwise */
_cups_islower(int ch)1340 _cups_islower(int ch)     /* I - Character to test */
1341 {
1342   return (ch >= 'a' && ch <= 'z');
1343 }
1344 
1345 int     /* O - Converted character */
_cups_toupper(int ch)1346 _cups_toupper(int ch)     /* I - Character to convert */
1347 {
1348   return (_cups_islower(ch) ? ch - 'a' + 'A' : ch);
1349 }
1350 
1351 static void
pwg_ppdize_name(const char * ipp,char * name,size_t namesize)1352 pwg_ppdize_name(const char *ipp,  /* I - IPP keyword */
1353                 char       *name, /* I - Name buffer */
1354 		size_t     namesize)  /* I - Size of name buffer */
1355 {
1356   char  *ptr,       /* Pointer into name buffer */
1357         *end;       /* End of name buffer */
1358 
1359   *name = (char)toupper(*ipp++);
1360 
1361   for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
1362   {
1363     if (*ipp == '-' && _cups_isalpha(ipp[1]))
1364     {
1365       ipp ++;
1366       *ptr++ = (char)toupper(*ipp++ & 255);
1367     }
1368     else
1369       *ptr++ = *ipp++;
1370   }
1371 
1372   *ptr = '\0';
1373 }
1374 
add_mimetype_attributes(char * cluster_name,ipp_t ** merged_attributes)1375 void add_mimetype_attributes(char* cluster_name, ipp_t **merged_attributes)
1376 {
1377   int                  count, i;
1378   remote_printer_t     *p;
1379   const char           *str;
1380   char                 *q;
1381   cups_array_t         *list;
1382   ipp_attribute_t      *attr;
1383   int                  num_value, attr_no;
1384   char* attributes[] = {
1385     "document-format-supported"
1386   };
1387 
1388   for (attr_no = 0; attr_no < 1; attr_no++) {
1389     if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1390 			      (cups_acopy_func_t)strdup,
1391 			      (cups_afree_func_t)free)) == NULL)
1392       return ;
1393 
1394     num_value = 0;
1395     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1396 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1397       if (strcmp(cluster_name,p->queue_name))
1398         continue;
1399       if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1400 	 p->status ==  STATUS_TO_BE_RELEASED )
1401         continue;
1402       if ((attr = ippFindAttribute(p->prattrs,attributes[attr_no],
1403 				   IPP_TAG_MIMETYPE)) != NULL) {
1404 	count = ippGetCount(attr);
1405 	for (i = 0; i < count; i ++) {
1406 	  str = ippGetString(attr, i, NULL);
1407 	  if (!cupsArrayFind(list, (void *)str)){
1408 	    cupsArrayAdd(list, (void *)str);
1409 	    num_value++;
1410 	  }
1411 	}
1412       }
1413     }
1414     if (num_value != 0) {
1415       char    *values[num_value];
1416       for (q = (char *)cupsArrayFirst(list),i=0;
1417 	   q;
1418 	   q = (char *)cupsArrayNext(list),i++) {
1419         values[i]=malloc(sizeof(char) * (strlen(q) + 1));
1420         snprintf(values[i], strlen(q) + 1, "%s", q);
1421       }
1422       ippAddStrings(*merged_attributes, IPP_TAG_PRINTER,IPP_TAG_MIMETYPE,
1423 		    attributes[attr_no], num_value, NULL,
1424 		    (const char * const *)values);
1425 
1426       for (int k = 0; k < i; k++) {
1427         free(values[k]);
1428         values[k] = NULL;
1429       }
1430     }
1431     cupsArrayDelete(list);
1432     list = NULL;
1433   }
1434 }
1435 
1436 /*add_tagzero_attributes - Adds attribute to the merged_attribute variable for
1437                            the cluster. This function adds attribute with value
1438                            tag IPP_TAG_ZERO */
add_tagzero_attributes(char * cluster_name,ipp_t ** merged_attributes)1439 void add_tagzero_attributes(char* cluster_name, ipp_t **merged_attributes)
1440 {
1441   int                  count, i;
1442   remote_printer_t     *p;
1443   const char           *str;
1444   char                 *q;
1445   cups_array_t         *list;
1446   ipp_attribute_t      *attr;
1447   int                  num_value, attr_no;
1448   char* attributes[] = {
1449     "media-supported",
1450     "output-bin-supported",
1451     "print-content-optimize-supported",
1452     "print-rendering-intent-supported",
1453     "print-scaling-supported"
1454   };
1455 
1456   for (attr_no = 0; attr_no < 5; attr_no++) {
1457     /* Cups Array to store the values for the attribute*/
1458     if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1459 			      (cups_acopy_func_t)strdup,
1460 			      (cups_afree_func_t)free)) == NULL)
1461       return ;
1462 
1463     num_value = 0;
1464     /* Iterating over all the printers in the cluster*/
1465     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1466 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1467       if (strcmp(cluster_name, p->queue_name))
1468 	continue;
1469       if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1470 	 p->status ==  STATUS_TO_BE_RELEASED )
1471 	continue;
1472       if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no],
1473 				   IPP_TAG_ZERO)) != NULL) {
1474 	count = ippGetCount(attr);
1475 	for(i = 0; i < count; i ++) {
1476 	  /* Pick next format from attribute */
1477 	  str = ippGetString(attr, i, NULL);
1478 	  /* Add format to list, skip duplicates */
1479 	  if (!cupsArrayFind(list, (void *)str)){
1480 	    cupsArrayAdd(list, (void *)str);
1481 	    num_value ++;
1482 	  }
1483 	}
1484       }
1485     }
1486     if (num_value != 0) {
1487       char    *values[num_value];
1488       /* Transferring attributes value from cups Array to char* array*/
1489       for (q = (char *)cupsArrayFirst(list), i = 0; q;
1490            q = (char *)cupsArrayNext(list), i ++) {
1491         values[i] = malloc(sizeof(char) * (strlen(q) + 1));
1492         snprintf(values[i], strlen(q) + 1, "%s", q);
1493       }
1494       ippAddStrings(*merged_attributes, IPP_TAG_PRINTER,
1495                     IPP_TAG_KEYWORD, attributes[attr_no],
1496                     num_value, NULL,
1497                     (const char * const *)values);
1498 
1499       for (int k = 0; k < i; k++) {
1500         free(values[k]);
1501         values[k] = NULL;
1502       }
1503     }
1504     cupsArrayDelete(list);
1505     list = NULL;
1506   }
1507 }
1508 
1509 /*add_keyword_attributes - Adds attributes to the merged_attribute variable for
1510                            the cluster. This function adds attributes with
1511                            value tag IPP_TAG_KEYWORD*/
add_keyword_attributes(char * cluster_name,ipp_t ** merged_attributes)1512 void add_keyword_attributes(char* cluster_name, ipp_t **merged_attributes)
1513 {
1514   int                  count, i;
1515   remote_printer_t     *p;
1516   const char           *str;
1517   char                 *q;
1518   cups_array_t         *list;
1519   ipp_attribute_t      *attr;
1520   int                  num_value, attr_no;
1521   char* attributes[] = {
1522     "output-mode-supported",
1523     "urf-supported",
1524     "pwg-raster-document-type-supported",
1525     "media-source-supported",
1526     "media-type-supported",
1527     "print-color-mode-supported",
1528     "sides-supported"
1529   };
1530 
1531   for (attr_no = 0; attr_no < 7; attr_no ++) {
1532     if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1533 			      (cups_acopy_func_t)strdup,
1534 			      (cups_afree_func_t)free)) == NULL)
1535       return;
1536 
1537     num_value = 0;
1538     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1539          p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1540       if (strcmp(cluster_name, p->queue_name))
1541         continue;
1542       if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1543 	 p->status == STATUS_TO_BE_RELEASED )
1544         continue;
1545       if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no],
1546 				   IPP_TAG_KEYWORD)) != NULL) {
1547         count = ippGetCount(attr);
1548         for (i = 0; i < count; i++) {
1549           str = ippGetString(attr, i, NULL);
1550           if (!cupsArrayFind(list, (void *)str)){
1551             cupsArrayAdd(list, (void *)str);
1552             num_value ++;
1553           }
1554         }
1555       }
1556     }
1557     if (num_value != 0) {
1558       char    *values[num_value];
1559       for (q = (char *)cupsArrayFirst(list), i=0;
1560 	   q;
1561 	   q = (char *)cupsArrayNext(list), i ++) {
1562         values[i] = malloc(sizeof(char) * (strlen(q) + 1));
1563         snprintf(values[i], strlen(q) + 1, "%s", q);
1564       }
1565       ippAddStrings(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1566 		    attributes[attr_no], num_value, NULL,
1567 		    (const char * const *)values);
1568 
1569       for (int k = 0; k < i; k++) {
1570         free(values[k]);
1571         values[k] = NULL;
1572       }
1573     }
1574     cupsArrayDelete(list);
1575     list = NULL;
1576   }
1577 }
1578 
1579 /*add_enum_attributes - Adds attributes to the merged_attribute variable for
1580                         the cluster. This function adds attributes with value
1581                         tag IPP_TAG_BEGIN_ENUM*/
add_enum_attributes(char * cluster_name,ipp_t ** merged_attributes)1582 void add_enum_attributes(char* cluster_name, ipp_t **merged_attributes)
1583 {
1584   int                  count, i, value;
1585   remote_printer_t     *p;
1586   char                 *str = NULL;
1587   char                 *q;
1588   cups_array_t         *list;
1589   ipp_attribute_t      *attr;
1590   int                  num_value, attr_no;
1591   char* attributes[] = {
1592                          "finishings-supported",
1593                          "print-quality-supported",
1594                          "finishing-template",
1595                          "finishings-col-database"
1596                        };
1597 
1598   for (attr_no = 0; attr_no < 4; attr_no ++) {
1599     if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1600 			      (cups_acopy_func_t)strdup,
1601 			      (cups_afree_func_t)free)) == NULL)
1602       return ;
1603     str = malloc(sizeof(char) * 10);
1604     num_value = 0;
1605     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1606          p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1607       if (strcmp(cluster_name,p->queue_name))
1608         continue;
1609       if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1610         p->status ==  STATUS_TO_BE_RELEASED )
1611         continue;
1612       if ((attr = ippFindAttribute(p->prattrs,attributes[attr_no],  IPP_TAG_ENUM)) != NULL) {
1613         count = ippGetCount(attr);
1614         for (i = 0; i < count; i ++) {
1615           value = ippGetInteger(attr, i);
1616           sprintf(str,"%d",value);
1617           if (!cupsArrayFind(list, (void *)str)){
1618             cupsArrayAdd(list, (void *)str);
1619             num_value++;
1620           }
1621         }
1622       }
1623     }
1624 
1625     if (num_value != 0){
1626       int   values[num_value];
1627       for (q = (char *)cupsArrayFirst(list), i = 0;q;
1628            q = (char *)cupsArrayNext(list), i++) {
1629         values[i] = atoi(q);
1630       }
1631       ippAddIntegers(*merged_attributes, IPP_TAG_PRINTER,IPP_TAG_ENUM,
1632 		     attributes[attr_no], num_value,values);
1633     }
1634 
1635     if (str != NULL) {
1636       free(str);
1637       str = NULL;
1638     }
1639     cupsArrayDelete(list);
1640     list = NULL;
1641   }
1642 }
1643 
1644 /*add_margin_attribute - Adds margin attributes to the merged_attribute variable for the cluster.*/
add_margin_attributes(char * cluster_name,ipp_t ** merged_attributes)1645 void add_margin_attributes(char* cluster_name, ipp_t **merged_attributes)
1646 {
1647   int                  count, i, value;
1648   remote_printer_t     *p;
1649   char                 *str;
1650   char                 *q;
1651   cups_array_t         *list;
1652   ipp_attribute_t      *attr;
1653   int                  num_value, attr_no;
1654   char* attributes[] = {
1655     "media-bottom-margin-supported",
1656     "media-left-margin-supported",
1657     "media-top-margin-supported",
1658     "media-right-margin-supported"
1659   };
1660 
1661   for (attr_no = 0; attr_no < 4; attr_no++) {
1662     if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1663 			      (cups_acopy_func_t)strdup,
1664 			      (cups_afree_func_t)free)) == NULL)
1665       return ;
1666     str = malloc(sizeof(char)*10);
1667     num_value = 0;
1668     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1669          p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1670       if (strcmp(cluster_name,p->queue_name))
1671         continue;
1672       if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1673       p->status ==  STATUS_TO_BE_RELEASED )
1674         continue;
1675       if ((attr = ippFindAttribute(p->prattrs,attributes[attr_no],  IPP_TAG_INTEGER)) != NULL) {
1676         count = ippGetCount(attr);
1677         for (i = 0; i < count; i++) {
1678           value = ippGetInteger(attr, i);
1679           sprintf(str,"%d",value);
1680           if (!cupsArrayFind(list, (void *)str)){
1681             cupsArrayAdd(list, (void *)str);
1682             num_value++;
1683           }
1684         }
1685       }
1686     }
1687 
1688     if (num_value != 0){
1689       int   values[num_value];
1690       for (q = (char *)cupsArrayFirst(list),i=0;q;
1691            q = (char *)cupsArrayNext(list),i++) {
1692         values[i] = atoi(q);
1693       }
1694       ippAddIntegers(*merged_attributes, IPP_TAG_PRINTER,IPP_TAG_INTEGER,
1695 		     attributes[attr_no], num_value,values);
1696     }
1697 
1698     if (str != NULL) {
1699       free(str);
1700       str = NULL;
1701     }
1702     cupsArrayDelete(list);
1703     list = NULL;
1704   }
1705 }
1706 
1707 /*add_resolution_attributes - Adds resolution attributes to the merged_attribute
1708                               for the cluster*/
add_resolution_attributes(char * cluster_name,ipp_t ** merged_attributes)1709 void add_resolution_attributes(char* cluster_name, ipp_t **merged_attributes)
1710 {
1711   int                  count, i;
1712   remote_printer_t     *p;
1713   ipp_attribute_t      *attr;
1714   int                  num_resolution, attr_no;
1715   cups_array_t         *res_array;
1716   res_t                *res, *resolution;
1717   char* attributes[] = {
1718                          "printer-resolution-supported",
1719                          "pwg-raster-document-resolution-supported",
1720                          "pclm-source-resolution-supported"
1721                        };
1722 
1723   for (attr_no = 0; attr_no < 3; attr_no ++) {
1724     res_array = NULL;
1725     res_array = resolutionArrayNew();
1726     num_resolution = 0;
1727     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1728 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1729       if (strcmp(cluster_name, p->queue_name))
1730 	continue;
1731       if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1732 	 p->status == STATUS_TO_BE_RELEASED )
1733 	continue;
1734       if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no],
1735 				   IPP_TAG_RESOLUTION)) != NULL) {
1736         for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1737           if ((res = ippResolutionToRes(attr, i)) != NULL) {
1738 	          if (cupsArrayFind(res_array, res) == NULL) {
1739 	            cupsArrayAdd(res_array, res);
1740 	            num_resolution ++;
1741 	          }
1742 	          free_resolution(res, NULL);
1743 	        }
1744 	      }
1745       }
1746     }
1747     if (num_resolution) {
1748       int xres[num_resolution], yres[num_resolution];
1749       for (i = 0, resolution=cupsArrayFirst(res_array); resolution;
1750 	   i ++, resolution = cupsArrayNext(res_array)) {
1751 	xres[i] = resolution->x;
1752 	yres[i] = resolution->y;
1753       }
1754       ippAddResolutions(*merged_attributes, IPP_TAG_PRINTER,
1755 			attributes[attr_no], num_resolution,
1756 			IPP_RES_PER_INCH, xres, yres);
1757     }
1758     cupsArrayDelete(res_array);
1759     res_array = NULL;
1760   }
1761 }
1762 
1763 /*add_mediasize_attribute - Adds media sizes to the merged_attribute for the
1764                             printer*/
add_mediasize_attributes(char * cluster_name,ipp_t ** merged_attributes)1765 void add_mediasize_attributes(char* cluster_name, ipp_t **merged_attributes)
1766 {
1767   int                  count, i = 0;
1768   remote_printer_t     *p;
1769   ipp_attribute_t      *attr, *media_size_supported, *x_dim, *y_dim;
1770   int                  num_sizes, attr_no,num_ranges;
1771   ipp_t                *media_size;
1772   cups_array_t         *sizes, *size_ranges;
1773   media_size_t         *temp, *media_s;
1774   pagesize_range_t     *temp_range = NULL, *range = NULL;
1775   char* attributes[] = {
1776                          "media-size-supported",
1777                        };
1778 
1779   sizes = cupsArrayNew3((cups_array_func_t)compare_mediasize, NULL, NULL, 0,
1780 			(cups_acopy_func_t)copy_media_size,
1781 			(cups_afree_func_t)free);
1782   size_ranges = cupsArrayNew3((cups_array_func_t)compare_rangesize, NULL, NULL,
1783 			      0,
1784 			      (cups_acopy_func_t)copy_range_size,
1785 			      (cups_afree_func_t)free);
1786   temp = (media_size_t *)malloc(sizeof(media_size_t));
1787   temp_range = (pagesize_range_t *)malloc(sizeof(pagesize_range_t));
1788   for (attr_no = 0; attr_no < 1; attr_no ++) {
1789     num_sizes = 0;
1790     num_ranges = 0;
1791     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1792 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1793       if (strcmp(cluster_name,p->queue_name))
1794         continue;
1795       if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1796 	 p->status ==  STATUS_TO_BE_RELEASED )
1797         continue;
1798       if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no],
1799 				   IPP_TAG_BEGIN_COLLECTION)) != NULL) {
1800         for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1801           media_size  = ippGetCollection(attr, i);
1802           x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
1803           y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
1804           if (ippGetValueTag(x_dim) == IPP_TAG_RANGE ||
1805 	      ippGetValueTag(y_dim) == IPP_TAG_RANGE) {
1806             if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
1807               temp_range->x_dim_min = ippGetRange(x_dim, 0,
1808 						  &temp_range->x_dim_max);
1809             else
1810               temp_range->x_dim_min = temp_range->x_dim_max =
1811 		ippGetInteger(x_dim, 0);
1812 
1813             if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
1814               temp_range->y_dim_min = ippGetRange(y_dim, 0,
1815 						  &temp_range->y_dim_max);
1816             else
1817               temp_range->y_dim_min = temp_range->y_dim_max =
1818 		ippGetInteger(y_dim, 0);
1819             if (!cupsArrayFind(size_ranges,temp_range)) {
1820               cupsArrayAdd(size_ranges, temp_range);
1821               num_ranges++;
1822             }
1823           } else {
1824             temp->x = ippGetInteger(x_dim,0);
1825             temp->y = ippGetInteger(y_dim,0);
1826             if (!cupsArrayFind(sizes, temp)){
1827               cupsArrayAdd(sizes, temp);
1828               num_sizes++;
1829             }
1830           }
1831         }
1832       }
1833     }
1834     media_size_supported =
1835       ippAddCollections(*merged_attributes,
1836 			IPP_TAG_PRINTER, attributes[attr_no],
1837 			num_sizes+num_ranges, NULL);
1838     if (num_sizes) {
1839       for (i = 0, media_s = cupsArrayFirst(sizes);
1840 	   media_s; i ++, media_s = cupsArrayNext(sizes)) {
1841         ipp_t *size = create_media_size(media_s->x, media_s->y);
1842         ippSetCollection(*merged_attributes, &media_size_supported, i, size);
1843         ippDelete(size);
1844       }
1845     }
1846     if (num_ranges) {
1847       for (range = cupsArrayFirst(size_ranges); range;
1848 	   i++, range = cupsArrayNext(size_ranges)) {
1849         ipp_t *size_range = create_media_range(range->x_dim_min,
1850 					       range->x_dim_max,
1851 					       range->y_dim_min,
1852 					       range->y_dim_max);
1853         ippSetCollection(*merged_attributes, &media_size_supported, i,
1854 			 size_range);
1855         ippDelete(size_range);
1856       }
1857     }
1858   }
1859 
1860   free(temp);
1861   free(temp_range);
1862   cupsArrayDelete(sizes);
1863   cupsArrayDelete(size_ranges);
1864 }
1865 
1866 /*add_mediadatabase_attribute - Adds media-col-database attributes for the
1867                                 cluster*/
1868 void
add_mediadatabase_attributes(char * cluster_name,ipp_t ** merged_attributes)1869 add_mediadatabase_attributes(char* cluster_name, ipp_t **merged_attributes)
1870 {
1871   int                  count, i;
1872   remote_printer_t     *p;
1873   ipp_attribute_t      *attr, *media_attr;
1874   int                  num_database, attr_no;
1875   cups_array_t         *media_database;
1876   media_col_t          *temp, *media_data;
1877   ipp_t                *media_col,
1878                        *media_size, *current_media;
1879   ipp_attribute_t      *media_col_database;
1880   char                 media_source[32], media_type[32];
1881   char* attributes[] = {
1882                          "media-col-database",
1883                        };
1884   temp = (media_col_t *)malloc(sizeof(media_col_t));
1885   media_database = cupsArrayNew3((cups_array_func_t)compare_media,
1886 				 NULL, NULL, 0,
1887 				 (cups_acopy_func_t)copy_media,
1888 				 (cups_afree_func_t)free);
1889   for (attr_no = 0; attr_no < 1; attr_no ++) {
1890     num_database = 0;
1891     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1892          p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1893       if (strcmp(cluster_name, p->queue_name))
1894         continue;
1895       if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1896 	 p->status == STATUS_TO_BE_RELEASED )
1897         continue;
1898       if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no],
1899 				   IPP_TAG_BEGIN_COLLECTION)) != NULL){
1900         for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1901           media_col = ippGetCollection(attr, i);
1902           media_size =
1903 	    ippGetCollection(ippFindAttribute(media_col,
1904 					      "media-size",
1905 					      IPP_TAG_BEGIN_COLLECTION), 0);
1906           temp->x = ippGetInteger(ippFindAttribute(media_size, "x-dimension",
1907 						   IPP_TAG_ZERO),0);
1908           temp->y = ippGetInteger(ippFindAttribute(media_size, "y-dimension",
1909 						   IPP_TAG_ZERO),0);
1910           temp->top_margin =
1911 	    ippGetInteger(ippFindAttribute(media_col,
1912 					   "media-top-margin",
1913 					   IPP_TAG_INTEGER),
1914 			  0);
1915           temp->bottom_margin =
1916 	    ippGetInteger(ippFindAttribute(media_col,
1917 					   "media-bottom-margin",
1918 					   IPP_TAG_INTEGER),
1919 			  0);
1920           temp->left_margin =
1921 	    ippGetInteger(ippFindAttribute(media_col,
1922 					   "media-left-margin",
1923 					   IPP_TAG_INTEGER),
1924 			  0);
1925           temp->right_margin =
1926 	    ippGetInteger(ippFindAttribute(media_col,
1927 					   "media-right-margin",
1928 					   IPP_TAG_INTEGER),
1929 			  0);
1930           media_type[0] = '\0';
1931           media_source[0] = '\0';
1932           temp->media_source = NULL;
1933           temp->media_type = NULL;
1934           if ((media_attr = ippFindAttribute(media_col,
1935 					     "media-type",
1936 					     IPP_TAG_KEYWORD)) != NULL)
1937             pwg_ppdize_name(ippGetString(media_attr, 0, NULL), media_type,
1938 			    sizeof(media_type));
1939           if (strlen(media_type) > 1) {
1940             temp->media_type = (char*)malloc(sizeof(char) * 32);
1941             strcpy(temp->media_type, media_type);
1942           }
1943           if ((media_attr = ippFindAttribute(media_col, "media-source",
1944 					     IPP_TAG_KEYWORD)) != NULL) {
1945             pwg_ppdize_name(ippGetString(media_attr, 0, NULL), media_source,
1946 			    sizeof(media_source));
1947           }
1948           if(strlen(media_source) > 1) {
1949             temp->media_source = (char*)malloc(sizeof(char) * 32);
1950             strcpy(temp->media_source, media_source);
1951           }
1952 
1953           if (!cupsArrayFind(media_database, temp)) {
1954             cupsArrayAdd(media_database, temp);
1955             num_database ++;
1956           }
1957         }
1958       }
1959     }
1960 
1961     if (num_database != 0) {
1962       media_col_database = ippAddCollections(*merged_attributes,
1963 					     IPP_TAG_PRINTER,
1964 					     attributes[attr_no],
1965 					     num_database, NULL);
1966       for (i = 0, media_data = cupsArrayFirst(media_database); media_data;
1967            i ++, media_data = cupsArrayNext(media_database)) {
1968         current_media = create_media_col(media_data->x, media_data->y,
1969 					 media_data->left_margin,
1970 					 media_data->right_margin,
1971 					 media_data->top_margin,
1972 					 media_data->bottom_margin,
1973 					 media_data->media_source,
1974 					 media_data->media_type);
1975         ippSetCollection(*merged_attributes, &media_col_database, i,
1976 			 current_media);
1977         ippDelete(current_media);
1978       }
1979     }
1980   }
1981 
1982   free(temp);
1983   cupsArrayDelete(media_database);
1984 }
1985 
1986 /*add_jobpresets_attribute - Adds presets attributes for the cluster*/
add_jobpresets_attribute(char * cluster_name,ipp_t ** merged_attributes)1987 void add_jobpresets_attribute(char* cluster_name, ipp_t ** merged_attributes)
1988 {
1989   int                  count, i, num_preset = 0, preset_no = 0;
1990   remote_printer_t     *p;
1991   cups_array_t         *list, *added_presets;
1992   ipp_t                *preset;
1993   ipp_attribute_t      *attr;
1994   const char           *preset_name;
1995   ipp_attribute_t      *preset_attribute;
1996 
1997   if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1998 			    (cups_acopy_func_t)strdup,
1999 			    (cups_afree_func_t)free)) == NULL)
2000     return;
2001 
2002   if ((added_presets = cupsArrayNew3((cups_array_func_t)strcasecmp,
2003 				     NULL, NULL, 0,
2004 				     (cups_acopy_func_t)strdup,
2005 				     (cups_afree_func_t)free)) == NULL)
2006     return;
2007 
2008   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2009        p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2010     if (strcmp(cluster_name, p->queue_name))
2011       continue;
2012     if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
2013        p->status == STATUS_TO_BE_RELEASED )
2014       continue;
2015     if ((attr = ippFindAttribute(p->prattrs, "job-presets-supported",
2016 				 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
2017       for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2018         preset = ippGetCollection(attr, i);
2019         preset_name = ippGetString(ippFindAttribute(preset, "preset-name",
2020 						    IPP_TAG_ZERO), 0, NULL);
2021         if (!cupsArrayFind(list, (void *)preset_name)) {
2022           cupsArrayAdd(list, (void *)preset_name);
2023           num_preset++;
2024         }
2025       }
2026     }
2027   }
2028 
2029   if (num_preset == 0) {
2030     cupsArrayDelete(list);
2031     cupsArrayDelete(added_presets);
2032     return;
2033   }
2034 
2035   preset_attribute = ippAddCollections(*merged_attributes, IPP_TAG_PRINTER,
2036 				       "job-presets-supported", num_preset,
2037 				       NULL);
2038 
2039   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2040        p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2041     if ((attr = ippFindAttribute(p->prattrs, "job-presets-supported",
2042 				 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
2043       for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2044         preset = ippGetCollection(attr, i);
2045         preset_name = ippGetString(ippFindAttribute(preset, "preset-name",
2046 						    IPP_TAG_ZERO), 0, NULL);
2047         if (!cupsArrayFind(added_presets, (void *)preset_name)) {
2048           cupsArrayAdd(added_presets, (void *)preset_name);
2049           ippSetCollection(*merged_attributes, &preset_attribute, i, preset);
2050           preset_no++;
2051         } else
2052           continue;
2053       }
2054     }
2055   }
2056 
2057   cupsArrayDelete(list);
2058   cupsArrayDelete(added_presets);
2059 }
2060 
2061 /* get_pagesize: Function returns the standard/custom page size using
2062                  generate_sizes function from ppdgenerator.c*/
get_pagesize(ipp_t * printer_attributes)2063 static cups_array_t* get_pagesize(ipp_t *printer_attributes)
2064 {
2065   cups_array_t            *sizes, *page_media;
2066   cups_size_t             *size;
2067   ipp_attribute_t         *defattr;
2068   char                    *ppdsizename, *temp;
2069   int                     min_length = INT_MAX, min_width = INT_MAX,
2070                           max_length = 0, max_width = 0,
2071                           bottom, left, right, top;
2072   char                    ppdname[41];
2073 
2074   ppdsizename = (char *)malloc(sizeof(char) * 128);
2075   sizes = generate_sizes(printer_attributes, &defattr, &min_length, &min_width,
2076 			 &max_length, &max_width,
2077                          &bottom, &left, &right, &top,ppdname);
2078   if ((page_media = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2079 				  (cups_acopy_func_t)strdup,
2080 				  (cups_afree_func_t)free)) == NULL)
2081     return NULL;
2082   for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
2083        size = (cups_size_t *)cupsArrayNext(sizes)) {
2084     strcpy(ppdsizename, size->media);
2085     if (( temp = strchr(ppdsizename, ' ')) != NULL)
2086       *temp = '\0';
2087     cupsArrayAdd(page_media, ppdsizename);
2088   }
2089   free(ppdsizename);
2090   cupsArrayDelete(sizes);
2091 
2092   return page_media;
2093 }
2094 
2095 /*get_mediadata - This function extracts the MediaType, InputSlot and OutputBin
2096                   supported, using IPP Response message of the printer*/
get_mediadata(ipp_t * printer_attributes,char * requested_attr)2097 cups_array_t* get_mediadata(ipp_t *printer_attributes, char* requested_attr)
2098 {
2099   ipp_attribute_t         *attr;
2100   int                     count, i;
2101   cups_array_t            *media_data;
2102   const char              *keyword; /* Keyword value */
2103   char                    ppdname[41];
2104   char                    requested_option[30];
2105 
2106   if (!strcmp(requested_attr, "MediaType"))
2107     strcpy(requested_option, "media-type-supported");
2108   else if (!strcmp(requested_attr, "InputSlot"))
2109     strcpy(requested_option, "media-source-supported");
2110   else if (!strcmp(requested_attr, "OutputBin"))
2111     strcpy(requested_option, "output-bin-supported");
2112   else
2113     return NULL;
2114 
2115   if ((media_data = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2116 				  (cups_acopy_func_t)strdup,
2117 				  (cups_afree_func_t)free)) == NULL)
2118     return NULL;
2119   if ((attr = ippFindAttribute(printer_attributes, requested_option,
2120 			       IPP_TAG_ZERO)) != NULL
2121       && (count = ippGetCount(attr)) > 1) {
2122     for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2123       keyword = ippGetString(attr, i, NULL);
2124       pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
2125       cupsArrayAdd(media_data, ppdname);
2126     }
2127   }
2128   return media_data;
2129 }
2130 
2131 /*get_mimetype_attribute - Adds attributes to the merged_attribute variable for
2132                            the cluster. This function adds attribute with value
2133                            tag IPP_TAG_MIMETYPE*/
get_mimetype_attributes(ipp_t * printer_attributes)2134 cups_array_t* get_mimetype_attributes(ipp_t *printer_attributes)
2135 {
2136   int                  count, i;
2137   const char           *str;
2138   cups_array_t         *document_formats;
2139   ipp_attribute_t      *attr;
2140 
2141   if ((document_formats = cupsArrayNew3((cups_array_func_t)strcasecmp,
2142 					NULL, NULL, 0,
2143 					(cups_acopy_func_t)strdup,
2144 					(cups_afree_func_t)free)) == NULL)
2145     return NULL;
2146 
2147   if ((attr = ippFindAttribute(printer_attributes, "document-format-supported",
2148 			       IPP_TAG_MIMETYPE)) != NULL) {
2149     count = ippGetCount(attr);
2150     for (i = 0; i < count; i ++) {
2151       str = ippGetString(attr, i, NULL);
2152       if (!cupsArrayFind(document_formats, (void *)str))
2153 	cupsArrayAdd(document_formats, (void *)str);
2154     }
2155   }
2156   return document_formats;
2157 }
2158 
2159 /*get_staplelocation: This function returns the supported staple locations of
2160                       the printer */
get_staplelocation(ipp_t * printer_attributes)2161 cups_array_t* get_staplelocation(ipp_t *printer_attributes)
2162 {
2163   ipp_attribute_t        *attr;
2164   int                    count, value, i;
2165   const char             *name;
2166   cups_array_t           *staplelocation;
2167 
2168   if ((staplelocation = cupsArrayNew3((cups_array_func_t)strcasecmp,
2169 				      NULL, NULL, 0,
2170 				      (cups_acopy_func_t)strdup,
2171 				      (cups_afree_func_t)free)) == NULL)
2172     return NULL;
2173   if ((attr = ippFindAttribute(printer_attributes, "finishings-supported",
2174 			       IPP_TAG_ENUM)) != NULL) {
2175     count = ippGetCount(attr);
2176     for (i = 0; i < count; i ++) {
2177       value = ippGetInteger(attr, i);
2178       name  = ippEnumString("finishings", value);
2179       if (!strncmp(name, "staple-", 7) || !strncmp(name, "bind-", 5) ||
2180 	  !strncmp(name, "edge-stitch-", 12) || !strcmp(name, "saddle-stitch"))
2181         if (!cupsArrayFind(staplelocation,(void*)name))
2182           cupsArrayAdd(staplelocation,(void*)name);
2183     }
2184   }
2185   return staplelocation;
2186 }
2187 
2188 /*get_foldtype - Function returns the supported foldtype for the printer*/
get_foldtype(ipp_t * printer_attributes)2189 cups_array_t* get_foldtype(ipp_t *printer_attributes)
2190 {
2191   ipp_attribute_t        *attr;
2192   int                    count, value, i;
2193   const char             *name;
2194   cups_array_t           *foldtype;
2195 
2196   if ((foldtype = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2197 				(cups_acopy_func_t)strdup,
2198 				(cups_afree_func_t)free)) == NULL)
2199     return NULL;
2200   if ((attr = ippFindAttribute(printer_attributes, "finishings-supported",
2201 			       IPP_TAG_ENUM)) != NULL) {
2202     count = ippGetCount(attr);
2203     for (i = 0; i < count; i ++) {
2204       value = ippGetInteger(attr, i);
2205       name  = ippEnumString("finishings", value);
2206       if (!strncmp(name, "fold-", 5))
2207         if (!cupsArrayFind(foldtype, (void*)name))
2208           cupsArrayAdd(foldtype, (void*)name);
2209     }
2210   }
2211   return foldtype;
2212 }
2213 
2214 /*get_finishings - Function returns the supported finishings for the printer*/
get_finishings(ipp_t * printer_attributes)2215 cups_array_t* get_finishings(ipp_t *printer_attributes)
2216 {
2217   ipp_attribute_t        *attr;
2218   int                    count, value, i;
2219   const char             *name;
2220   cups_array_t           *finishings;
2221 
2222   if ((finishings = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2223 				  (cups_acopy_func_t)strdup,
2224 				  (cups_afree_func_t)free)) == NULL)
2225     return NULL;
2226   if ((attr = ippFindAttribute(printer_attributes, "finishings-supported",
2227 			       IPP_TAG_ENUM)) != NULL) {
2228     count = ippGetCount(attr);
2229     for (i = 0; i < count; i ++) {
2230       value = ippGetInteger(attr, i);
2231       name  = ippEnumString("finishings", value);
2232       if (!cupsArrayFind(finishings, (void*)name))
2233 	cupsArrayAdd(finishings, (void*)name);
2234     }
2235   }
2236   return finishings;
2237 }
2238 
2239 
2240 /*get_punchmedia - Returns the puchmedia supported by the printer*/
get_punchmedia(ipp_t * printer_attributes)2241 cups_array_t* get_punchmedia(ipp_t *printer_attributes)
2242 {
2243   ipp_attribute_t        *attr;
2244   int                    count, value, i;
2245   const char             *name;
2246   cups_array_t           *punchmedia;
2247 
2248   if ((punchmedia = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2249 				  (cups_acopy_func_t)strdup,
2250 				  (cups_afree_func_t)free)) == NULL)
2251     return NULL;
2252   if ((attr = ippFindAttribute(printer_attributes, "finishings-supported",
2253 			       IPP_TAG_ENUM)) != NULL) {
2254     count = ippGetCount(attr);
2255     for (i = 0; i < count; i ++) {
2256       value = ippGetInteger(attr, i);
2257       name  = ippEnumString("finishings", value);
2258       if (!strncmp(name, "punch-", 6))
2259         if (!cupsArrayFind(punchmedia, (void*)name))
2260           cupsArrayAdd(punchmedia, (void*)name);
2261     }
2262   }
2263   return punchmedia;
2264 }
2265 
2266 /*get_duplex - Function returns whether the printer support Duplex,
2267                DuplexTumble, DuplexNoTumble using attributes returned by the
2268                IPP Request*/
get_duplex(ipp_t * printer_attributes)2269 cups_array_t* get_duplex(ipp_t *printer_attributes)
2270 {
2271   ipp_attribute_t        *attr;
2272   int                    count, i;
2273   cups_array_t           *duplex_options;
2274   const char             *str;
2275 
2276   if ((duplex_options = cupsArrayNew3((cups_array_func_t)strcasecmp,
2277 				      NULL, NULL, 0,
2278 				      (cups_acopy_func_t)strdup,
2279 				      (cups_afree_func_t)free)) == NULL)
2280     return NULL;
2281   if ((attr = ippFindAttribute(printer_attributes, "sides-supported",
2282 			       IPP_TAG_KEYWORD)) != NULL) {
2283     count = ippGetCount(attr);
2284     for (i = 0; i < count; i ++) {
2285       str = ippGetString(attr, i, NULL);
2286       if (!strcmp(str, "one-sided"))
2287         cupsArrayAdd(duplex_options, "None");
2288       else if (!strcmp(str, "two-sided-long-edge"))
2289         cupsArrayAdd(duplex_options, "DuplexNoTumble");
2290       else if (!strcmp(str, "two-sided-short-edge"))
2291         cupsArrayAdd(duplex_options, "DuplexTumble");
2292     }
2293   }
2294   return duplex_options;
2295 }
2296 
2297 /* get_colormodel - Returns the colormodel supported by the printer*/
get_colormodel(ipp_t * printer_attributes)2298 cups_array_t* get_colormodel(ipp_t *printer_attributes)
2299 {
2300   ipp_attribute_t        *attr;
2301   int                    count, i;
2302   cups_array_t           *colormodel;
2303   const char             *keyword;
2304   int                    have_bi_level = 0,
2305                          have_mono = 0;
2306 
2307   if ((colormodel = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2308 				  (cups_acopy_func_t)strdup,
2309 				  (cups_afree_func_t)free)) == NULL)
2310     return NULL;
2311   if ((attr = ippFindAttribute(printer_attributes, "urf-supported",
2312 			       IPP_TAG_KEYWORD)) == NULL)
2313     if ((attr = ippFindAttribute(printer_attributes,
2314 				 "pwg-raster-document-type-supported",
2315 				 IPP_TAG_KEYWORD)) == NULL)
2316       if ((attr = ippFindAttribute(printer_attributes,
2317 				   "print-color-mode-supported",
2318 				   IPP_TAG_KEYWORD)) == NULL)
2319         attr = ippFindAttribute(printer_attributes, "output-mode-supported",
2320 				IPP_TAG_KEYWORD);
2321 
2322   if (attr && ippGetCount(attr) > 0) {
2323     for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2324       keyword = ippGetString(attr, i, NULL);
2325       if (!have_bi_level && (!strcasecmp(keyword, "black_1") ||
2326 			     !strcmp(keyword, "bi-level") ||
2327 			     !strcmp(keyword, "process-bi-level"))) {
2328 	cupsArrayAdd(colormodel,"FastGray");
2329 	have_bi_level = 1;
2330       } else if (!have_mono &&(!strcasecmp(keyword, "sgray_8") ||
2331 			       !strncmp(keyword, "W8", 2) ||
2332 			       !strcmp(keyword, "monochrome") ||
2333 			       !strcmp(keyword, "process-monochrome"))) {
2334         have_mono = 1;
2335         cupsArrayAdd(colormodel,"Gray");
2336       } else if (!strcasecmp(keyword, "sgray_16") ||
2337 		 !strncmp(keyword, "W8-16", 5) || !strncmp(keyword, "W16", 3))
2338         cupsArrayAdd(colormodel,"Gray16");
2339       else if (!strcasecmp(keyword, "srgb_8") ||
2340 	       !strncmp(keyword, "SRGB24", 6) || !strcmp(keyword, "color"))
2341         cupsArrayAdd(colormodel,"RGB");
2342       else if ((!strcasecmp(keyword, "srgb_16") ||
2343 		!strncmp(keyword, "SRGB48", 6)) &&
2344 	       !ippContainsString(attr, "srgb_8"))
2345         cupsArrayAdd(colormodel,"RGB");
2346       else if (!strcasecmp(keyword, "adobe-rgb_16") ||
2347 	       !strncmp(keyword, "ADOBERGB48", 10) ||
2348 	       !strncmp(keyword, "ADOBERGB24-48", 13))
2349         cupsArrayAdd(colormodel,"AdobeRGB");
2350       else if ((!strcasecmp(keyword, "adobe-rgb_8") ||
2351 		!strcmp(keyword, "ADOBERGB24")) &&
2352 	       !ippContainsString(attr, "adobe-rgb_16"))
2353         cupsArrayAdd(colormodel,"AdobeRGB");
2354       else if ((!strcasecmp(keyword, "black_8") &&
2355 		!ippContainsString(attr, "black_16")) ||
2356 	       !strcmp(keyword, "DEVW8"))
2357         cupsArrayAdd(colormodel,"DeviceGray");
2358       else if (!strcasecmp(keyword, "black_16") ||
2359 	       !strcmp(keyword, "DEVW16") || !strcmp(keyword, "DEVW8-16"))
2360         cupsArrayAdd(colormodel,"DeviceGray");
2361       else if ((!strcasecmp(keyword, "cmyk_8") &&
2362 		!ippContainsString(attr, "cmyk_16")) ||
2363 	       !strcmp(keyword, "DEVCMYK32"))
2364         cupsArrayAdd(colormodel,"CMYK");
2365       else if (!strcasecmp(keyword, "cmyk_16") ||
2366 	       !strcmp(keyword, "DEVCMYK32-64") ||
2367 	       !strcmp(keyword, "DEVCMYK64"))
2368         cupsArrayAdd(colormodel,"CMYK");
2369       else if ((!strcasecmp(keyword, "rgb_8") &&
2370 		!ippContainsString(attr, "rgb_16")) ||
2371 	       !strcmp(keyword, "DEVRGB24"))
2372         cupsArrayAdd(colormodel,"DeviceRGB");
2373       else if (!strcasecmp(keyword, "rgb_16") ||
2374 	       !strcmp(keyword, "DEVRGB24-48") ||
2375 	       !strcmp(keyword, "DEVRGB48"))
2376         cupsArrayAdd(colormodel,"DeviceRGB");
2377     }
2378   }
2379   return colormodel;
2380 }
2381 
2382 /* get_printquality - Returns the print qualities supported by the printer*/
get_printquality(ipp_t * printer_attributes)2383 cups_array_t* get_printquality(ipp_t *printer_attributes)
2384 {
2385   ipp_attribute_t       *quality;
2386   cups_array_t          *print_qualities;
2387 
2388   if ((print_qualities = cupsArrayNew3((cups_array_func_t)strcasecmp,
2389 				       NULL, NULL, 0,
2390 				       (cups_acopy_func_t)strdup,
2391 				       (cups_afree_func_t)free)) == NULL)
2392     return NULL;
2393   if ((quality=ippFindAttribute(printer_attributes, "print-quality-supported",
2394 				IPP_TAG_ENUM)) != NULL) {
2395     if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
2396       cupsArrayAdd(print_qualities, "3");
2397     if (ippContainsInteger(quality, IPP_QUALITY_HIGH))
2398       cupsArrayAdd(print_qualities, "5");
2399     cupsArrayAdd(print_qualities, "4");
2400   }
2401   return print_qualities;
2402 }
2403 
2404 /* get_job_data - Returns the job_sheets,multiple-document-handling supported
2405                   by the printer*/
get_job_data(ipp_t * printer_attributes,char * requested_attr)2406 cups_array_t* get_job_data(ipp_t *printer_attributes, char* requested_attr)
2407 {
2408   ipp_attribute_t       *attr;
2409   cups_array_t          *job_data;
2410   int                   i, count;
2411   const char*           str;
2412 
2413   if ((job_data = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2414 				(cups_acopy_func_t)strdup,
2415 				(cups_afree_func_t)free)) == NULL)
2416     return NULL;
2417   if ((attr = ippFindAttribute(printer_attributes, requested_attr,
2418 			       IPP_TAG_KEYWORD)) != NULL) {
2419     for(i = 0, count = ippGetCount(attr); i < count; i ++) {
2420       str = ippGetString(attr, i, NULL);
2421       if (!cupsArrayFind(job_data, (void *)str))
2422         cupsArrayAdd(job_data, (void*)str);
2423     }
2424   }
2425   return job_data;
2426 }
2427 
2428 /* get_finishingtemplate - Returns the Finishing Templates supported by the
2429                            printer*/
get_finishingtemplate(ipp_t * printer_attributes)2430 cups_array_t* get_finishingtemplate(ipp_t *printer_attributes)
2431 {
2432   ipp_attribute_t       *attr;
2433   cups_array_t          *finishing_templates;
2434   ipp_t                 *finishing_col; /* Current finishing collection */
2435   int                   count, i;
2436   const char            *keyword;
2437 
2438   if ((finishing_templates = cupsArrayNew3((cups_array_func_t)strcasecmp,
2439 					   NULL, NULL, 0,
2440 					   (cups_acopy_func_t)strdup,
2441 					   (cups_afree_func_t)free)) == NULL)
2442     return NULL;
2443   if ((attr = ippFindAttribute(printer_attributes, "finishings-col-database",
2444 			       IPP_TAG_BEGIN_COLLECTION)) != NULL) {
2445     count = ippGetCount(attr);
2446     for (i = 0; i < count; i ++) {
2447       finishing_col = ippGetCollection(attr, i);
2448       keyword       = ippGetString(ippFindAttribute(finishing_col,
2449 						    "finishing-template",
2450 						    IPP_TAG_ZERO), 0, NULL);
2451       if (!keyword || cupsArrayFind(finishing_templates, (void *)keyword))
2452         continue;
2453       if (strncmp(keyword, "fold-", 5) && (strstr(keyword, "-bottom") ||
2454 					   strstr(keyword, "-left") ||
2455 					   strstr(keyword, "-right") ||
2456 					   strstr(keyword, "-top")))
2457         continue;
2458       cupsArrayAdd(finishing_templates, (void*)keyword);
2459     }
2460   }
2461   return finishing_templates;
2462 }
2463 
2464 /* get_printing_data - Returns the print-content-optimize,print-rendering-intent
2465                        and print-scaling attributes for the printer*/
get_printing_data(ipp_t * printer_attributes,char * requested_attr)2466 cups_array_t* get_printing_data(ipp_t *printer_attributes,char* requested_attr)
2467 {
2468   ipp_attribute_t         *attr;
2469   int                     count, i;
2470   cups_array_t            *printing_support;
2471   const char              *keyword;
2472   char                    requested_option[40];
2473 
2474   if(!strcmp(requested_attr, "print-content-optimize"))
2475     strcpy(requested_option, "print-content-optimize-supported");
2476   else if (!strcmp(requested_attr, "print-rendering-intent"))
2477     strcpy(requested_option, "print-rendering-intent-supported");
2478   else if(!strcmp(requested_attr, "print-scaling"))
2479     strcpy(requested_option, "print-scaling-supported");
2480   else if (!strcmp(requested_attr, "job-sheets-supported"))
2481     strcpy(requested_option, "job-sheets-supported");
2482   else
2483     return NULL;
2484 
2485   if ((printing_support = cupsArrayNew3((cups_array_func_t)strcasecmp,
2486 					NULL, NULL, 0,
2487 					(cups_acopy_func_t)strdup,
2488 					(cups_afree_func_t)free)) == NULL)
2489     return NULL;
2490   if ((attr = ippFindAttribute(printer_attributes, requested_option,
2491 			       IPP_TAG_ZERO)) != NULL &&
2492       (count = ippGetCount(attr)) > 1) {
2493     for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2494       keyword = ippGetString(attr, i, NULL);
2495       cupsArrayAdd(printing_support, (void *)keyword);
2496     }
2497   }
2498   return printing_support;
2499 }
2500 
2501 /*get_presets - Returns a list of presets name supported by the printer*/
get_presets(ipp_t * printer_attributes)2502 cups_array_t* get_presets(ipp_t *printer_attributes)
2503 {
2504   ipp_attribute_t         *attr;
2505   int                     count, i;
2506   cups_array_t            *presets;
2507   ipp_t                   *preset;
2508   const char              *preset_name;
2509 
2510   if ((presets = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2511 			       (cups_acopy_func_t)strdup,
2512 			       (cups_afree_func_t)free)) == NULL)
2513     return NULL;
2514   if ((attr = ippFindAttribute(printer_attributes, "job-presets-supported",
2515 			       IPP_TAG_BEGIN_COLLECTION)) != NULL &&
2516       (count = ippGetCount(attr)) > 1) {
2517     for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2518       preset = ippGetCollection(attr, i);
2519       preset_name = ippGetString(ippFindAttribute(preset, "preset-name",
2520 						  IPP_TAG_ZERO), 0, NULL);
2521       if(!cupsArrayFind(presets, (void*)preset_name))
2522         cupsArrayAdd(presets, (void *)preset_name);
2523     }
2524   }
2525   return presets;
2526 }
2527 
2528 /* get_booklet - Returns True if booklet is supported */
get_booklet(ipp_t * printer_attributes)2529 cups_array_t* get_booklet(ipp_t *printer_attributes)
2530 {
2531   ipp_attribute_t       *attr;
2532   cups_array_t          *booklet;
2533 
2534   if ((booklet = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2535 			       (cups_acopy_func_t)strdup,
2536 			       (cups_afree_func_t)free)) == NULL)
2537     return NULL;
2538   if ((attr = ippFindAttribute(printer_attributes, "finishings-supported",
2539 			       IPP_TAG_ENUM)) != NULL) {
2540     if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER)) {
2541       /* Assuming that the printer which supports Booklet also supports
2542          printing without Booklet, so for this printer we will return
2543          both "True" and "False" */
2544       cupsArrayAdd(booklet, "True");
2545     }
2546   }
2547   cupsArrayAdd(booklet, "False");
2548   return booklet;
2549 }
2550 
2551 /* get_supported_options - Function returns various attributes supported by the
2552                            printer, such as PageSize,ColorModel etc.*/
get_supported_options(ipp_t * printer_attributes,char * option)2553 cups_array_t* get_supported_options(ipp_t *printer_attributes, char* option)
2554 {
2555   if (!strcmp(option, "PageSize") || !strcmp(option, "PageRegion"))
2556     return get_pagesize(printer_attributes);
2557   else if (!strcmp(option, "MediaType") || !strcmp(option, "InputSlot") ||
2558 	   !strcmp(option, "OutputBin"))
2559     return get_mediadata(printer_attributes, option);
2560   else if (!strcmp(option, "StapleLocation"))
2561     return get_staplelocation(printer_attributes);
2562   else if (!strcmp(option, "FoldType"))
2563     return get_foldtype(printer_attributes);
2564   else if (!strcmp(option, "PunchMedia"))
2565     return get_punchmedia(printer_attributes);
2566   else if (!strcmp(option, "cupsFinishingTemplate"))
2567     return get_finishingtemplate(printer_attributes);
2568   else if (!strcmp(option, "cupsPrintQuality"))
2569     return get_printquality(printer_attributes);
2570   else if (!strcmp(option, "job-sheets-supported") ||
2571 	   !strcmp(option, "print-content-optimize") ||
2572 	   !strcmp(option, "print-rendering-intent") ||
2573 	   !strcmp(option, "print-scaling"))
2574     return get_printing_data(printer_attributes, option);
2575   else if (!strcmp(option, "APPrinterPreset"))
2576     return get_presets(printer_attributes);
2577   else if(!strcmp(option, "Booklet"))
2578     return get_booklet(printer_attributes);
2579   else if(!strcmp(option, "ColorModel"))
2580     return get_colormodel(printer_attributes);
2581   else if (!strcmp(option, "Duplex"))
2582     return get_duplex(printer_attributes);
2583   else if (!strcmp(option, "multiple-document-handling-supported") ||
2584 	   !strcmp(option, "cover-back-supported") ||
2585 	   !strcmp(option, "cover-front-supported") ||
2586 	   !strcmp(option, "cover-type-supported") ||
2587 	   !strcmp(option, "media-type-supported"))
2588     return get_job_data(printer_attributes, option);
2589   else if (!strcmp(option,"finishings-supported"))
2590     return get_finishings(printer_attributes);
2591   return NULL;
2592 }
2593 
2594 /*check_printer_with_options - Checks whether a printer in an cluster supports
2595                                option1 for keyword at value idx_option1 in
2596                                ppd_keywords[] and option2 for keyword at value
2597                                idx_option2*/
check_printer_with_options(char * cluster_name,int idx_option1,char * option1,int idx_option2,char * option2)2598 int check_printer_with_options(char* cluster_name, int idx_option1,
2599 			       char* option1, int idx_option2, char* option2)
2600 {
2601   remote_printer_t     *p;
2602   cups_array_t         *first_attributes_value;
2603   cups_array_t         *second_attributes_value;
2604   char                 *borderless_pagesize = NULL;
2605   int                  option1_is_size = 0, option2_is_size = 0;
2606   unsigned long int    max_length = 0, option1_len = 0, option2_len = 0, t_len = 0;
2607   char  t[] = ".Borderless";
2608 
2609   t_len = strlen(t);
2610   if (option1)
2611     option1_len = strlen(option1);
2612   if (option2)
2613     option2_len = strlen(option2);
2614 
2615   /* Seems to be possible to have both options...*/
2616   max_length = option1_len + option2_len + (2 * t_len) + 1;
2617 
2618   borderless_pagesize = (char *)malloc(sizeof(char) * max_length);
2619   if (borderless_pagesize == NULL)
2620   {
2621     debug_printf("check_printer_with_options: Run out of memory.\n");
2622     return 0;
2623   }
2624   memset(borderless_pagesize, 0, max_length);
2625 
2626   if (!strcmp(ppd_keywords[idx_option1], "PageSize") ||
2627       !strcmp(ppd_keywords[idx_option1], "PageRegion")) {
2628     /* Check that we are generating .Borderless for the correct size, i.e We
2629        are generating 4x5.Borderless for 4x5 and not generating
2630        4x5.Borderless.Borderless for 4x5.Borderless */
2631     if (option1_len >= 11 &&
2632 	!strcmp(&option1[option1_len - t_len], t))
2633       ;
2634     else {
2635       strcat(borderless_pagesize, option1);
2636       strcat(borderless_pagesize, t);
2637       option1_is_size = 1;
2638     }
2639   }
2640   if (!strcmp(ppd_keywords[idx_option2], "PageSize") ||
2641       !strcmp(ppd_keywords[idx_option2], "PageRegion")) {
2642     if(option2_len >=11 &&
2643        !strcmp(&option2[option2_len - t_len], t))
2644       ;
2645     else {
2646       strcat(borderless_pagesize, option2);
2647       strcat(borderless_pagesize, t);
2648       option2_is_size = 1;
2649     }
2650   }
2651   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2652        p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2653     if(strcmp(cluster_name, p->queue_name))
2654       continue;
2655     first_attributes_value = get_supported_options(p->prattrs,
2656 						   ppd_keywords[idx_option1]);
2657     if(cupsArrayFind(first_attributes_value, (void*)option1) ||
2658        (option1_is_size && cupsArrayFind(first_attributes_value,
2659 					 (void*)borderless_pagesize))) {
2660       second_attributes_value =
2661 	get_supported_options(p->prattrs,
2662 			      ppd_keywords[idx_option2]);
2663       if (cupsArrayFind(second_attributes_value,(void*)option2) ||
2664 	  (option2_is_size && cupsArrayFind(second_attributes_value,
2665 					    (void*)borderless_pagesize)))
2666       {
2667         free(borderless_pagesize);
2668         return 1;
2669       }
2670     }
2671   }
2672   free(borderless_pagesize);
2673   return 0;
2674 }
2675 
2676 /* The function returns a array containint the sizes supported by the cluster*/
get_cluster_sizes(char * cluster_name)2677 cups_array_t* get_cluster_sizes(char* cluster_name)
2678 {
2679   cups_array_t         *sizes = NULL;
2680   cups_array_t         *cluster_sizes = NULL,
2681                        *sizes_ppdname;
2682   cups_size_t          *size;
2683   remote_printer_t     *p;
2684   ipp_attribute_t      *defattr;
2685   char                 ppdname[41], pagesize[128];
2686   char*                first_space;
2687   int                  min_length, min_width, max_length, max_width,
2688                        bottom, left, right, top;
2689 
2690   cluster_sizes = cupsArrayNew3((cups_array_func_t)pwg_compare_sizes,
2691 				NULL, NULL, 0,
2692 				(cups_acopy_func_t)pwg_copy_size,
2693 				(cups_afree_func_t)free);
2694   sizes_ppdname = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2695 				(cups_acopy_func_t)strdup,
2696 				(cups_afree_func_t)free);
2697   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2698        p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2699     if (!strcmp(p->queue_name, cluster_name)) {
2700       if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
2701 	 p->status == STATUS_TO_BE_RELEASED )
2702 	continue;
2703       defattr = NULL;
2704       min_length = INT_MAX;
2705       min_width = INT_MAX;
2706       max_length = 0;
2707       max_width = 0;
2708       bottom = 0;
2709       left = 0;
2710       right = 0;
2711       top = 0;
2712       sizes = generate_sizes(p->prattrs, &defattr, &min_length, &min_width,
2713 			     &max_length, &max_width,
2714 			     &bottom, &left, &right, &top, ppdname);
2715       for (size = (cups_size_t *)cupsArrayFirst(sizes);
2716 	   size; size = (cups_size_t *)cupsArrayNext(sizes)) {
2717 	if (!cupsArrayFind(cluster_sizes, size)) {
2718 	  strcpy(pagesize, size->media);
2719 	  if ((first_space = strchr(pagesize, ' ')) != NULL) {
2720 	    *first_space = '\0';
2721 	  }
2722 	  if (!cupsArrayFind(sizes_ppdname, pagesize)) {
2723 	    cupsArrayAdd(cluster_sizes, size);
2724 	    cupsArrayAdd(sizes_ppdname, pagesize);
2725 	  }
2726         }
2727       }
2728 
2729       cupsArrayDelete(sizes);
2730       sizes = NULL;
2731     }
2732   }
2733 
2734   cupsArrayDelete(sizes_ppdname);
2735 
2736   return cluster_sizes;
2737 }
2738 
2739 /* generate_cluster_conflicts - Function generates conflicts for the cluster*/
generate_cluster_conflicts(char * cluster_name,ipp_t * merged_attributes)2740 cups_array_t* generate_cluster_conflicts(char* cluster_name,
2741 					 ipp_t *merged_attributes)
2742 {
2743   remote_printer_t     *p;
2744   cups_array_t         *conflict_pairs = NULL;
2745   int                  i, k, j, no_of_printers = 0, no_of_ppd_keywords;
2746   cups_array_t         *printer_first_options = NULL,
2747                        *printer_second_options = NULL;
2748   char                 *opt1, *opt2, constraint[100], *ppdsizename, *temp;
2749   cups_array_t         *sizes = NULL, *pagesizes;
2750   cups_size_t          *size;
2751 
2752   /* Cups Array to store the conflicts*/
2753   ppdsizename = (char *)malloc(sizeof(char) * 128);
2754   if ((conflict_pairs = cupsArrayNew3((cups_array_func_t)strcasecmp,
2755 				      NULL, NULL, 0,
2756 				      (cups_acopy_func_t)strdup,
2757 				      (cups_afree_func_t)free)) == NULL)
2758     return NULL;
2759 
2760   /* Storing all the values supported by the cluster in cluster_options*/
2761   no_of_ppd_keywords = sizeof(ppd_keywords) / sizeof(ppd_keywords[0]);
2762   cups_array_t         *cluster_options[no_of_ppd_keywords];
2763   for(i = 0; i < no_of_ppd_keywords; i ++) {
2764     if (strcmp(ppd_keywords[i], "PageSize") &&
2765 	strcmp(ppd_keywords[i], "PageRegion"))
2766       cluster_options[i] =
2767 	get_supported_options(merged_attributes,ppd_keywords[i]);
2768     else {
2769       sizes = get_cluster_sizes(cluster_name);
2770       if ((pagesizes =
2771 	   cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2772 			 (cups_acopy_func_t)strdup,
2773 			 (cups_afree_func_t)free)) == NULL)
2774 	return NULL;
2775       for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
2776 	   size = (cups_size_t *)cupsArrayNext(sizes)) {
2777         strcpy(ppdsizename, size->media);
2778         if ((temp = strchr(ppdsizename, ' ')) != NULL)
2779           *temp = '\0';
2780         cupsArrayAdd(pagesizes, ppdsizename);
2781       }
2782       cluster_options[i] = pagesizes;
2783 
2784       cupsArrayDelete(sizes);
2785       sizes = NULL;
2786     }
2787   }
2788 
2789   /* Algorithm to find constraints: We iterate over printer, if we
2790      find a value for a keyword which is supported by the cluster but
2791      not by the printer, that value can be part of the conflict. With
2792      this value v and a new value (for an different keyword, at index
2793      more than the index of first keyword), we generate a pair (v,u)
2794      and then we check whether some printer satisfy this pair, if no
2795      such printer exists then the pair is a conflict, we add it to
2796      conflict_pairs array */
2797 
2798   no_of_printers = cupsArrayCount(remote_printers);
2799   for (j = 0; j < no_of_printers; j ++) {
2800     p = (remote_printer_t *)cupsArrayIndex(remote_printers, j);
2801     if (strcmp(cluster_name, p->queue_name))
2802       continue;
2803     if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
2804        p->status == STATUS_TO_BE_RELEASED )
2805       continue;
2806     for (i = 0; i < no_of_ppd_keywords; i ++) {
2807       printer_first_options =
2808 	get_supported_options(p->prattrs, ppd_keywords[i]);
2809       if (i == 0)
2810 	for (opt1 = cupsArrayFirst(cluster_options[i]); opt1;
2811 	     opt1 = cupsArrayNext(cluster_options[i])) {
2812 	  if (cupsArrayFind(printer_first_options, opt1))
2813 	    continue;
2814 	  for (k = i + 1; k < no_of_ppd_keywords; k++) {
2815 	    if (!strcmp(ppd_keywords[i], "PageSize") &&
2816 		!strcmp(ppd_keywords[k], "PageRegion"))
2817 	      continue;
2818 	    printer_second_options = get_supported_options(p->prattrs,
2819 							   ppd_keywords[k]);
2820 	    for(opt2 = cupsArrayFirst(printer_second_options); opt2;
2821 		opt2 = cupsArrayNext(printer_second_options)) {
2822 	      if (check_printer_with_options(cluster_name, i, opt1, k, opt2))
2823 		continue;
2824 	      if (!strcasecmp(opt1, AUTO_OPTION) ||
2825 		  !strcasecmp(opt2, AUTO_OPTION))
2826 		continue;
2827 	      if (!strcmp(opt1, "Gray") || !strcmp(opt2, "Gray"))
2828 		continue;
2829 	      sprintf(constraint, "*UIConstraints: *%s %s *%s %s\n",
2830 		      ppd_keywords[i],
2831 		      opt1,ppd_keywords[k], opt2);
2832 	      if (!cupsArrayFind(conflict_pairs, constraint)) {
2833 		cupsArrayAdd(conflict_pairs, constraint);
2834 	      }
2835 	      sprintf(constraint, "*UIConstraints: *%s %s *%s %s\n",
2836 		      ppd_keywords[k],
2837 		      opt2, ppd_keywords[i], opt1);
2838 	      if (!cupsArrayFind(conflict_pairs, constraint)) {
2839 		cupsArrayAdd(conflict_pairs, constraint);
2840 	      }
2841 	    }
2842 
2843 	    cupsArrayDelete(printer_second_options);
2844 	    printer_second_options = NULL;
2845 	  }
2846 	}
2847 
2848       cupsArrayDelete(printer_first_options);
2849       printer_first_options = NULL;
2850     }
2851   }
2852 
2853   for(i = 0; i < no_of_ppd_keywords; i ++) {
2854     cupsArrayDelete(cluster_options[i]);
2855   }
2856 
2857   free(ppdsizename);
2858   return conflict_pairs;
2859 }
2860 
2861 /*get_cluster_attributes - Returns ipp_t* containing the options supplied by
2862                            all the printers in the cluster, which can be sent
2863                            to ppdCreateFromIPP2() to generate the ppd file */
get_cluster_attributes(char * cluster_name)2864 ipp_t* get_cluster_attributes(char* cluster_name)
2865 {
2866   remote_printer_t     *p;
2867   ipp_t                *merged_attributes = NULL;
2868   char                 printer_make_and_model[256];
2869   ipp_attribute_t      *attr;
2870   int                  color_supported = 0, make_model_done = 0, i;
2871   char                 valuebuffer[65536];
2872   merged_attributes = ippNew();
2873   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2874        p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2875     if (strcmp(cluster_name, p->queue_name))
2876       continue;
2877     if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
2878        p->status == STATUS_TO_BE_RELEASED )
2879       continue;
2880     if (!make_model_done) {
2881       strcpy(printer_make_and_model, "Cluster ");
2882       strcat(printer_make_and_model, cluster_name);
2883       make_model_done = 1;
2884     }
2885     if (((attr = ippFindAttribute(p->prattrs, "color-supported",
2886 				  IPP_TAG_BOOLEAN)) != NULL &&
2887 	 ippGetBoolean(attr, 0)))
2888       color_supported = 1;
2889   }
2890 
2891   ippAddString(merged_attributes, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2892 	       "printer-make-and-model",
2893                NULL, printer_make_and_model);
2894   ippAddBoolean(merged_attributes, IPP_TAG_PRINTER, "color-supported",
2895                 color_supported);
2896 
2897   add_keyword_attributes(cluster_name, &merged_attributes);
2898   add_mimetype_attributes(cluster_name, &merged_attributes);
2899   add_tagzero_attributes(cluster_name, &merged_attributes);
2900   add_enum_attributes(cluster_name, &merged_attributes);
2901   add_resolution_attributes(cluster_name, &merged_attributes);
2902   add_margin_attributes(cluster_name, &merged_attributes);
2903   add_mediasize_attributes(cluster_name, &merged_attributes);
2904   add_mediadatabase_attributes(cluster_name, &merged_attributes);
2905   add_jobpresets_attribute(cluster_name, &merged_attributes);
2906   attr = ippFirstAttribute(merged_attributes);
2907   /* Printing merged attributes*/
2908   debug_printf("Merged attributes for the cluster %s : \n", cluster_name);
2909   while (attr) {
2910     debug_printf("  Attr: %s\n",
2911 		 ippGetName(attr));
2912     ippAttributeString(attr, valuebuffer, sizeof(valuebuffer));
2913     debug_printf("  Value: %s\n", valuebuffer);
2914     const char *kw;
2915     for (i = 0; i < ippGetCount(attr); i ++)
2916       if ((kw = ippGetString(attr, i, NULL)) != NULL)
2917 	debug_printf("  Keyword: %s\n", kw);
2918     attr = ippNextAttribute(merged_attributes);
2919   }
2920   return merged_attributes;
2921 }
2922 
cluster_supports_given_attribute(char * cluster_name,ipp_tag_t tag,const char * attribute)2923 int cluster_supports_given_attribute(char* cluster_name, ipp_tag_t tag,
2924 				     const char* attribute)
2925 {
2926   remote_printer_t        *p;
2927   ipp_attribute_t         *attr;
2928   int                     count;
2929 
2930   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2931        p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2932     if (strcmp(cluster_name, p->queue_name))
2933       continue;
2934     if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
2935        p->status == STATUS_TO_BE_RELEASED )
2936       continue;
2937     if ((attr = ippFindAttribute(p->prattrs, attribute, tag)) != NULL &&
2938         (count = ippGetCount(attr)) > 1)
2939       return 1;
2940   }
2941   return 0;
2942 }
2943 
2944 /* Generating the default values for the cluster*/
get_cluster_default_attributes(ipp_t ** merged_attributes,char * cluster_name,char * default_pagesize,const char ** default_color)2945 void get_cluster_default_attributes(ipp_t** merged_attributes,
2946                                     char* cluster_name,
2947                                     char* default_pagesize,
2948                                     const char **default_color)
2949 {
2950   int                     max_pages_per_min = 0, pages_per_min;
2951   remote_printer_t        *p, *def_printer = NULL;
2952   int                     i, count;
2953   ipp_attribute_t         *attr, *media_attr, *media_col_default, *defattr;
2954   ipp_t                   *media_col,
2955                           *media_size, *current_media=NULL;
2956   char                    media_source[32], media_type[32];
2957   const char              *str;
2958   media_col_t             *temp;
2959   const char              *keyword;
2960   res_t                   *res;
2961   int                     xres, yres;
2962   int                     min_length = INT_MAX, min_width = INT_MAX,
2963                           max_length = 0, max_width = 0,
2964                           bottom, left, right, top;
2965   char                    ppdname[41];
2966   cups_array_t            *sizes;
2967 
2968   /*The printer with the maximum Throughtput(pages_per_min) is selected as
2969     the default printer*/
2970   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2971        p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2972     if (strcmp(p->queue_name, cluster_name))
2973       continue;
2974     if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
2975        p->status == STATUS_TO_BE_RELEASED )
2976       continue;
2977     if ((attr = ippFindAttribute (p->prattrs, "pages-per-minute",
2978 				  IPP_TAG_INTEGER)) != NULL) {
2979       pages_per_min = ippGetInteger (attr, 0);
2980       if (pages_per_min > max_pages_per_min) {
2981         max_pages_per_min = pages_per_min;
2982         def_printer = p;
2983       }
2984     }
2985   }
2986 
2987   /* If none of the printer in the cluster has "pages-per-minute" in the ipp
2988      response message, then select the first printer in the cluster */
2989   if (!def_printer) {
2990     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2991 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2992       if (strcmp(p->queue_name, cluster_name))
2993         continue;
2994       else {
2995         def_printer = p;
2996         break;
2997       }
2998     }
2999   }
3000 
3001   debug_printf("Selecting printer (%s) as the default for the cluster %s\n",
3002 	       def_printer->uri, cluster_name);
3003   debug_printf("Default Attributes of the cluster %s are : \n", cluster_name);
3004 
3005   /* Generating the default pagesize for the cluster*/
3006   sizes = generate_sizes(def_printer->prattrs, &defattr, &min_length,
3007 			 &min_width, &max_length, &max_width,
3008                          &bottom, &left, &right, &top, ppdname);
3009   strcpy(default_pagesize, ppdname);
3010   debug_printf("Default PageSize : %s\n", default_pagesize);
3011 
3012   /* Generating the default media-col for the cluster*/
3013   if ((attr = ippFindAttribute(def_printer->prattrs, "media-col-default",
3014 			       IPP_TAG_BEGIN_COLLECTION)) != NULL) {
3015     media_col = ippGetCollection(attr, 0);
3016     media_size = ippGetCollection(ippFindAttribute(media_col, "media-size",
3017 						   IPP_TAG_BEGIN_COLLECTION),
3018 				  0);
3019     temp = (media_col_t *)malloc(sizeof(media_col_t));
3020     temp->x = ippGetInteger(ippFindAttribute(media_size, "x-dimension",
3021 					     IPP_TAG_ZERO), 0);
3022     temp->y = ippGetInteger(ippFindAttribute(media_size, "y-dimension",
3023 					     IPP_TAG_ZERO), 0);
3024     temp->top_margin = ippGetInteger(ippFindAttribute(media_col,
3025 						      "media-top-margin",
3026 						      IPP_TAG_INTEGER), 0);
3027     temp->bottom_margin = ippGetInteger(ippFindAttribute(media_col,
3028 							 "media-bottom-margin",
3029 							 IPP_TAG_INTEGER), 0);
3030     temp->left_margin = ippGetInteger(ippFindAttribute(media_col,
3031 						       "media-left-margin",
3032 						       IPP_TAG_INTEGER), 0);
3033     temp->right_margin = ippGetInteger(ippFindAttribute(media_col,
3034 							"media-right-margin",
3035 							IPP_TAG_INTEGER), 0);
3036     media_type[0] = '\0';
3037     media_source[0] = '\0';
3038     temp->media_source = NULL;
3039     temp->media_type = NULL;
3040 
3041     if ((media_attr = ippFindAttribute(media_col, "media-type",
3042 				       IPP_TAG_KEYWORD)) != NULL)
3043       pwg_ppdize_name(ippGetString(media_attr, 0, NULL), media_type,
3044 		      sizeof(media_type));
3045 
3046     if (strlen(media_type) > 1) {
3047       temp->media_type = (char*)malloc(sizeof(char)*32);
3048       strcpy(temp->media_type, media_type);
3049       debug_printf("Default MediaType: %s\n", media_type);
3050     }
3051 
3052     if (temp->media_type == NULL) {
3053       if (cluster_supports_given_attribute(cluster_name, IPP_TAG_KEYWORD,
3054 					   "media-type-supported")) {
3055         temp->media_type = (char*)malloc(sizeof(char)*32);
3056         strcpy(temp->media_type, AUTO_OPTION);
3057         debug_printf("Default MediaType: " AUTO_OPTION "\n");
3058       }
3059     }
3060 
3061     if ((media_attr = ippFindAttribute(media_col, "media-source",
3062 				       IPP_TAG_KEYWORD)) != NULL) {
3063       pwg_ppdize_name(ippGetString(media_attr, 0, NULL), media_source,
3064 		      sizeof(media_source));
3065     }
3066 
3067     if (strlen(media_source) > 1) {
3068       temp->media_source = (char*)malloc(sizeof(char)*32);
3069       strcpy(temp->media_source, media_source);
3070       debug_printf("Default MediaSource: %s\n", media_source);
3071     }
3072 
3073     if (temp->media_source == NULL) {
3074       if (cluster_supports_given_attribute(cluster_name, IPP_TAG_KEYWORD,
3075 					   "media-source-supported")) {
3076         temp->media_source = (char*)malloc(sizeof(char) * 32);
3077         strcpy(temp->media_source, AUTO_OPTION);
3078         debug_printf("Default MediaSource: " AUTO_OPTION "\n");
3079       }
3080     }
3081 
3082     media_col_default = ippAddCollection(*merged_attributes, IPP_TAG_PRINTER,
3083 					 "media-col-default", NULL);
3084     current_media = create_media_col(temp->x, temp->y, temp->left_margin,
3085 				     temp->right_margin, temp->top_margin,
3086 				     temp->bottom_margin,
3087 				     temp->media_source, temp->media_type);
3088     ippSetCollection(*merged_attributes, &media_col_default, 0, current_media);
3089 
3090     free(temp->media_source);
3091     free(temp->media_type);
3092     free(temp);
3093     ippDelete(current_media);
3094   }
3095 
3096   /*Finding the default colormodel for the cluster*/
3097   if ((attr = ippFindAttribute(def_printer->prattrs, "urf-supported",
3098 			       IPP_TAG_KEYWORD)) == NULL)
3099     if ((attr = ippFindAttribute(def_printer->prattrs,
3100 				 "pwg-raster-document-type-supported",
3101 				 IPP_TAG_KEYWORD)) == NULL)
3102       if ((attr = ippFindAttribute(def_printer->prattrs,
3103 				   "print-color-mode-supported",
3104 				   IPP_TAG_KEYWORD)) == NULL)
3105 	attr = ippFindAttribute(def_printer->prattrs, "output-mode-supported",
3106 				IPP_TAG_KEYWORD);
3107 
3108   if (attr && ippGetCount(attr) > 0) {
3109     *default_color = NULL;
3110     for (i = 0, count = ippGetCount(attr); i < count; i ++) {
3111       keyword = ippGetString(attr, i, NULL);
3112       if ((!strcasecmp(keyword, "black_1") ||
3113 	   !strcmp(keyword, "bi-level") ||
3114 	   !strcmp(keyword, "process-bi-level"))) {
3115         if (!*default_color)
3116           *default_color = "FastGray";
3117       } else if ((!strcasecmp(keyword, "sgray_8") ||
3118 		  !strncmp(keyword, "W8", 2) ||
3119 		  !strcmp(keyword, "monochrome") ||
3120 		  !strcmp(keyword, "process-monochrome"))) {
3121         if (!*default_color || !strcmp(*default_color, "FastGray"))
3122           *default_color = "Gray";
3123       } else if (!strcasecmp(keyword, "sgray_16") ||
3124 		 !strncmp(keyword, "W8-16", 5) ||
3125 		 !strncmp(keyword, "W16", 3)) {
3126         if (!*default_color || !strcmp(*default_color, "FastGray"))
3127           *default_color = "Gray16";
3128       } else if (!strcasecmp(keyword, "srgb_8") ||
3129 		 !strncmp(keyword, "SRGB24", 6) ||
3130 		 !strcmp(keyword, "color")) {
3131         *default_color = "RGB";
3132       } else if ((!strcasecmp(keyword, "srgb_16") ||
3133 		  !strncmp(keyword, "SRGB48", 6)) &&
3134 		 !ippContainsString(attr, "srgb_8")) {
3135         *default_color = "RGB";
3136       } else if (!strcasecmp(keyword, "adobe-rgb_16") ||
3137 		 !strncmp(keyword, "ADOBERGB48", 10) ||
3138 		 !strncmp(keyword, "ADOBERGB24-48", 13)) {
3139         if (!*default_color)
3140           *default_color = "AdobeRGB";
3141       } else if ((!strcasecmp(keyword, "adobe-rgb_8") ||
3142 		  !strcmp(keyword, "ADOBERGB24")) &&
3143 		 !ippContainsString(attr, "adobe-rgb_16")) {
3144         if (!*default_color)
3145           *default_color = "AdobeRGB";
3146       }
3147     }
3148     if (*default_color)
3149       debug_printf("Default ColorModel : %s\n", *default_color);
3150   }
3151 
3152   if ((attr = ippFindAttribute(def_printer->prattrs, "output-bin-default",
3153 			       IPP_TAG_ZERO)) != NULL) {
3154     str = ippGetString(attr, 0, NULL);
3155     ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3156 		 "output-bin-default", NULL, str);
3157     debug_printf("Default OutputBin: %s\n", str);
3158   } else {
3159     if (cluster_supports_given_attribute(cluster_name,IPP_TAG_ZERO,
3160 					 "output-bin-supported")) {
3161       ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3162 		   "output-bin-default", NULL, AUTO_OPTION);
3163       debug_printf("Default OutputBin: %s\n", AUTO_OPTION);
3164     }
3165   }
3166 
3167   if ((attr = ippFindAttribute(def_printer->prattrs,
3168 			       "print-content-optimize-default",
3169 			       IPP_TAG_ZERO)) != NULL) {
3170     str = ippGetString(attr, 0, NULL);
3171     ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3172 		 "print-content-optimize-default", NULL, str);
3173     debug_printf("Default print-content-optimize: %s\n", str);
3174   } else {
3175     if (cluster_supports_given_attribute(cluster_name, IPP_TAG_ZERO,
3176 					 "print-content-optimize-default")) {
3177       ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3178 		   "print-content-optimize-default", NULL, AUTO_OPTION);
3179       debug_printf("Default print-content-optimize: %s\n", AUTO_OPTION);
3180     }
3181   }
3182 
3183   if ((attr = ippFindAttribute(def_printer->prattrs,
3184 			       "print-rendering-intent-default",
3185 			       IPP_TAG_ZERO)) != NULL) {
3186     str = ippGetString(attr, 0, NULL);
3187     ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3188 		 "print-rendering-intent-default", NULL, str);
3189     debug_printf("Default print-rendering-intent: %s\n", str);
3190   } else {
3191     if (cluster_supports_given_attribute(cluster_name, IPP_TAG_ZERO,
3192 					 "print-rendering-intent-default")) {
3193       ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3194 		   "print-rendering-intent-default", NULL, AUTO_OPTION);
3195       debug_printf("Default print-rendering-intent: %s\n", AUTO_OPTION);
3196     }
3197   }
3198 
3199   if ((attr = ippFindAttribute(def_printer->prattrs, "print-scaling-default",
3200 			       IPP_TAG_ZERO)) != NULL) {
3201     str = ippGetString(attr, 0, NULL);
3202     ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3203 		 "print-scaling-default", NULL, str);
3204     debug_printf("Default print-scaling: %s\n",str);
3205   } else {
3206     if (cluster_supports_given_attribute(cluster_name, IPP_TAG_ZERO,
3207 					 "print-scaling-default")) {
3208       ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3209 		   "print-scaling-default", NULL, AUTO_OPTION);
3210       debug_printf("Default print-scaling: %s\n", AUTO_OPTION);
3211     }
3212   }
3213 
3214   if ((attr = ippFindAttribute(def_printer->prattrs,
3215 			       "printer-resolution-default",
3216 			       IPP_TAG_ZERO)) != NULL) {
3217     if ((res = ippResolutionToRes(attr, 0)) != NULL) {
3218       xres = res->x;
3219       yres = res->y;
3220       ippAddResolution(*merged_attributes, IPP_TAG_PRINTER,
3221 		       "printer-resolution-default",
3222 		       IPP_RES_PER_INCH, xres, yres);
3223       debug_printf("Default Resolution : %dx%d\n", xres, yres);
3224       free_resolution(res, NULL);
3225     }
3226   }
3227 
3228   cupsArrayDelete(sizes);
3229 }
3230 
3231 /* Function to see which printer in the cluster supports the
3232    requested job attributes*/
supports_job_attributes_requested(const gchar * printer,int printer_index,int job_id,int * print_quality)3233 int supports_job_attributes_requested(const gchar* printer, int printer_index,
3234                                       int job_id, int *print_quality)
3235 {
3236   char                  uri[1024];
3237   http_t                *http = NULL;
3238   ipp_attribute_t       *attr, *attr1;
3239   ipp_t                 *request, *response = NULL;
3240   const char            *str, *side, *resource;
3241   cups_array_t          *job_sheet_supported = NULL,
3242                         *multiple_doc_supported = NULL, *print_qualities = NULL,
3243                         *media_type_supported = NULL, *staplelocation_supported = NULL,
3244                         *foldtype_supported = NULL, *punchmedia_supported = NULL,
3245                         *color_supported = NULL;
3246   remote_printer_t      *p;
3247   int                   i, count, side_found, orien_req, orien,
3248                         orien_found;
3249   cups_array_t          *sizes = NULL;
3250   int                   ret = 1;
3251 
3252   p = (remote_printer_t *)cupsArrayIndex(remote_printers, printer_index);
3253   static const char * const jattrs[] =  /* Job attributes we want */
3254   {
3255     "all"
3256   };
3257 
3258   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3259 		   "localhost", 0, "/printers/%s", printer);
3260 
3261   /* Getting the resource */
3262   resource = uri + (strlen(uri) - strlen(printer) - 10);
3263 
3264   http = http_connect_local();
3265   request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES);
3266   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
3267 	       uri);
3268   ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
3269   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
3270 	       "requesting-user-name", NULL, cupsUser());
3271   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3272 		"requested-attributes",
3273 		(int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
3274 
3275   response = cupsDoRequest(http, request,resource);
3276   attr = ippFirstAttribute(response);
3277 
3278   /* Document Format */
3279   /*
3280   if ((attr = ippFindAttribute(response, "document-format-detected",
3281 			       IPP_TAG_MIMETYPE)) != NULL &&
3282       ippGetCount(attr) > 0) {
3283     str = ippGetString(attr, 0, NULL);
3284     debug_printf("The job-document is of the format %s\n.",str);
3285     formats_supported = get_mimetype_attributes(p->prattrs);
3286     if(!cupsArrayFind(formats_supported, (void *)str)){
3287       debug_printf("Printer %s doesn't support the document format %s\n",
3288 		   printer, str);
3289       return 0;
3290     }
3291   }
3292   */
3293 
3294   /* Job Sheets */
3295   if ((attr = ippFindAttribute(response, "job-sheets",
3296 			       IPP_TAG_ZERO)) != NULL &&
3297       ippGetCount(attr) > 0) {
3298     str = ippGetString(attr, 0, NULL);
3299     debug_printf("The job-sheets %s is requested for the job\n", str);
3300     job_sheet_supported = get_supported_options(p->prattrs,
3301 						"job-sheets-supported");
3302     if (str) {
3303       if (!cupsArrayFind(job_sheet_supported, (void *)str) &&
3304 	  strcasecmp(str,"none")) {
3305 	debug_printf("Printer %s doesn't support the job-sheet %s\n", printer,
3306 		     str);
3307 	ret = 0;
3308 	goto cleanup;
3309       }
3310     }
3311   }
3312 
3313   /* Multiple document handling */
3314   /* Can't get multiple-document-handling data from job templates */
3315   if ((attr = ippFindAttribute(response, "multiple-document-handling",
3316 			       IPP_TAG_ZERO)) != NULL && ippGetCount(attr)>0) {
3317     str = ippGetString(attr, 0, NULL);
3318     debug_printf("The multiple-document-handling type  %s is requested\n", str);
3319     if (str) {
3320       multiple_doc_supported =
3321 	get_supported_options(p->prattrs,
3322 			      "multiple-document-handling-supported");
3323       if (!cupsArrayFind(multiple_doc_supported, (void *)str)) {
3324 	debug_printf("Printer %s doesn't support the multiple document handling option %s\n",
3325 		     printer, str);
3326 	ret = 0;
3327 	goto cleanup;
3328       }
3329     }
3330   }
3331 
3332   /* Media Type */
3333   if ((attr = ippFindAttribute(response, "MediaType",
3334 			       IPP_TAG_ZERO)) != NULL &&
3335       ippGetCount(attr) > 0) {
3336     str = ippGetString(attr, 0, NULL);
3337     debug_printf("The mediatype %s is requested for the job\n", str);
3338     if (str != NULL) {
3339       media_type_supported = get_supported_options(p->prattrs,
3340 						   "media-type-supported");
3341       if (!cupsArrayFind(media_type_supported, (void *)str) &&
3342 	  strcasecmp(str, AUTO_OPTION)) {
3343 	debug_printf("Printer %s doesn't support the media-type %s\n",
3344 		     printer, str);
3345 	ret = 0;
3346 	goto cleanup;
3347       }
3348     }
3349   }
3350 
3351   /* Staple Location*/
3352   if ((attr = ippFindAttribute(response, "StapleLocation",
3353 			       IPP_TAG_ZERO)) != NULL &&
3354       ippGetCount(attr) > 0) {
3355     str = ippGetString(attr, 0, NULL);
3356     debug_printf("The staple location %s is requested for the job\n", str);
3357     if (str != NULL) {
3358       staplelocation_supported =
3359 	get_supported_options(p->prattrs, "StapleLocation");
3360       if (!cupsArrayFind(staplelocation_supported, (void *)str) &&
3361 	  strcasecmp(str, "None")) {
3362 	debug_printf("Printer %s doesn't support the staple location %s\n",
3363 		     printer, str);
3364 	ret = 0;
3365 	goto cleanup;
3366       }
3367     }
3368   }
3369 
3370   /* FoldType */
3371   if ((attr = ippFindAttribute(response, "FoldType",
3372 			       IPP_TAG_ZERO)) != NULL &&
3373       ippGetCount(attr) > 0) {
3374     str = ippGetString(attr, 0, NULL);
3375     debug_printf("The FoldType %s is requested for the job\n", str);
3376     if (str != NULL) {
3377       foldtype_supported = get_supported_options(p->prattrs, "FoldType");
3378       if (!cupsArrayFind(foldtype_supported, (void *)str) &&
3379 	  strcasecmp(str, "None")) {
3380 	debug_printf("Printer %s doesn't support the FoldType %s\n",
3381 		     printer, str);
3382 	ret = 0;
3383 	goto cleanup;
3384       }
3385     }
3386   }
3387 
3388   /* PunchMedia */
3389   if ((attr = ippFindAttribute(response, "PunchMedia",
3390 			       IPP_TAG_ZERO)) != NULL &&
3391       ippGetCount(attr) > 0) {
3392     str = ippGetString(attr, 0, NULL);
3393     debug_printf("The PunchMedia %s is requested for the job\n", str);
3394     if (str != NULL) {
3395       punchmedia_supported = get_supported_options(p->prattrs, "PunchMedia");
3396       if (!cupsArrayFind(punchmedia_supported, (void *)str) &&
3397 	  strcasecmp(str, "none")) {
3398 	debug_printf("Printer %s doesn't support the PunchMedia %s\n",
3399 		     printer, str);
3400 	ret = 0;
3401 	goto cleanup;
3402       }
3403     }
3404   }
3405 
3406   /* ColorModel */
3407   if ((attr = ippFindAttribute(response, "ColorModel",
3408 			       IPP_TAG_ZERO)) != NULL &&
3409       ippGetCount(attr) > 0) {
3410     str = ippGetString(attr, 0, NULL);
3411     debug_printf("The ColorModel %s is requested for the job\n", str);
3412     if (str != NULL) {
3413       color_supported = get_supported_options(p->prattrs, "ColorModel");
3414       if (!cupsArrayFind(color_supported, (void *)str) &&
3415 	  strcasecmp(str,"Gray")) {
3416 	debug_printf("Printer %s doesn't support the ColorModel %s\n",
3417 		     printer, str);
3418 	ret = 0;
3419 	goto cleanup;
3420       }
3421     }
3422   }
3423 
3424   /* Sides supported */
3425   if ((attr = ippFindAttribute(response, "Duplex",
3426 			       IPP_TAG_ZERO)) != NULL) {
3427     side_found = 0;
3428     str = ippGetString(attr, 0, NULL);
3429     if (str) {
3430       if ((attr1 = ippFindAttribute(p->prattrs, "sides-supported",
3431 				    IPP_TAG_KEYWORD)) != NULL) {
3432         for (i = 0, count = ippGetCount(attr1); i < count; i++) {
3433           side = ippGetString(attr1, i, NULL);
3434           debug_printf("The duplex option %s is requested\n", side);
3435           if (!strcasecmp(str, "None") && !strcmp(side, "one-sided")) {
3436             side_found = 1;
3437             break;
3438           } else if (!strcmp(str, "DuplexNoTumble") &&
3439 		     !strcmp(side, "two-sided-long-edge")) {
3440             side_found = 1;
3441             break;
3442           } else if (!strcmp(str, "DuplexTumble") &&
3443 		     !strcmp(side, "two-sided-short-edge")) {
3444             side_found = 1;
3445             break;
3446           }
3447         }
3448         if (!side_found) {
3449           debug_printf("Printer %s doesn't support the required duplex options\n",
3450 		       printer);
3451           ret = 0;
3452           goto cleanup;
3453         }
3454       }
3455     }
3456   }
3457 
3458   /* Orientation Requested */
3459   if ((attr = ippFindAttribute(response, "orientation-requested",
3460 			       IPP_TAG_ENUM)) != NULL) {
3461     orien_found = 0;
3462     orien_req = ippGetInteger(attr, 0);
3463     if ((attr1 = ippFindAttribute(p->prattrs,
3464 				  "orientation-requested-supported",
3465 				  IPP_TAG_ENUM)) != NULL) {
3466       for (i = 0, count = ippGetCount(attr1); i < count; i ++) {
3467         orien = ippGetInteger(attr1, i);
3468         if (orien == orien_req) {
3469           orien_found = 1;
3470           break;
3471         }
3472       }
3473       if (!orien_found) {
3474         debug_printf("Printer %s doesn't support the requested orientation\n",
3475 		     printer);
3476         ret = 0;
3477         goto cleanup;
3478       }
3479     }
3480   }
3481 
3482   /* Page Size */
3483   if ((attr = ippFindAttribute(response, "PageSize",
3484 			       IPP_TAG_ZERO)) != NULL &&
3485       ippGetCount(attr) > 0) {
3486     str = ippGetString(attr, 0, NULL);
3487     if (str) {
3488       sizes = get_pagesize(p->prattrs);
3489       if (!cupsArrayFind(sizes, (void*)str)) {
3490         debug_printf("Printer %s doesn't support %s PageSize\n", p->uri, str);
3491         ret = 0;
3492         goto cleanup;
3493       }
3494     }
3495   }
3496 
3497   /* Print Quality */
3498   *print_quality = 4;
3499   if ((attr = ippFindAttribute(response, "cupsPrintQuality",
3500 			       IPP_TAG_ZERO)) != NULL &&
3501       ippGetCount(attr) > 0) {
3502     print_qualities = get_supported_options(p->prattrs, "cupsPrintQuality");
3503     str = ippGetString(attr, 0, NULL);
3504     debug_printf("%s\n", str);
3505     if (str && !cupsArrayFind(print_qualities, (void*)str)) {
3506       debug_printf("In\n");
3507       if(!strcmp(str, "5"))
3508         *print_quality = 5;
3509       else if (!strcmp(str, "3"))
3510         *print_quality = 3;
3511       debug_printf("Printer doesn't support %s print quality\n",
3512 		   !strcmp(str, "5") ? "HIGH": "DRAFT");
3513       ret = 0;
3514       goto cleanup;
3515     }
3516   }
3517 
3518   cleanup:
3519     if (response != NULL)
3520       ippDelete(response);
3521     if (job_sheet_supported != NULL)
3522       cupsArrayDelete(job_sheet_supported);
3523     if (multiple_doc_supported)
3524       cupsArrayDelete(multiple_doc_supported);
3525     if (media_type_supported != NULL)
3526       cupsArrayDelete(media_type_supported);
3527     if (staplelocation_supported != NULL)
3528       cupsArrayDelete(staplelocation_supported);
3529     if (foldtype_supported != NULL)
3530       cupsArrayDelete(foldtype_supported);
3531     if (punchmedia_supported != NULL)
3532       cupsArrayDelete(punchmedia_supported);
3533     if (color_supported != NULL)
3534       cupsArrayDelete(color_supported);
3535     if (print_qualities != NULL)
3536       cupsArrayDelete(print_qualities);
3537     if (sizes != NULL)
3538       cupsArrayDelete(sizes);
3539 
3540   return ret;
3541 }
3542 
3543 
3544 /*
3545  * Remove all illegal characters and replace each group of such characters
3546  * by a single separator character (dash or underscore), return a free()-able
3547  * string.
3548  *
3549  * mode = 0: Only allow letters, numbers, dashes, and underscores for
3550  *           turning make/model info into a valid print queue name or
3551  *           into a string which can be supplied as option value in a
3552  *           filter command line without need of quoting. Replace all
3553  *           groups of illegal characters by single dashes and remove
3554  *           leading and trailing dashes.
3555  * mode = 1: Allow also '/', '.', ',' for cleaning up MIME type
3556  *           strings (here available Page Description Languages, PDLs) to
3557  *           supply them on a filter command line without quoting.
3558  *           Replace all groups of illegal characters by single dashes
3559  *           and remove leading and trailing dashes.
3560  * mode = 2: Keep all locale-free alphanumeric characters (a-z, A-Z, 0-9)
3561  *           and replace everything else by underscores. Replace all
3562  *           groups of illegal characters by single underscores. This is
3563  *           for generating print queue names from DNS-SD service names
3564  *           to do it exactly as CUPS 2.2.x (or newer) does, so that CUPS
3565  *           does not create its own temporary queues in addition.
3566  *
3567  * Especially this prevents from arbitrary code execution by interface scripts
3568  * generated for print queues to native IPP printers when a malicious IPP
3569  * print service with forged PDL and/or make/model info gets broadcasted into
3570  * the local network.
3571  */
3572 
3573 char *                                 /* O - Cleaned string */
remove_bad_chars(const char * str_orig,int mode)3574 remove_bad_chars(const char *str_orig, /* I - Original string */
3575 		 int mode)             /* I - 0: Make/Model, queue name */
3576                                        /*     1: MIME types/PDLs */
3577                                        /*     2: Queue name from DNS-SD */
3578                                        /*        service name */
3579 {
3580   int i, j;
3581   int havesep = 0;
3582   char sep, *str;
3583 
3584   if (str_orig == NULL)
3585     return NULL;
3586 
3587   str = strdup(str_orig);
3588 
3589   /* for later str[strlen(str)-1] access */
3590   if (strlen(str) < 1)
3591     return str;
3592 
3593   /* Select separator character */
3594   if (mode == 2)
3595     sep = '_';
3596   else
3597     sep = '-';
3598 
3599   for (i = 0, j = 0; i < strlen(str); i++, j++) {
3600     if (((str[i] >= 'A') && (str[i] <= 'Z')) ||
3601 	((str[i] >= 'a') && (str[i] <= 'z')) ||
3602 	((str[i] >= '0') && (str[i] <= '9')) ||
3603 	(mode != 2 && (str[i] == '_' ||
3604 		       str[i] == '.')) ||
3605 	(mode == 1 && (str[i] == '/' ||
3606 		       str[i] == ','))) {
3607       /* Allowed character, keep it */
3608       havesep = 0;
3609       str[j] = str[i];
3610     } else {
3611       /* Replace all other characters by a single separator */
3612       if (havesep == 1)
3613 	j --;
3614       else {
3615 	havesep = 1;
3616 	str[j] = sep;
3617       }
3618     }
3619   }
3620   /* Add terminating zero */
3621   str[j] = '\0';
3622 
3623   i = 0;
3624   if (mode != 2) {
3625     /* Cut off trailing separators */
3626     while (strlen(str) > 0 && str[strlen(str)-1] == sep)
3627       str[strlen(str)-1] = '\0';
3628 
3629     /* Cut off leading separators */
3630     while (str[i] == sep)
3631       ++i;
3632   }
3633 
3634   /* keep a free()-able string. +1 for trailing \0 */
3635   return memmove(str, str + i, strlen(str) - i + 1);
3636 }
3637 
3638 
3639 static local_printer_t *
new_local_printer(const char * device_uri,const char * uuid,gboolean cups_browsed_controlled)3640 new_local_printer (const char *device_uri,
3641 		   const char *uuid,
3642 		   gboolean cups_browsed_controlled)
3643 {
3644   local_printer_t *printer = g_malloc (sizeof (local_printer_t));
3645   printer->device_uri = strdup (device_uri);
3646   printer->uuid = (char*)uuid;
3647   printer->cups_browsed_controlled = cups_browsed_controlled;
3648   return printer;
3649 }
3650 
3651 static void
free_local_printer(gpointer data)3652 free_local_printer (gpointer data)
3653 {
3654   local_printer_t *printer = data;
3655   debug_printf("free_local_printer() in THREAD %ld\n", pthread_self());
3656   free (printer->device_uri);
3657   if (printer->uuid) free (printer->uuid);
3658   free (printer);
3659 }
3660 
3661 static gboolean
local_printer_is_same_device(gpointer key,gpointer value,gpointer user_data)3662 local_printer_is_same_device (gpointer key,
3663 			      gpointer value,
3664 			      gpointer user_data)
3665 {
3666   local_printer_t *lprinter = value;
3667   remote_printer_t *p = user_data;
3668   char    lhost[HTTP_MAX_URI],     /* Local printer: Hostname */
3669           lresource[HTTP_MAX_URI], /* Local printer: Resource path */
3670           lscheme[32],             /* Local printer: URI's scheme */
3671           lusername[64],           /* Local printer: URI's username */
3672           *ltype = NULL,           /* Local printer: If URI DNS-SD-based */
3673           *ldomain = NULL;         /* pointers into lhost for components*/
3674   int     lport = 0;               /* Local printer: URI's port number */
3675 
3676   debug_printf("local_printer_is_same_device() in THREAD %ld\n",
3677 	       pthread_self());
3678   if (!lprinter || !lprinter->device_uri || !p)
3679     return (0);
3680   /* Separate the local printer's URI into their components */
3681   memset(lscheme, 0, sizeof(lscheme));
3682   memset(lusername, 0, sizeof(lusername));
3683   memset(lhost, 0, sizeof(lhost));
3684   memset(lresource, 0, sizeof(lresource));
3685   httpSeparateURI(HTTP_URI_CODING_ALL, lprinter->device_uri,
3686 		  lscheme, sizeof(lscheme) - 1,
3687 		  lusername, sizeof(lusername) - 1,
3688 		  lhost, sizeof(lhost) - 1,
3689 		  &lport,
3690 		  lresource, sizeof(lresource) - 1);
3691   if ((ltype = strstr(lhost, "._ipp._tcp.")) != NULL ||
3692       (ltype = strstr(lhost, "._ipps._tcp.")) != NULL) {
3693     *ltype = '\0';
3694     ltype ++;
3695     ldomain = strchr(ltype + 9, '.');
3696     *ldomain = '\0';
3697     ldomain ++;
3698     if (*ldomain && ldomain[strlen(ldomain) - 1] == '.')
3699       ldomain[strlen(ldomain) - 1] = '\0';
3700   }
3701   /* Consider not only absolutely equal URIs as equal
3702      but alo URIs which differ only by use of IPP or
3703      IPPS and/or have the IPP standard port 631
3704      replaced by the HTTPS standard port 443, as this
3705      is common on network printers */
3706   return ((ltype && p->service_name && p->domain &&
3707 	   g_str_equal(lhost, p->service_name) &&
3708 	   !strncmp(ldomain, p->domain, strlen(ldomain))) ||
3709 	  (!ltype && p->host && p->resource &&
3710 	   (g_str_equal(lscheme, "ipp") || g_str_equal(lscheme, "ipps")) &&
3711 	   !lusername[0] &&
3712 	   g_str_equal(lhost, p->host) &&
3713 	   ((!p->port && (lport == 631 || lport == 443)) ||
3714 	    lport == p->port ||
3715 	    (lport == 631 && p->port == 443) ||
3716 	    (lport == 443 && p->port == 631)) &&
3717 	   g_str_equal(lresource, p->resource)));
3718 }
3719 
3720 static gboolean
local_printer_has_uuid(gpointer key,gpointer value,gpointer user_data)3721 local_printer_has_uuid (gpointer key,
3722 			gpointer value,
3723 			gpointer user_data)
3724 {
3725   local_printer_t *printer = value;
3726   char    *uuid = user_data;
3727 
3728   debug_printf("local_printer_has_uuid() in THREAD %ld\n", pthread_self());
3729   return (printer != NULL && printer->uuid != NULL && uuid != NULL &&
3730 	  g_str_equal(printer->uuid, uuid));
3731 }
3732 
3733 static gboolean
local_printer_service_name_matches(gpointer key,gpointer value,gpointer user_data)3734 local_printer_service_name_matches (gpointer key,
3735 				    gpointer value,
3736 				    gpointer user_data)
3737 {
3738   char *queue_name = key;
3739   char *service_name = user_data;
3740   char *p;
3741   debug_printf("local_printer_service_name_matches() in THREAD %ld\n",
3742 	       pthread_self());
3743   p = remove_bad_chars(service_name, 2);
3744   if (p && strncasecmp(p, queue_name, 63) == 0) {
3745     free(p);
3746     return TRUE;
3747   }
3748   if (p) free(p);
3749   return FALSE;
3750 }
3751 
3752 static void
local_printers_create_subscription(http_t * conn)3753 local_printers_create_subscription (http_t *conn)
3754 {
3755   char temp[1024];
3756   if (!local_printers_context) {
3757     local_printers_context = g_malloc0 (sizeof (browsepoll_t));
3758     /* The httpGetAddr() function was introduced in CUPS 2.0.0 */
3759 #ifdef HAVE_CUPS_2_0
3760     local_printers_context->server =
3761       strdup(httpAddrString(httpGetAddress(conn),
3762 			    temp, sizeof(temp)));
3763     local_printers_context->port = httpAddrPort(httpGetAddress(conn));
3764 #else
3765     local_printers_context->server = cupsServer();
3766     local_printers_context->port = ippPort();
3767 #endif
3768     local_printers_context->can_subscribe = TRUE;
3769   }
3770 
3771   browse_poll_create_subscription (local_printers_context, conn);
3772 }
3773 
3774 int
add_dest_cb(dest_list_t * user_data,unsigned flags,cups_dest_t * dest)3775 add_dest_cb(dest_list_t *user_data, unsigned flags, cups_dest_t *dest)
3776 {
3777   if (flags & CUPS_DEST_FLAGS_REMOVED)
3778     /* Remove destination from array */
3779     user_data->num_dests =
3780       cupsRemoveDest(dest->name, dest->instance, user_data->num_dests,
3781 		     &(user_data->dests));
3782   else
3783     /* Add destination to array... */
3784     user_data->num_dests =
3785       cupsCopyDest(dest, user_data->num_dests,
3786 		   &(user_data->dests));
3787   return (1);
3788 }
3789 
3790 
3791 const char *
get_printer_uuid(http_t * http_printer,const char * raw_uri)3792 get_printer_uuid(http_t *http_printer,
3793 		 const char* raw_uri)
3794 {
3795   ipp_t *response = NULL;
3796   ipp_attribute_t *attr = NULL;
3797   const char * uuid = NULL;
3798 
3799   const char * const pattrs[] = {
3800     "printer-uuid",
3801   };
3802   const char * const req_attrs[] = {
3803     "printer-uuid",
3804   };
3805 
3806   if (http_printer == NULL)
3807     debug_printf ("HTTP connection for printer with URI %s not set!\n",
3808 		  raw_uri);
3809 
3810   if ((response =
3811        get_printer_attributes2(http_printer, raw_uri,
3812 			       pattrs, 1, req_attrs, 1, 0)) == NULL) {
3813     debug_printf ("Printer with URI %s has no \"printer-uuid\" IPP attribute!\n",
3814 		  raw_uri);
3815     return NULL;
3816   }
3817 
3818   attr = ippFindAttribute(response, "printer-uuid", IPP_TAG_URI);
3819 
3820 
3821   if (attr)
3822     uuid = strdup(ippGetString(attr, 0, NULL) + 9);
3823   else {
3824     debug_printf ("Printer with URI %s: Cannot read \"printer-uuid\" IPP attribute!\n",
3825 		  raw_uri);
3826   }
3827 
3828   ippDelete(response);
3829 
3830   return uuid;
3831 }
3832 
3833 static void
get_local_printers(void)3834 get_local_printers (void)
3835 {
3836   dest_list_t dest_list = {0, NULL};
3837   http_t *conn = NULL;
3838 
3839   conn = http_connect_local ();
3840 
3841   /* We only want to have a list of actually existing CUPS queues, not of
3842      DNS-SD-discovered printers for which CUPS can auto-setup a driverless
3843      print queue */
3844   if (OnlyUnsupportedByCUPS)
3845     cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, 0, 0,
3846 		  (cups_dest_cb_t)add_dest_cb, &dest_list);
3847   else
3848     cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, CUPS_PRINTER_LOCAL,
3849 		  CUPS_PRINTER_DISCOVERED, (cups_dest_cb_t)add_dest_cb,
3850 		  &dest_list);
3851   debug_printf ("cups-browsed (%s): cupsEnumDests\n", local_server_str);
3852   g_hash_table_remove_all (local_printers);
3853   if (OnlyUnsupportedByCUPS)
3854     g_hash_table_remove_all (cups_supported_remote_printers);
3855   int num_dests = dest_list.num_dests;
3856   cups_dest_t *dests = dest_list.dests;
3857   for (int i = 0; i < num_dests; i++) {
3858     const char *val;
3859     cups_dest_t *dest = &dests[i];
3860     local_printer_t *printer;
3861     gboolean cups_browsed_controlled;
3862     gboolean is_temporary;
3863     gboolean is_cups_supported_remote;
3864     char uri[HTTP_MAX_URI];
3865 
3866     const char *device_uri = cupsGetOption ("device-uri",
3867 					    dest->num_options,
3868 					    dest->options);
3869     if (device_uri == NULL)
3870       device_uri = "";
3871 
3872     /* Temporary CUPS queue? */
3873     val = cupsGetOption ("printer-is-temporary",
3874 			 dest->num_options,
3875 			 dest->options);
3876     is_temporary = (val && (!strcasecmp (val, "yes") ||
3877 			    !strcasecmp (val, "on") ||
3878 			    !strcasecmp (val, "true")));
3879 
3880     if (OnlyUnsupportedByCUPS) {
3881       /* Printer discovered by DNS-SD and supported by CUPS' temporary
3882 	 queues? */
3883       val = cupsGetOption ("printer-uri-supported",
3884 			   dest->num_options,
3885 			   dest->options);
3886       /* Printer has no local CUPS queue but CUPS would create a
3887 	 temporary queue on-demand */
3888       is_cups_supported_remote = (val == NULL || is_temporary);
3889     } else {
3890       is_cups_supported_remote = 0;
3891       if (is_temporary)
3892 	continue;
3893     }
3894 
3895     val = cupsGetOption (CUPS_BROWSED_MARK,
3896 			 dest->num_options,
3897 			 dest->options);
3898     cups_browsed_controlled = val && (!strcasecmp (val, "yes") ||
3899 				      !strcasecmp (val, "on") ||
3900 				      !strcasecmp (val, "true"));
3901     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3902 		     "localhost", 0, "/printers/%s", dest->name);
3903     printer = new_local_printer (device_uri, get_printer_uuid(conn, uri),
3904 				 cups_browsed_controlled);
3905     debug_printf ("Printer %s: %s, %s%s%s\n",
3906 		  dest->name, device_uri, printer->uuid,
3907 		  cups_browsed_controlled ? ", cups_browsed" : "",
3908 		  is_cups_supported_remote ? ", temporary" : "");
3909 
3910     if (is_cups_supported_remote)
3911       g_hash_table_insert (cups_supported_remote_printers,
3912 			   g_ascii_strdown (dest->name, -1),
3913 			   printer);
3914     else
3915       g_hash_table_insert (local_printers,
3916 			   g_ascii_strdown (dest->name, -1),
3917 			   printer);
3918   }
3919 
3920   cupsFreeDests (num_dests, dests);
3921 }
3922 
3923 static browse_data_t *
new_browse_data(int type,int state,const gchar * uri,const gchar * location,const gchar * info,const gchar * make_model,const gchar * browse_options)3924 new_browse_data (int type, int state, const gchar *uri,
3925 		 const gchar *location, const gchar *info,
3926 		 const gchar *make_model, const gchar *browse_options)
3927 {
3928   browse_data_t *data = g_malloc (sizeof (browse_data_t));
3929   data->type = type;
3930   data->state = state;
3931   data->uri = g_strdup (uri);
3932   data->location = g_strdup (location);
3933   data->info = g_strdup (info);
3934   data->make_model = g_strdup (make_model);
3935   data->browse_options = g_strdup (browse_options);
3936   return data;
3937 }
3938 
3939 static void
browse_data_free(gpointer data)3940 browse_data_free (gpointer data)
3941 {
3942   browse_data_t *bdata = data;
3943   debug_printf("browse_data_free() in THREAD %ld\n", pthread_self());
3944   g_free (bdata->uri);
3945   g_free (bdata->location);
3946   g_free (bdata->info);
3947   g_free (bdata->make_model);
3948   g_free (bdata->browse_options);
3949   g_free (bdata);
3950 }
3951 
3952 static void
prepare_browse_data(void)3953 prepare_browse_data (void)
3954 {
3955   static const char * const rattrs[] = { "printer-type",
3956 					 "printer-state",
3957 					 "printer-uri-supported",
3958 					 "printer-info",
3959 					 "printer-location",
3960 					 "printer-make-and-model",
3961 					 "auth-info-required",
3962 					 "printer-uuid",
3963 					 "job-template" };
3964   ipp_t *request, *response = NULL;
3965   ipp_attribute_t *attr;
3966   http_t *conn = NULL;
3967 
3968   conn = http_connect_local ();
3969 
3970   if (conn == NULL) {
3971     debug_printf("Browse send failed to connect to localhost\n");
3972     goto fail;
3973   }
3974 
3975   request = ippNewRequest(CUPS_GET_PRINTERS);
3976   ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3977 		 "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]),
3978 		 NULL, rattrs);
3979   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
3980 		"requesting-user-name", NULL, cupsUser ());
3981 
3982   debug_printf("preparing browse data\n");
3983   response = cupsDoRequest (conn, request, "/");
3984   if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
3985     debug_printf("browse send failed for localhost: %s\n",
3986 		 cupsLastErrorString ());
3987     goto fail;
3988   }
3989 
3990   g_list_free_full (browse_data, browse_data_free);
3991   browse_data = NULL;
3992   for (attr = ippFirstAttribute(response); attr;
3993        attr = ippNextAttribute(response)) {
3994     int type = -1, state = -1;
3995     const char *uri = NULL;
3996     gchar *location = NULL;
3997     gchar *info = NULL;
3998     gchar *make_model = NULL;
3999     GString *browse_options = g_string_new ("");
4000 
4001     /* Skip any non-printer attributes */
4002     while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
4003       attr = ippNextAttribute(response);
4004 
4005     if (!attr)
4006       break;
4007 
4008     while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
4009       const char *attrname = ippGetName(attr);
4010       int value_tag = ippGetValueTag(attr);
4011 
4012       if (!strcasecmp(attrname, "printer-type") &&
4013 	  value_tag == IPP_TAG_ENUM) {
4014 	type = ippGetInteger(attr, 0);
4015 	if (type & CUPS_PRINTER_NOT_SHARED) {
4016 	  /* Skip CUPS queues not marked as shared */
4017 	  state = -1;
4018 	  type = -1;
4019 	  break;
4020 	}
4021       } else if (!strcasecmp(attrname, "printer-state") &&
4022 		 value_tag == IPP_TAG_ENUM)
4023 	state = ippGetInteger(attr, 0);
4024       else if (!strcasecmp(attrname, "printer-uri-supported") &&
4025 	       value_tag == IPP_TAG_URI)
4026 	uri = ippGetString(attr, 0, NULL);
4027       else if (!strcasecmp(attrname, "printer-location") &&
4028 	       value_tag == IPP_TAG_TEXT) {
4029 	/* Remove quotes */
4030 	gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
4031 	location = g_strjoinv ("", tokens);
4032 	g_strfreev (tokens);
4033       } else if (!strcasecmp(attrname, "printer-info") &&
4034 		 value_tag == IPP_TAG_TEXT) {
4035 	/* Remove quotes */
4036 	gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
4037 	info = g_strjoinv ("", tokens);
4038 	g_strfreev (tokens);
4039       } else if (!strcasecmp(attrname, "printer-make-and-model") &&
4040 		 value_tag == IPP_TAG_TEXT) {
4041 	/* Remove quotes */
4042 	gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
4043 	make_model = g_strjoinv ("", tokens);
4044 	g_strfreev (tokens);
4045       } else if (!strcasecmp(attrname, "auth-info-required") &&
4046 		 value_tag == IPP_TAG_KEYWORD) {
4047 	if (strcasecmp (ippGetString(attr, 0, NULL), "none"))
4048 	  g_string_append_printf (browse_options, "auth-info-required=%s ",
4049 				  ippGetString(attr, 0, NULL));
4050       } else if (!strcasecmp(attrname, "printer-uuid") &&
4051 		 value_tag == IPP_TAG_URI)
4052 	g_string_append_printf (browse_options, "uuid=%s ",
4053 				ippGetString(attr, 0, NULL));
4054       else if (!strcasecmp(attrname, "job-sheets-default") &&
4055 	       value_tag == IPP_TAG_NAME &&
4056 	       ippGetCount(attr) == 2)
4057 	g_string_append_printf (browse_options, "job-sheets=%s,%s ",
4058 				ippGetString(attr, 0, NULL),
4059 				ippGetString(attr, 1, NULL));
4060       else if (strstr(attrname, "-default")) {
4061 	gchar *name = g_strdup (attrname);
4062 	gchar *value = NULL;
4063 	*strstr (name, "-default") = '\0';
4064 
4065 	switch (value_tag) {
4066 	  gchar **tokens;
4067 
4068 	case IPP_TAG_KEYWORD:
4069 	case IPP_TAG_STRING:
4070 	case IPP_TAG_NAME:
4071 	  /* Escape value */
4072 	  tokens = g_strsplit_set (ippGetString(attr, 0, NULL),
4073 				   " \"\'\\", -1);
4074 	  value = g_strjoinv ("\\", tokens);
4075 	  g_strfreev (tokens);
4076 	  break;
4077 
4078 	default:
4079 	  /* other values aren't needed? */
4080 	  debug_printf("skipping %s (%d)\n", name, value_tag);
4081 	  break;
4082 	}
4083 
4084 	if (value) {
4085 	  g_string_append_printf (browse_options, "%s=%s ", name, value);
4086 	  g_free (value);
4087 	}
4088 
4089 	g_free (name);
4090       }
4091 
4092       attr = ippNextAttribute(response);
4093     }
4094 
4095     if (type != -1 && state != -1 && uri && location && info && make_model) {
4096       gchar *browse_options_str = g_string_free (browse_options, FALSE);
4097       browse_data_t *data;
4098       browse_options = NULL;
4099       g_strchomp (browse_options_str);
4100       data = new_browse_data (type, state, uri, location,
4101 			      info, make_model, browse_options_str);
4102       browse_data = g_list_insert (browse_data, data, 0);
4103       g_free (browse_options_str);
4104     }
4105 
4106     if (make_model)
4107       g_free (make_model);
4108 
4109     if (info)
4110       g_free (info);
4111 
4112     if (location)
4113       g_free (location);
4114 
4115     if (browse_options)
4116       g_string_free (browse_options, TRUE);
4117 
4118     if (!attr)
4119       break;
4120   }
4121 
4122  fail:
4123   if (response)
4124     ippDelete(response);
4125 }
4126 
4127 static void
update_local_printers(void)4128 update_local_printers (void)
4129 {
4130   gboolean get_printers = FALSE;
4131   http_t *conn;
4132 
4133   if (inhibit_local_printers_update)
4134     return;
4135 
4136   conn = http_connect_local ();
4137   if (conn &&
4138       (!local_printers_context || local_printers_context->can_subscribe)) {
4139     if (!local_printers_context ||
4140 	local_printers_context->subscription_id == -1) {
4141       /* No subscription yet. First, create the subscription. */
4142       local_printers_create_subscription (conn);
4143       get_printers = TRUE;
4144     } else
4145       /* We already have a subscription, so use it. */
4146 
4147       /* Note: for the moment, browse_poll_get_notifications() just
4148        * tells us whether we should re-fetch the printer list, so it
4149        * is safe to use here. */
4150       get_printers = browse_poll_get_notifications (local_printers_context,
4151 						    conn);
4152   } else
4153     get_printers = TRUE;
4154 
4155   if (get_printers) {
4156     get_local_printers ();
4157 
4158     if (BrowseLocalProtocols & BROWSE_CUPS)
4159       prepare_browse_data ();
4160   }
4161 }
4162 
4163 int
check_jobs()4164 check_jobs () {
4165   int num_jobs = 0;
4166   cups_job_t *jobs = NULL;
4167   remote_printer_t *p;
4168   http_t *conn = NULL;
4169 
4170   conn = http_connect_local ();
4171   if (conn == NULL) {
4172     debug_printf("Cannot connect to local CUPS to check whether there are still jobs.\n");
4173     return 0;
4174   }
4175 
4176   if (cupsArrayCount(remote_printers) > 0)
4177     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
4178 	 p;
4179 	 p = (remote_printer_t *)cupsArrayNext(remote_printers))
4180       if (!p->slave_of) {
4181 	num_jobs = cupsGetJobs2(conn, &jobs, p->queue_name, 0,
4182 				CUPS_WHICHJOBS_ACTIVE);
4183 	if (num_jobs > 0) {
4184 	  debug_printf("Queue %s still has jobs!\n", p->queue_name);
4185 	  cupsFreeJobs(num_jobs, jobs);
4186 	  return 1;
4187 	}
4188       }
4189 
4190   debug_printf("All our remote printers are without jobs.\n");
4191   return 0;
4192 }
4193 
4194 gboolean
autoshutdown_execute(gpointer data)4195 autoshutdown_execute (gpointer data)
4196 {
4197   debug_printf("autoshutdown_execute() in THREAD %ld\n", pthread_self());
4198   /* Are we still in auto shutdown mode and are we still without queues or
4199      jobs*/
4200   if (autoshutdown &&
4201       (cupsArrayCount(remote_printers) == 0 ||
4202        (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
4203     debug_printf("Automatic shutdown as there are no print queues maintained by us or no jobs on them for %d sec.\n",
4204 		 autoshutdown_timeout);
4205     g_main_loop_quit(gmainloop);
4206     g_main_context_wakeup(NULL);
4207   }
4208 
4209   /* Stop this timeout handler, we needed it only once */
4210   return FALSE;
4211 }
4212 
4213 int
color_space_score(const char * color_space)4214 color_space_score(const char *color_space)
4215 {
4216   int score = 0;
4217   const char *p = color_space;
4218 
4219   if (!p) return -1;
4220   if (!strncasecmp(p, "s", 1)) {
4221     p += 1;
4222     score += 2;
4223   } else if (!strncasecmp(p, "adobe", 5)) {
4224     p += 5;
4225     score += 1;
4226   } else
4227     score += 3;
4228   if (!strncasecmp(p, "black", 5)) {
4229     p += 5;
4230     score += 1000;
4231   } else if (!strncasecmp(p, "gray", 4)) {
4232     p += 4;
4233     score += 2000;
4234   } else if (!strncasecmp(p, "cmyk", 4)) {
4235     p += 4;
4236     score += 4000;
4237   } else if (!strncasecmp(p, "cmy", 3)) {
4238     p += 3;
4239     score += 3000;
4240   } else if (!strncasecmp(p, "rgb", 3)) {
4241     p += 3;
4242     score += 5000;
4243   }
4244   if (!strncasecmp(p, "-", 1) || !strncasecmp(p, "_", 1)) {
4245     p += 1;
4246   }
4247   score += strtol(p, (char **)&p, 10) * 10;
4248   debug_printf("Score for color space %s: %d\n", color_space, score);
4249   return score;
4250 }
4251 
4252 
4253 #ifdef HAVE_LDAP_REBIND_PROC
4254 #  if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
4255 /*
4256  * 'ldap_rebind_proc()' - Callback function for LDAP rebind
4257  */
4258 
4259 static int        /* O - Result code */
ldap_rebind_proc(LDAP * RebindLDAPHandle,LDAP_CONST char * refsp,ber_tag_t request,ber_int_t msgid,void * params)4260 ldap_rebind_proc(LDAP            *RebindLDAPHandle,  /* I - LDAP handle */
4261 		 LDAP_CONST char *refsp,             /* I - ??? */
4262 		 ber_tag_t       request,            /* I - ??? */
4263 		 ber_int_t       msgid,              /* I - ??? */
4264 		 void            *params)            /* I - ??? */
4265 {
4266   int   rc;     /* Result code */
4267 #    if LDAP_API_VERSION > 3000
4268   struct berval bval;     /* Bind value */
4269 #    endif /* LDAP_API_VERSION > 3000 */
4270   (void)request;
4271   (void)msgid;
4272   (void)params;
4273 
4274  /*
4275   * Bind to new LDAP server...
4276   */
4277 
4278   debug_printf("ldap_rebind_proc: Rebind to %s\n", refsp);
4279 
4280 #    if LDAP_API_VERSION > 3000
4281   bval.bv_val = BrowseLDAPPassword;
4282   bval.bv_len = (BrowseLDAPPassword == NULL) ? 0 : strlen(BrowseLDAPPassword);
4283 
4284   rc = ldap_sasl_bind_s(RebindLDAPHandle, BrowseLDAPBindDN, LDAP_SASL_SIMPLE,
4285                         &bval, NULL, NULL, NULL);
4286 #    else
4287   rc = ldap_bind_s(RebindLDAPHandle, BrowseLDAPBindDN, BrowseLDAPPassword,
4288                    LDAP_AUTH_SIMPLE);
4289 #    endif /* LDAP_API_VERSION > 3000 */
4290 
4291   return (rc);
4292 }
4293 
4294 
4295 #  else /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
4296 /*
4297  * 'ldap_rebind_proc()' - Callback function for LDAP rebind
4298  */
4299 
4300 static int        /* O - Result code */
ldap_rebind_proc(LDAP * RebindLDAPHandle,char ** dnp,char ** passwdp,int * authmethodp,int freeit,void * arg)4301 ldap_rebind_proc(LDAP *RebindLDAPHandle,   /* I - LDAP handle */
4302 		 char **dnp,               /* I - ??? */
4303 		 char **passwdp,           /* I - ??? */
4304 		 int  *authmethodp,        /* I - ??? */
4305 		 int  freeit,              /* I - ??? */
4306 		 void *arg)                /* I - ??? */
4307 {
4308   switch (freeit) {
4309   case 1:
4310     /*
4311      * Free current values...
4312      */
4313 
4314     debug_printf("ldap_rebind_proc: Free values...\n");
4315 
4316     if (dnp && *dnp)
4317       free(*dnp);
4318 
4319     if (passwdp && *passwdp)
4320       free(*passwdp);
4321     break;
4322 
4323   case 0:
4324     /*
4325      * Return credentials for LDAP referal...
4326      */
4327 
4328     debug_printf("ldap_rebind_proc: Return necessary values...\n");
4329 
4330     *dnp         = strdup(BrowseLDAPBindDN);
4331     *passwdp     = strdup(BrowseLDAPPassword);
4332     *authmethodp = LDAP_AUTH_SIMPLE;
4333     break;
4334 
4335   default:
4336     /*
4337      * Should never happen...
4338      */
4339 
4340     debug_printf("LDAP rebind has been called with wrong freeit value!\n");
4341     break;
4342   }
4343 
4344   return (LDAP_SUCCESS);
4345 }
4346 #  endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
4347 #endif /* HAVE_LDAP_REBIND_PROC */
4348 
4349 
4350 #ifdef HAVE_LDAP
4351 /*
4352  * 'ldap_new_connection()' - Start new LDAP connection
4353  */
4354 
4355 static LDAP *       /* O - LDAP handle */
ldap_new_connection(void)4356 ldap_new_connection(void)
4357 {
4358   int   rc;                       /* LDAP API status */
4359   int   version = 3;              /* LDAP version */
4360   struct berval bv = {0, ""};     /* SASL bind value */
4361   LDAP    *TempBrowseLDAPHandle=NULL;
4362                                   /* Temporary LDAP Handle */
4363 #  if defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP)
4364   int   ldap_ssl = 0;             /* LDAP SSL indicator */
4365   int   ssl_err = 0;              /* LDAP SSL error value */
4366 #  endif /* defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) */
4367 
4368 
4369 #  ifdef HAVE_OPENLDAP
4370 #    ifdef HAVE_LDAP_SSL
4371  /*
4372   * Set the certificate file to use for encrypted LDAP sessions...
4373   */
4374 
4375   if (BrowseLDAPCACertFile) {
4376     debug_printf("ldap_new_connection: Setting CA certificate file \"%s\"\n",
4377 		 BrowseLDAPCACertFile);
4378 
4379     if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
4380 			      (void *)BrowseLDAPCACertFile)) != LDAP_SUCCESS)
4381       debug_printf("Unable to set CA certificate file for LDAP "
4382 		   "connections: %d - %s\n", rc, ldap_err2string(rc));
4383   }
4384 #    endif /* HAVE_LDAP_SSL */
4385 
4386  /*
4387   * Initialize OPENLDAP connection...
4388   * LDAP stuff currently only supports ldapi EXTERNAL SASL binds...
4389   */
4390 
4391   if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost"))
4392     rc = ldap_initialize(&TempBrowseLDAPHandle, "ldapi:///");
4393   else
4394     rc = ldap_initialize(&TempBrowseLDAPHandle, BrowseLDAPServer);
4395 
4396 #  else /* HAVE_OPENLDAP */
4397 
4398   int   ldap_port = 0;          /* LDAP port */
4399   char    ldap_protocol[11],    /* LDAP protocol */
4400           ldap_host[255];       /* LDAP host */
4401 
4402  /*
4403   * Split LDAP URI into its components...
4404   */
4405 
4406   if (!BrowseLDAPServer) {
4407     debug_printf("BrowseLDAPServer not configured!\n");
4408     debug_printf("Disabling LDAP browsing!\n");
4409     /*BrowseLocalProtocols  &= ~BROWSE_LDAP;*/
4410     BrowseRemoteProtocols &= ~BROWSE_LDAP;
4411     return (NULL);
4412   }
4413 
4414   sscanf(BrowseLDAPServer, "%10[^:]://%254[^:/]:%d", ldap_protocol, ldap_host,
4415          &ldap_port);
4416 
4417   if (!strcmp(ldap_protocol, "ldap"))
4418     ldap_ssl = 0;
4419   else if (!strcmp(ldap_protocol, "ldaps"))
4420     ldap_ssl = 1;
4421   else {
4422     debug_printf("Unrecognized LDAP protocol (%s)!\n",
4423 		 ldap_protocol);
4424     debug_printf("Disabling LDAP browsing!\n");
4425     /*BrowseLocalProtocols &= ~BROWSE_LDAP;*/
4426     BrowseRemoteProtocols &= ~BROWSE_LDAP;
4427     return (NULL);
4428   }
4429 
4430   if (ldap_port == 0) {
4431     if (ldap_ssl)
4432       ldap_port = LDAPS_PORT;
4433     else
4434       ldap_port = LDAP_PORT;
4435   }
4436 
4437   debug_printf("ldap_new_connection: PROT:%s HOST:%s PORT:%d\n",
4438 	       ldap_protocol, ldap_host, ldap_port);
4439 
4440  /*
4441   * Initialize LDAP connection...
4442   */
4443 
4444   if (!ldap_ssl) {
4445     if ((TempBrowseLDAPHandle = ldap_init(ldap_host, ldap_port)) == NULL)
4446       rc = LDAP_OPERATIONS_ERROR;
4447     else
4448       rc = LDAP_SUCCESS;
4449 
4450 #    ifdef HAVE_LDAP_SSL
4451   } else {
4452    /*
4453     * Initialize SSL LDAP connection...
4454     */
4455 
4456     if (BrowseLDAPCACertFile) {
4457       rc = ldapssl_client_init(BrowseLDAPCACertFile, (void *)NULL);
4458       if (rc != LDAP_SUCCESS) {
4459         debug_printf("Failed to initialize LDAP SSL client!\n");
4460         rc = LDAP_OPERATIONS_ERROR;
4461       } else {
4462         if ((TempBrowseLDAPHandle = ldapssl_init(ldap_host, ldap_port,
4463                                                  1)) == NULL)
4464           rc = LDAP_OPERATIONS_ERROR;
4465         else
4466           rc = LDAP_SUCCESS;
4467       }
4468     } else {
4469       debug_printf("LDAP SSL certificate file/database not configured!\n");
4470       rc = LDAP_OPERATIONS_ERROR;
4471     }
4472 
4473 #    else /* HAVE_LDAP_SSL */
4474 
4475    /*
4476     * Return error, because client libraries doesn't support SSL
4477     */
4478 
4479     debug_printf("LDAP client libraries do not support SSL\n");
4480     rc = LDAP_OPERATIONS_ERROR;
4481 
4482 #    endif /* HAVE_LDAP_SSL */
4483   }
4484 #  endif /* HAVE_OPENLDAP */
4485 
4486  /*
4487   * Check return code from LDAP initialize...
4488   */
4489 
4490   if (rc != LDAP_SUCCESS) {
4491     debug_printf("Unable to initialize LDAP!\n");
4492 
4493     if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)
4494       debug_printf("Temporarily disabling LDAP browsing...\n");
4495     else {
4496       debug_printf("Disabling LDAP browsing!\n");
4497 
4498       /*BrowseLocalProtocols  &= ~BROWSE_LDAP;*/
4499       BrowseRemoteProtocols &= ~BROWSE_LDAP;
4500     }
4501 
4502     ldap_disconnect(TempBrowseLDAPHandle);
4503 
4504     return (NULL);
4505   }
4506 
4507  /*
4508   * Upgrade LDAP version...
4509   */
4510 
4511   if (ldap_set_option(TempBrowseLDAPHandle, LDAP_OPT_PROTOCOL_VERSION,
4512 		      (const void *)&version) != LDAP_SUCCESS) {
4513     debug_printf("Unable to set LDAP protocol version %d!\n",
4514 		 version);
4515     debug_printf("Disabling LDAP browsing!\n");
4516 
4517     /*BrowseLocalProtocols  &= ~BROWSE_LDAP;*/
4518     BrowseRemoteProtocols &= ~BROWSE_LDAP;
4519     ldap_disconnect(TempBrowseLDAPHandle);
4520 
4521     return (NULL);
4522   }
4523 
4524  /*
4525   * Register LDAP rebind procedure...
4526   */
4527 
4528 #  ifdef HAVE_LDAP_REBIND_PROC
4529 #    if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
4530 
4531   rc = ldap_set_rebind_proc(TempBrowseLDAPHandle, &ldap_rebind_proc,
4532                             (void *)NULL);
4533   if (rc != LDAP_SUCCESS)
4534     debug_printf("Setting LDAP rebind function failed with status %d: %s\n",
4535 		 rc, ldap_err2string(rc));
4536 
4537 #    else
4538 
4539   ldap_set_rebind_proc(TempBrowseLDAPHandle, &ldap_rebind_proc, (void *)NULL);
4540 
4541 #    endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
4542 #  endif /* HAVE_LDAP_REBIND_PROC */
4543 
4544  /*
4545   * Start LDAP bind...
4546   */
4547 
4548 #  if LDAP_API_VERSION > 3000
4549   struct berval bval;
4550   bval.bv_val = BrowseLDAPPassword;
4551   bval.bv_len = (BrowseLDAPPassword == NULL) ? 0 : strlen(BrowseLDAPPassword);
4552 
4553   if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost"))
4554     rc = ldap_sasl_bind_s(TempBrowseLDAPHandle, NULL, "EXTERNAL", &bv, NULL,
4555                           NULL, NULL);
4556   else
4557     rc = ldap_sasl_bind_s(TempBrowseLDAPHandle, BrowseLDAPBindDN,
4558 			  LDAP_SASL_SIMPLE, &bval, NULL, NULL, NULL);
4559 
4560 #  else
4561   rc = ldap_bind_s(TempBrowseLDAPHandle, BrowseLDAPBindDN,
4562 		   BrowseLDAPPassword, LDAP_AUTH_SIMPLE);
4563 #  endif /* LDAP_API_VERSION > 3000 */
4564 
4565   if (rc != LDAP_SUCCESS) {
4566     debug_printf("LDAP bind failed with error %d: %s\n",
4567 		 rc, ldap_err2string(rc));
4568 
4569 #  if defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP)
4570     if (ldap_ssl && (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)) {
4571       ssl_err = PORT_GetError();
4572       if (ssl_err != 0)
4573         debug_printf("LDAP SSL error %d: %s\n", ssl_err,
4574 		     ldapssl_err2string(ssl_err));
4575     }
4576 #  endif /* defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) */
4577 
4578     ldap_disconnect(TempBrowseLDAPHandle);
4579 
4580     return (NULL);
4581   }
4582 
4583   debug_printf("LDAP connection established\n");
4584 
4585   return (TempBrowseLDAPHandle);
4586 }
4587 
4588 
4589 /*
4590  * 'ldap_reconnect()' - Reconnect to LDAP Server
4591  */
4592 
4593 static LDAP *       /* O - New LDAP handle */
ldap_reconnect(void)4594 ldap_reconnect(void)
4595 {
4596   LDAP  *TempBrowseLDAPHandle = NULL; /* Temp Handle to LDAP server */
4597 
4598  /*
4599   * Get a new LDAP Handle and replace the global Handle
4600   * if the new connection was successful.
4601   */
4602 
4603   debug_printf("Try LDAP reconnect...\n");
4604 
4605   TempBrowseLDAPHandle = ldap_new_connection();
4606 
4607   if (TempBrowseLDAPHandle != NULL) {
4608     if (BrowseLDAPHandle != NULL)
4609       ldap_disconnect(BrowseLDAPHandle);
4610 
4611     BrowseLDAPHandle = TempBrowseLDAPHandle;
4612   }
4613 
4614   return (BrowseLDAPHandle);
4615 }
4616 
4617 
4618 /*
4619  * 'ldap_disconnect()' - Disconnect from LDAP Server
4620  */
4621 
4622 static void
ldap_disconnect(LDAP * ld)4623 ldap_disconnect(LDAP *ld)   /* I - LDAP handle */
4624 {
4625   int rc;       /* Return code */
4626 
4627  /*
4628   * Close LDAP handle...
4629   */
4630 
4631 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4632   rc = ldap_unbind_ext_s(ld, NULL, NULL);
4633 #  else
4634   rc = ldap_unbind_s(ld);
4635 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4636 
4637   if (rc != LDAP_SUCCESS)
4638     debug_printf("Unbind from LDAP server failed with status %d: %s\n",
4639 		 rc, ldap_err2string(rc));
4640 }
4641 
4642 /*
4643  * 'cupsdUpdateLDAPBrowse()' - Scan for new printers via LDAP...
4644  */
4645 
4646 void
cupsdUpdateLDAPBrowse(void)4647 cupsdUpdateLDAPBrowse(void)
4648 {
4649   char    uri[HTTP_MAX_URI],            /* Printer URI */
4650           host[HTTP_MAX_URI],           /* Hostname */
4651           resource[HTTP_MAX_URI],       /* Resource path */
4652           local_resource[HTTP_MAX_URI], /* Resource path */
4653           service_name[4096],
4654           location[1024],               /* Printer location */
4655           info[1024],                   /* Printer information */
4656           make_model[1024],             /* Printer make and model */
4657           type_num[30],                 /* Printer type number */
4658           scheme[32],                   /* URI's scheme */
4659           username[64];                 /* URI's username */
4660   int     port;                         /* URI's port number */
4661   char    *c;
4662   int     hl;
4663   int     rc;                           /* LDAP status */
4664   int     limit;                        /* Size limit */
4665   LDAPMessage *res,                     /* LDAP search results */
4666           *e;                           /* Current entry from search */
4667 
4668   debug_printf("UpdateLDAPBrowse\n");
4669 
4670  /*
4671   * Reconnect if LDAP Handle is invalid...
4672   */
4673 
4674   if (! BrowseLDAPHandle) {
4675     ldap_reconnect();
4676     return;
4677   }
4678 
4679  /*
4680   * Search for cups printers in LDAP directory...
4681   */
4682 
4683   rc = ldap_search_rec(BrowseLDAPHandle, BrowseLDAPDN, LDAP_SCOPE_SUBTREE,
4684                        BrowseLDAPFilter, (char **)ldap_attrs, 0, &res);
4685 
4686  /*
4687   * If ldap search was successfull then exit function
4688   * and temporary disable LDAP updates...
4689   */
4690 
4691   if (rc != LDAP_SUCCESS) {
4692     if (BrowseLDAPUpdate && ((rc == LDAP_SERVER_DOWN) ||
4693 			     (rc == LDAP_CONNECT_ERROR))) {
4694       BrowseLDAPUpdate = FALSE;
4695       debug_printf("LDAP update temporary disabled\n");
4696     }
4697     return;
4698   }
4699 
4700  /*
4701   * If LDAP updates were disabled, we will reenable them...
4702   */
4703 
4704   if (!BrowseLDAPUpdate) {
4705     BrowseLDAPUpdate = TRUE;
4706     debug_printf("LDAP update enabled\n");
4707   }
4708 
4709  /*
4710   * Count LDAP entries and return if no entry exist...
4711   */
4712 
4713   limit = ldap_count_entries(BrowseLDAPHandle, res);
4714   debug_printf("LDAP search returned %d entries\n", limit);
4715   if (limit < 1) {
4716     ldap_freeres(res);
4717     return;
4718   }
4719 
4720  /*
4721   * Loop through the available printers...
4722   */
4723 
4724   for (e = ldap_first_entry(BrowseLDAPHandle, res);
4725        e;
4726        e = ldap_next_entry(BrowseLDAPHandle, e)) {
4727    /*
4728     * Get the required values from this entry...
4729     */
4730 
4731     if (ldap_getval_firststring(BrowseLDAPHandle, e,
4732                                 "printerDescription", info, sizeof(info)) == -1)
4733       continue;
4734 
4735     if (ldap_getval_firststring(BrowseLDAPHandle, e,
4736                                 "printerLocation", location,
4737 				sizeof(location)) == -1)
4738       continue;
4739 
4740     if (ldap_getval_firststring(BrowseLDAPHandle, e,
4741                                 "printerMakeAndModel", make_model,
4742 				sizeof(make_model)) == -1)
4743       continue;
4744 
4745     if (ldap_getval_firststring(BrowseLDAPHandle, e,
4746                                 "printerType", type_num,
4747 				sizeof(type_num)) == -1)
4748       continue;
4749 
4750     if (ldap_getval_firststring(BrowseLDAPHandle, e,
4751                                 "printerURI", uri, sizeof(uri)) == -1)
4752       continue;
4753 
4754    /*
4755     * Process the entry...
4756     */
4757 
4758     memset(scheme, 0, sizeof(scheme));
4759     memset(username, 0, sizeof(username));
4760     memset(host, 0, sizeof(host));
4761     memset(resource, 0, sizeof(resource));
4762     memset(local_resource, 0, sizeof(local_resource));
4763 
4764     httpSeparateURI (HTTP_URI_CODING_ALL, uri,
4765 		     scheme, sizeof(scheme) - 1,
4766 		     username, sizeof(username) - 1,
4767 		     host, sizeof(host) - 1,
4768 		     &port,
4769 		     resource, sizeof(resource)- 1);
4770 
4771     if (strncasecmp (resource, "/printers/", 10) &&
4772 	strncasecmp (resource, "/classes/", 9)) {
4773       debug_printf("don't understand URI: %s\n", uri);
4774       return;
4775     }
4776 
4777     strncpy (local_resource, resource + 1, sizeof (local_resource) - 1);
4778     local_resource[sizeof (local_resource) - 1] = '\0';
4779     c = strchr (local_resource, '?');
4780     if (c)
4781       *c = '\0';
4782 
4783     /* Build the DNS-SD service name which CUPS would give to this printer
4784        when DNS-SD-broadcasting it */
4785     snprintf(service_name, sizeof (service_name), "%s @ %s",
4786 	     (strlen(info) > 0 ? info : strchr(local_resource, '/') + 1), host);
4787     /* Cut off trailing ".local" of host name */
4788     hl = strlen(service_name);
4789     if (hl > 6 && !strcasecmp(service_name + hl - 6, ".local"))
4790       service_name[hl - 6] = '\0';
4791     if (hl > 7 && !strcasecmp(service_name + hl - 7, ".local."))
4792       service_name[hl - 7] = '\0';
4793     /* DNS-SD service name has max. 63 characters */
4794     service_name[63] = '\0';
4795 
4796     debug_printf("LDAP: Remote host: %s; Port: %d; Remote queue name: %s; Service Name: %s\n",
4797 		 host, port, strchr(local_resource, '/') + 1, service_name);
4798 
4799     examine_discovered_printer_record(host, NULL, port, local_resource,
4800 				      service_name, location, info, "", "",
4801 				      "", 0, NULL);
4802 
4803   }
4804 
4805   ldap_freeres(res);
4806 }
4807 
4808 /*
4809  * 'ldap_search_rec()' - LDAP Search with reconnect
4810  */
4811 
4812 static int        /* O - Return code */
ldap_search_rec(LDAP * ld,char * base,int scope,char * filter,char * attrs[],int attrsonly,LDAPMessage ** res)4813 ldap_search_rec(LDAP        *ld,        /* I - LDAP handler */
4814                 char        *base,      /* I - Base dn */
4815                 int         scope,      /* I - LDAP search scope */
4816                 char        *filter,    /* I - Filter string */
4817                 char        *attrs[],   /* I - Requested attributes */
4818                 int         attrsonly,  /* I - Return only attributes? */
4819                 LDAPMessage **res)      /* I - LDAP handler */
4820 {
4821   int rc;       /* Return code */
4822   LDAP  *ldr;   /* LDAP handler after reconnect */
4823 
4824 
4825 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4826   rc = ldap_search_ext_s(ld, base, scope, filter, attrs, attrsonly, NULL, NULL,
4827                          NULL, LDAP_NO_LIMIT, res);
4828 #  else
4829   rc = ldap_search_s(ld, base, scope, filter, attrs, attrsonly, res);
4830 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4831 
4832  /*
4833   * If we have a connection problem try again...
4834   */
4835 
4836   if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR) {
4837     debug_printf("LDAP search failed with status %d: %s\n",
4838 		 rc, ldap_err2string(rc));
4839     debug_printf("We try the LDAP search once again after reconnecting to "
4840 		 "the server\n");
4841     ldap_freeres(*res);
4842     ldr = ldap_reconnect();
4843 
4844 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4845     rc = ldap_search_ext_s(ldr, base, scope, filter, attrs, attrsonly, NULL,
4846                            NULL, NULL, LDAP_NO_LIMIT, res);
4847 #  else
4848     rc = ldap_search_s(ldr, base, scope, filter, attrs, attrsonly, res);
4849 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4850   }
4851 
4852   if (rc == LDAP_NO_SUCH_OBJECT)
4853     debug_printf("ldap_search_rec: LDAP entry/object not found\n");
4854   else if (rc != LDAP_SUCCESS)
4855     debug_printf("ldap_search_rec: LDAP search failed with status %d: %s\n",
4856 		 rc, ldap_err2string(rc));
4857 
4858   if (rc != LDAP_SUCCESS)
4859     ldap_freeres(*res);
4860 
4861   return (rc);
4862 }
4863 
4864 
4865 /*
4866  * 'ldap_freeres()' - Free LDAPMessage
4867  */
4868 
4869 static void
ldap_freeres(LDAPMessage * entry)4870 ldap_freeres(LDAPMessage *entry)  /* I - LDAP handler */
4871 {
4872   int rc;       /* Return value */
4873 
4874   rc = ldap_msgfree(entry);
4875   if (rc == -1)
4876     debug_printf("Can't free LDAPMessage!\n");
4877   else if (rc == 0)
4878     debug_printf("Freeing LDAPMessage was unnecessary\n");
4879 }
4880 
4881 
4882 /*
4883  * 'ldap_getval_char()' - Get first LDAP value and convert to string
4884  */
4885 
4886 static int        /* O - Return code */
ldap_getval_firststring(LDAP * ld,LDAPMessage * entry,char * attr,char * retval,unsigned long maxsize)4887 ldap_getval_firststring(LDAP          *ld,     /* I - LDAP handler */
4888 			LDAPMessage   *entry,  /* I - LDAP message or search
4889 						      result */
4890 			char          *attr,   /* I - the wanted attribute  */
4891 			char          *retval, /* O - String to return */
4892 			unsigned long maxsize) /* I - Max string size */
4893 {
4894   char            *dn;    /* LDAP DN */
4895   int             rc = 0; /* Return code */
4896 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4897   struct berval   **bval; /* LDAP value array */
4898   unsigned long   size;   /* String size */
4899 
4900 
4901  /*
4902   * Get value from LDAPMessage...
4903   */
4904 
4905   if ((bval = ldap_get_values_len(ld, entry, attr)) == NULL) {
4906     rc = -1;
4907     dn = ldap_get_dn(ld, entry);
4908     debug_printf("Failed to get LDAP value %s for %s!\n",
4909 		 attr, dn);
4910     ldap_memfree(dn);
4911   } else {
4912    /*
4913     * Check size and copy value into our string...
4914     */
4915 
4916     size = maxsize;
4917     if (size < (bval[0]->bv_len + 1)) {
4918       rc = -1;
4919       dn = ldap_get_dn(ld, entry);
4920       debug_printf("Attribute %s is too big! (dn: %s)\n",
4921 		   attr, dn);
4922       ldap_memfree(dn);
4923     } else
4924       size = bval[0]->bv_len + 1;
4925 
4926     strncpy(retval, bval[0]->bv_val, size);
4927     if (size > 0)
4928       retval[size - 1] = '\0';
4929     ldap_value_free_len(bval);
4930   }
4931 #  else
4932   char  **value;      /* LDAP value */
4933 
4934  /*
4935   * Get value from LDAPMessage...
4936   */
4937 
4938   if ((value = (char **)ldap_get_values(ld, entry, attr)) == NULL) {
4939     rc = -1;
4940     dn = ldap_get_dn(ld, entry);
4941     debug_printf("Failed to get LDAP value %s for %s!\n",
4942 		 attr, dn);
4943     ldap_memfree(dn);
4944   } else {
4945     strncpy(retval, *value, maxsize);
4946     if (maxsize > 0)
4947       retval[maxsize - 1] = '\0';
4948     ldap_value_free(value);
4949   }
4950 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4951 
4952   return (rc);
4953 }
4954 
4955 #endif /* HAVE_LDAP */
4956 
4957 
4958 static int
create_subscription()4959 create_subscription ()
4960 {
4961   ipp_t *req;
4962   ipp_t *resp;
4963   ipp_attribute_t *attr;
4964   int id = 0;
4965   http_t *conn = NULL;
4966 
4967   conn = http_connect_local ();
4968   if (conn == NULL) {
4969     debug_printf("Cannot connect to local CUPS to subscribe to notifications.\n");
4970     return 0;
4971   }
4972 
4973   req = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION);
4974   ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
4975 		"printer-uri", NULL, "/");
4976   ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
4977 		"notify-events", NULL, "all");
4978   ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
4979 		"notify-recipient-uri", NULL, "dbus://");
4980   ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
4981 		 "notify-lease-duration", notify_lease_duration);
4982 
4983   resp = cupsDoRequest (conn, req, "/");
4984   if (!resp || cupsLastError() != IPP_STATUS_OK) {
4985     debug_printf ("Error subscribing to CUPS notifications: %s\n",
4986 		  cupsLastErrorString ());
4987     return 0;
4988   }
4989 
4990   attr = ippFindAttribute (resp, "notify-subscription-id", IPP_TAG_INTEGER);
4991   if (attr)
4992     id = ippGetInteger (attr, 0);
4993   else
4994     debug_printf (""
4995 		  "ipp-create-printer-subscription response doesn't contain "
4996 		  "subscription id.\n");
4997 
4998   ippDelete (resp);
4999   return id;
5000 }
5001 
5002 
5003 static gboolean
renew_subscription(int id)5004 renew_subscription (int id)
5005 {
5006   ipp_t *req;
5007   ipp_t *resp;
5008   http_t *conn = NULL;
5009 
5010   conn = http_connect_local ();
5011   if (conn == NULL) {
5012     debug_printf("Cannot connect to local CUPS to renew subscriptions.\n");
5013     return FALSE;
5014   }
5015 
5016   req = ippNewRequest (IPP_RENEW_SUBSCRIPTION);
5017   ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
5018 		 "notify-subscription-id", id);
5019   ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
5020 		"printer-uri", NULL, "/");
5021   ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5022 		"notify-recipient-uri", NULL, "dbus://");
5023   ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5024 		 "notify-lease-duration", notify_lease_duration);
5025 
5026   resp = cupsDoRequest (conn, req, "/");
5027   if (!resp || cupsLastError() != IPP_STATUS_OK) {
5028     debug_printf ("Error renewing CUPS subscription %d: %s\n",
5029 		  id, cupsLastErrorString ());
5030     return FALSE;
5031   }
5032 
5033   ippDelete (resp);
5034   return TRUE;
5035 }
5036 
5037 
5038 static gboolean
renew_subscription_timeout(gpointer userdata)5039 renew_subscription_timeout (gpointer userdata)
5040 {
5041   int *subscription_id = userdata;
5042 
5043   debug_printf("renew_subscription_timeout() in THREAD %ld\n", pthread_self());
5044 
5045   if (*subscription_id <= 0 || !renew_subscription (*subscription_id))
5046     *subscription_id = create_subscription ();
5047 
5048   return TRUE;
5049 }
5050 
5051 
5052 void
cancel_subscription(int id)5053 cancel_subscription (int id)
5054 {
5055   ipp_t *req;
5056   ipp_t *resp;
5057   http_t *conn = NULL;
5058 
5059   conn = http_connect_local ();
5060   if (conn == NULL) {
5061     debug_printf("Cannot connect to local CUPS to cancel subscriptions.\n");
5062     return;
5063   }
5064 
5065   if (id <= 0)
5066     return;
5067 
5068   req = ippNewRequest (IPP_CANCEL_SUBSCRIPTION);
5069   ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
5070 		"printer-uri", NULL, "/");
5071   ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
5072 		 "notify-subscription-id", id);
5073 
5074   resp = cupsDoRequest (conn, req, "/");
5075   if (!resp || cupsLastError() != IPP_STATUS_OK) {
5076     debug_printf ("Error subscribing to CUPS notifications: %s\n",
5077 		  cupsLastErrorString ());
5078     return;
5079   }
5080 
5081   ippDelete (resp);
5082 }
5083 
5084 int
is_created_by_cups_browsed(const char * printer)5085 is_created_by_cups_browsed (const char *printer) {
5086   remote_printer_t *p;
5087 
5088   if (printer == NULL)
5089     return 0;
5090   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
5091        p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
5092     if (!p->slave_of && !strcasecmp(printer, p->queue_name))
5093       return 1;
5094 
5095   return 0;
5096 }
5097 
5098 remote_printer_t *
printer_record(const char * printer)5099 printer_record (const char *printer) {
5100   remote_printer_t *p;
5101 
5102   if (printer == NULL)
5103     return NULL;
5104   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
5105        p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
5106     if (!p->slave_of && !strcasecmp(printer, p->queue_name))
5107       return p;
5108 
5109   return NULL;
5110 }
5111 
5112 void
log_cluster(remote_printer_t * p)5113 log_cluster(remote_printer_t *p) {
5114   remote_printer_t *q, *r;
5115   int i;
5116   if (p == NULL || (!debug_stderr && !debug_logfile))
5117     return;
5118   if (p->slave_of)
5119     q = p->slave_of;
5120   else
5121     q = p;
5122   if (q->queue_name == NULL)
5123     return;
5124   debug_printf("Remote CUPS printers clustered as queue %s:\n", q->queue_name);
5125   for (r = (remote_printer_t *)cupsArrayFirst(remote_printers), i = 0;
5126        r; r = (remote_printer_t *)cupsArrayNext(remote_printers), i ++)
5127     if (r->status != STATUS_DISAPPEARED && r->status != STATUS_UNCONFIRMED &&
5128 	r->status != STATUS_TO_BE_RELEASED &&
5129 	(r == q || r->slave_of == q))
5130       debug_printf("  %s%s%s\n", r->uri,
5131 		   (r == q ? "*" : ""),
5132 		   (i == q->last_printer ? " (last job printed)" : ""));
5133 }
5134 
5135 void
log_all_printers()5136 log_all_printers() {
5137   remote_printer_t *p, *q;
5138   if (!debug_stderr && !debug_logfile)
5139     return;
5140   debug_printf("=== Remote printer overview ===\n");
5141   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
5142        p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
5143     debug_printf("Printer %s (%s, %s): Local queue %s, %s, Slave of %s%s\n",
5144 		 p->uri,
5145 		 p->host, (p->ip ? p->ip : "IP not determined"), p->queue_name,
5146 		 (p->netprinter ? "IPP Printer" : "Remote CUPS Printer"),
5147 		 ((q = p->slave_of) != NULL ?
5148 		  (q->uri ? q->uri : "Deleted Printer") : "None"),
5149 		 (p->status == STATUS_UNCONFIRMED ? " (Unconfirmed)" :
5150 		  (p->status == STATUS_DISAPPEARED ? " (Disappeared)" :
5151 		   (p->status == STATUS_TO_BE_RELEASED ?
5152 		    " (To be released from cups-browsed)" :
5153 		    (p->status == STATUS_TO_BE_CREATED ?
5154 		     " (To be created/updated)" : "")))));
5155   debug_printf("===============================\n");
5156 }
5157 
5158 char*
is_disabled(const char * printer,const char * reason)5159 is_disabled(const char *printer, const char *reason) {
5160   ipp_t *request, *response;
5161   ipp_attribute_t *attr;
5162   const char *pname = NULL;
5163   ipp_pstate_t pstate = IPP_PRINTER_IDLE;
5164   const char *p;
5165   char *pstatemsg = NULL;
5166   static const char *pattrs[] =
5167                 {
5168                   "printer-name",
5169                   "printer-state",
5170 		  "printer-state-message"
5171                 };
5172   http_t *conn = NULL;
5173 
5174   conn = http_connect_local ();
5175   if (conn == NULL) {
5176     debug_printf("Cannot connect to local CUPS to check whether the printer %s is disabled.\n",
5177 		 printer);
5178     return NULL;
5179   }
5180 
5181   request = ippNewRequest(CUPS_GET_PRINTERS);
5182   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
5183 		"requested-attributes",
5184 		sizeof(pattrs) / sizeof(pattrs[0]),
5185 		NULL, pattrs);
5186   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
5187 	       "requesting-user-name",
5188 	       NULL, cupsUser());
5189   if ((response = cupsDoRequest(conn, request, "/")) != NULL) {
5190     for (attr = ippFirstAttribute(response); attr != NULL;
5191 	 attr = ippNextAttribute(response)) {
5192       while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
5193 	attr = ippNextAttribute(response);
5194       if (attr == NULL)
5195 	break;
5196       pname = NULL;
5197       pstate = IPP_PRINTER_IDLE;
5198       if (pstatemsg) {
5199 	free(pstatemsg);
5200 	pstatemsg = NULL;
5201       }
5202       while (attr != NULL && ippGetGroupTag(attr) ==
5203 	     IPP_TAG_PRINTER) {
5204 	if (!strcmp(ippGetName(attr), "printer-name") &&
5205 	    ippGetValueTag(attr) == IPP_TAG_NAME)
5206 	  pname = ippGetString(attr, 0, NULL);
5207 	else if (!strcmp(ippGetName(attr), "printer-state") &&
5208 		 ippGetValueTag(attr) == IPP_TAG_ENUM)
5209 	  pstate = (ipp_pstate_t)ippGetInteger(attr, 0);
5210 	else if (!strcmp(ippGetName(attr), "printer-state-message") &&
5211 		 ippGetValueTag(attr) == IPP_TAG_TEXT) {
5212 	  if (pstatemsg != NULL) {
5213 	    free(pstatemsg);
5214 	    pstatemsg = NULL;
5215 	  }
5216 	  p = ippGetString(attr, 0, NULL);
5217 	  if (p != NULL) pstatemsg = strdup(p);
5218 	}
5219 	attr = ippNextAttribute(response);
5220       }
5221       if (pname == NULL) {
5222 	if (attr == NULL)
5223 	  break;
5224 	else
5225 	  continue;
5226       }
5227       if (!strcasecmp(pname, printer)) {
5228 	switch (pstate) {
5229 	case IPP_PRINTER_IDLE:
5230 	case IPP_PRINTER_PROCESSING:
5231 	  ippDelete(response);
5232 	  if (pstatemsg != NULL) {
5233 	    free(pstatemsg);
5234 	    pstatemsg = NULL;
5235 	  }
5236 	  return NULL;
5237 	case IPP_PRINTER_STOPPED:
5238 	  ippDelete(response);
5239 	  if (reason == NULL)
5240 	    return pstatemsg;
5241 	  else if (pstatemsg != NULL && (strcasestr(pstatemsg, reason) != NULL))
5242 	    return pstatemsg;
5243 	  else {
5244             if (pstatemsg != NULL) {
5245 	      free(pstatemsg);
5246 	      pstatemsg = NULL;
5247             }
5248 	    return NULL;
5249 	  }
5250 	}
5251       }
5252     }
5253     debug_printf("No information regarding enabled/disabled found about the requested printer '%s'\n",
5254 		 printer);
5255     ippDelete(response);
5256     if (pstatemsg != NULL) {
5257       free(pstatemsg);
5258       pstatemsg = NULL;
5259     }
5260     return NULL;
5261   }
5262   debug_printf("ERROR: Request for printer info failed: %s\n",
5263 	       cupsLastErrorString());
5264   if (pstatemsg != NULL) {
5265     free(pstatemsg);
5266     pstatemsg = NULL;
5267   }
5268   return NULL;
5269 }
5270 
5271 int
enable_printer(const char * printer)5272 enable_printer (const char *printer) {
5273   ipp_t *request;
5274   char uri[HTTP_MAX_URI];
5275   http_t *conn = NULL;
5276 
5277   conn = http_connect_local ();
5278   if (conn == NULL) {
5279     debug_printf("Cannot connect to local CUPS to enable printer %s.\n",
5280 		 printer);
5281     return -1;
5282   }
5283 
5284   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
5285 		   "localhost", 0, "/printers/%s", printer);
5286   request = ippNewRequest (IPP_RESUME_PRINTER);
5287   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
5288 		"printer-uri", NULL, uri);
5289   ippDelete(cupsDoRequest (conn, request, "/admin/"));
5290   if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
5291     debug_printf("ERROR: Failed enabling printer '%s': %s\n",
5292 		 printer, cupsLastErrorString());
5293     return -1;
5294   }
5295   debug_printf("Enabled printer '%s'\n", printer);
5296   return 0;
5297 }
5298 
5299 int
disable_printer(const char * printer,const char * reason)5300 disable_printer (const char *printer, const char *reason) {
5301   ipp_t *request;
5302   char uri[HTTP_MAX_URI];
5303   http_t *conn = NULL;
5304 
5305   conn = http_connect_local ();
5306   if (conn == NULL) {
5307     debug_printf("Cannot connect to local CUPS to disable printer %s.\n",
5308 		 printer);
5309     return -1;
5310   }
5311 
5312   if (reason == NULL)
5313     reason = "Disabled by cups-browsed";
5314   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
5315 		   "localhost", 0, "/printers/%s", printer);
5316   request = ippNewRequest (IPP_PAUSE_PRINTER);
5317   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
5318 		"printer-uri", NULL, uri);
5319   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
5320 		"printer-state-message", NULL, reason);
5321   ippDelete(cupsDoRequest (conn, request, "/admin/"));
5322   if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
5323     debug_printf("ERROR: Failed disabling printer '%s': %s\n",
5324 		 printer, cupsLastErrorString());
5325     return -1;
5326   }
5327   debug_printf("Disabled printer '%s'\n", printer);
5328   return 0;
5329 }
5330 
5331 int
set_cups_default_printer(const char * printer)5332 set_cups_default_printer(const char *printer) {
5333   ipp_t *request;
5334   char uri[HTTP_MAX_URI];
5335   http_t *conn = NULL;
5336 
5337   conn = http_connect_local ();
5338   if (conn == NULL) {
5339     debug_printf("Cannot connect to local CUPS to subscribe to set printer %s as default printer.\n",
5340 		 printer);
5341     return -1;
5342   }
5343 
5344   if (printer == NULL)
5345     return 0;
5346   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
5347                    "localhost", 0, "/printers/%s", printer);
5348   request = ippNewRequest(IPP_OP_CUPS_SET_DEFAULT);
5349   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
5350                "printer-uri", NULL, uri);
5351   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
5352                NULL, cupsUser());
5353   ippDelete(cupsDoRequest(conn, request, "/admin/"));
5354   if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
5355     debug_printf("ERROR: Failed setting CUPS default printer to '%s': %s\n",
5356 		 printer, cupsLastErrorString());
5357     return -1;
5358   }
5359   debug_printf("Successfully set CUPS default printer to '%s'\n",
5360 	       printer);
5361   return 0;
5362 }
5363 
5364 char*
get_cups_default_printer()5365 get_cups_default_printer() {
5366   ipp_t *request, *response;
5367   ipp_attribute_t *attr;
5368   const char *default_printer_name = NULL;
5369   char *name_string;
5370   http_t *conn = NULL;
5371 
5372   conn = http_connect_local ();
5373   if (conn == NULL) {
5374     debug_printf("Cannot connect to local CUPS to find out which is the default printer.\n");
5375     return NULL;
5376   }
5377 
5378   request = ippNewRequest(CUPS_GET_DEFAULT);
5379   /* Default user */
5380   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
5381 	       "requesting-user-name", NULL, cupsUser());
5382   /* Do it */
5383   response = cupsDoRequest(conn, request, "/");
5384   if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE || !response) {
5385     debug_printf("Could not determine system default printer!\n");
5386   } else {
5387     for (attr = ippFirstAttribute(response); attr != NULL;
5388 	 attr = ippNextAttribute(response)) {
5389       while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
5390 	attr = ippNextAttribute(response);
5391       if (attr) {
5392 	for (; attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER;
5393 	     attr = ippNextAttribute(response)) {
5394 	  if (!strcasecmp(ippGetName(attr), "printer-name") &&
5395 	      ippGetValueTag(attr) == IPP_TAG_NAME) {
5396 	    default_printer_name = ippGetString(attr, 0, NULL);
5397 	    break;
5398 	  }
5399 	}
5400       }
5401       if (default_printer_name)
5402 	break;
5403     }
5404   }
5405 
5406   if (default_printer_name != NULL) {
5407     name_string = strdup(default_printer_name);
5408   } else {
5409     name_string = NULL;
5410   }
5411 
5412   ippDelete(response);
5413 
5414   return name_string;
5415 }
5416 
5417 int
is_cups_default_printer(const char * printer)5418 is_cups_default_printer(const char *printer) {
5419   if (printer == NULL)
5420     return 0;
5421   char *cups_default = get_cups_default_printer();
5422   if (cups_default == NULL)
5423     return 0;
5424   if (!strcasecmp(printer, cups_default)) {
5425     free(cups_default);
5426     return 1;
5427   }
5428   free(cups_default);
5429   return 0;
5430 }
5431 
5432 int
invalidate_default_printer(int local)5433 invalidate_default_printer(int local) {
5434   const char *filename = local ? local_default_printer_file :
5435     remote_default_printer_file;
5436   unlink(filename);
5437   return 0;
5438 }
5439 
5440 int
record_default_printer(const char * printer,int local)5441 record_default_printer(const char *printer, int local) {
5442   FILE *fp = NULL;
5443   const char *filename = local ? local_default_printer_file :
5444     remote_default_printer_file;
5445 
5446   if (printer == NULL || strlen(printer) == 0)
5447     return invalidate_default_printer(local);
5448 
5449   fp = fopen(filename, "w+");
5450   if (fp == NULL) {
5451     debug_printf("ERROR: Failed creating file %s\n",
5452 		 filename);
5453     invalidate_default_printer(local);
5454     return -1;
5455   }
5456   fprintf(fp, "%s", printer);
5457   fclose(fp);
5458 
5459   return 0;
5460 }
5461 
5462 char*
retrieve_default_printer(int local)5463 retrieve_default_printer(int local) {
5464   FILE *fp = NULL;
5465   const char *filename = local ? local_default_printer_file :
5466     remote_default_printer_file;
5467   const char *printer = NULL;
5468   char *p, buf[1024];
5469   int n;
5470 
5471   fp = fopen(filename, "r");
5472   if (fp == NULL) {
5473     debug_printf("Failed reading file %s\n",
5474 		 filename);
5475     return NULL;
5476   }
5477   p = buf;
5478   n = fscanf(fp, "%s", p);
5479   if (n == 1) {
5480     if (strlen(p) > 0)
5481       printer = p;
5482   }
5483   fclose(fp);
5484 
5485   return (printer ? strdup(printer) : NULL);
5486 }
5487 
5488 int
invalidate_printer_options(const char * printer)5489 invalidate_printer_options(const char *printer) {
5490   char filename[1024];
5491 
5492   snprintf(filename, sizeof(filename), save_options_file,
5493 	   printer);
5494   unlink(filename);
5495   return 0;
5496 }
5497 
5498 char*
loadPPD(http_t * http,const char * name)5499 loadPPD(http_t *http,
5500 	const char *name)
5501 {
5502   /* This function replaces cupsGetPPD2(), but is much simplified
5503      (does not support classes) and works with non-standard (!= 631)
5504      ports */
5505 
5506   char uri[HTTP_MAX_URI];
5507   char *resource;
5508   int fd, status;
5509   char tempfile[1024] = "";
5510 
5511   /* Download URI and resource for the PPD file */
5512   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "http", NULL,
5513 		   "localhost", 0, "/printers/%s.ppd", name);
5514   resource = strstr(uri, "/printers/");
5515 
5516   /* Download the file */
5517   fd = cupsTempFd(tempfile, sizeof(tempfile));
5518   status = cupsGetFd(http, resource, fd);
5519   close(fd);
5520 
5521   /* Check for errors */
5522   if (status == HTTP_STATUS_OK)
5523   {
5524     if (tempfile[0])
5525       return(strdup(tempfile));
5526   }
5527   else if (tempfile[0])
5528     unlink(tempfile);
5529   return NULL;
5530 }
5531 
5532 int
record_printer_options(const char * printer)5533 record_printer_options(const char *printer) {
5534   remote_printer_t *p;
5535   char filename[1024];
5536   FILE *fp = NULL;
5537   char uri[HTTP_MAX_URI], *resource;
5538   ipp_t *request, *response;
5539   ipp_attribute_t *attr;
5540   const char *key;
5541   char buf[65536], *c;
5542   char *ppdname = NULL;
5543   ppd_file_t *ppd;
5544   ppd_option_t *ppd_opt;
5545   cups_option_t *option;
5546   int i;
5547   /* List of IPP attributes to get recorded */
5548   static const char *attrs_to_record[] =
5549     {
5550      "*-default",
5551      "auth-info-required",
5552      /*"device-uri",*/
5553      "job-quota-period",
5554      "job-k-limit",
5555      "job-page-limit",
5556      /*"port-monitor",*/
5557      "printer-error-policy",
5558      "printer-info",
5559      "printer-is-accepting-jobs",
5560      "printer-is-shared",
5561      "printer-geo-location",
5562      "printer-location",
5563      "printer-op-policy",
5564      "printer-organization",
5565      "printer-organizational-unit",
5566      /*"printer-state",
5567        "printer-state-message",
5568        "printer-state-reasons",*/
5569      "requesting-user-name-allowed",
5570      "requesting-user-name-denied",
5571      NULL
5572     };
5573   const char **ptr;
5574   http_t *conn = NULL;
5575 
5576   if (printer == NULL || strlen(printer) == 0)
5577     return 0;
5578 
5579   /* Get our data about this printer */
5580   p = printer_record(printer);
5581 
5582   if (p == NULL) {
5583     debug_printf("Not recording printer options for %s: Unknown printer!\n",
5584 		 printer);
5585     return 0;
5586   }
5587 
5588   if (p->status == STATUS_TO_BE_RELEASED) {
5589     debug_printf("Not recording printer options for externally modified printer %s.\n",
5590 		 printer);
5591     return 0;
5592   }
5593 
5594   snprintf(filename, sizeof(filename), save_options_file,
5595 	   printer);
5596 
5597   debug_printf("Recording printer options for %s to %s\n",
5598 	       printer, filename);
5599 
5600   conn = http_connect_local ();
5601   if (conn) {
5602     /* If there is a PPD file for this printer, we save the local
5603        settings for the PPD options. */
5604     if (cups_notifier != NULL || (p && p->netprinter)) {
5605       if ((ppdname = loadPPD(conn, printer)) == NULL) {
5606 	debug_printf("Unable to get PPD file for %s: %s\n",
5607 		     printer, cupsLastErrorString());
5608       } else if ((ppd = ppdOpenFile(ppdname)) == NULL) {
5609 	unlink(ppdname);
5610 	debug_printf("Unable to open PPD file for %s.\n",
5611 		     printer);
5612       } else {
5613 	debug_printf("Recording option settings of the PPD file for %s (%s):\n",
5614 		     printer, ppd->nickname);
5615 	ppdMarkDefaults(ppd);
5616 	for (ppd_opt = ppdFirstOption(ppd); ppd_opt;
5617 	     ppd_opt = ppdNextOption(ppd))
5618 	  if (strcasecmp(ppd_opt->keyword, "PageRegion") != 0) {
5619 	    debug_printf("   %s=%s\n",
5620 			 ppd_opt->keyword, ppd_opt->defchoice);
5621 	    strncpy(buf, ppd_opt->keyword, sizeof(buf));
5622 	    p->num_options = cupsAddOption(buf, ppd_opt->defchoice,
5623 					   p->num_options, &(p->options));
5624 	  }
5625 	ppdClose(ppd);
5626 	unlink(ppdname);
5627       }
5628     }
5629 
5630     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
5631 		     "localhost", 0, "/printers/%s", printer);
5632     resource = uri + (strlen(uri) - strlen(printer) - 10);
5633     request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
5634     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
5635 		 uri);
5636     response = cupsDoRequest(conn, request, resource);
5637 
5638     /* Write all supported printer attributes */
5639     if (response) {
5640       debug_printf("Recording option settings from the IPP attributes for %s:\n",
5641 		   printer);
5642       attr = ippFirstAttribute(response);
5643       while (attr) {
5644 	key = ippGetName(attr);
5645 	for (ptr = attrs_to_record; *ptr; ptr++)
5646 	  if (strcasecmp(key, *ptr) == 0 ||
5647 	      (*ptr[0] == '*' &&
5648 	       strcasecmp(key + strlen(key) - strlen(*ptr) + 1, *ptr + 1) == 0))
5649 	    break;
5650 	if (*ptr != NULL) {
5651 	  if (strcasecmp(key, CUPS_BROWSED_DEST_PRINTER "-default") != 0) {
5652 	    ippAttributeString(attr, buf, sizeof(buf));
5653 	    buf[sizeof(buf) - 1] = '\0';
5654 	    c = buf;
5655 	    while (*c) {
5656 	      if (*c == '\\')
5657 		memmove(c, c + 1, strlen(c));
5658 	      if (*c) c ++;
5659 	    }
5660 	    debug_printf("   %s=%s\n", key, buf);
5661 	    p->num_options = cupsAddOption(key, buf, p->num_options,
5662 					   &(p->options));
5663 	  }
5664 	}
5665 	attr = ippNextAttribute(response);
5666       }
5667       ippDelete(response);
5668     }
5669   } else {
5670     debug_printf("Cannot connect to local CUPS to read out the IPP and PPD attributes for printer %s.\n",
5671 		 printer);
5672   }
5673 
5674   if (ppdname)
5675     free(ppdname);
5676 
5677   if (p->num_options > 0) {
5678     fp = fopen(filename, "w+");
5679     if (fp == NULL) {
5680       debug_printf("ERROR: Failed creating file %s: %s\n",
5681 		   filename, strerror(errno));
5682       return -1;
5683     }
5684 
5685     for (i = p->num_options, option = p->options; i > 0; i --, option ++)
5686       if (fprintf (fp, "%s=%s\n", option->name, option->value) < 0) {
5687 	debug_printf("ERROR: Failed to write into file %s: %s\n",
5688 		     filename, strerror(errno));
5689 	fclose(fp);
5690 	return -1;
5691       }
5692 
5693     fclose(fp);
5694 
5695     return 0;
5696   } else
5697     return -1;
5698 }
5699 
5700 int
load_printer_options(const char * printer,int num_options,cups_option_t ** options)5701 load_printer_options(const char *printer, int num_options,
5702 		     cups_option_t **options) {
5703   char filename[1024];
5704   FILE *fp = NULL;
5705   char *opt = NULL, *val;
5706   size_t optlen = 0;
5707 
5708   if (printer == NULL || strlen(printer) == 0 || options == NULL)
5709     return 0;
5710 
5711   /* Prepare reading file with saved option settings */
5712   snprintf(filename, sizeof(filename), save_options_file,
5713 	   printer);
5714 
5715   debug_printf("Loading saved printer options for %s from %s\n",
5716 	       printer, filename);
5717 
5718   /* Open the file with the saved option settings for this print queue */
5719   fp = fopen(filename, "r");
5720   if (fp == NULL) {
5721     debug_printf("Failed reading file %s, probably no options recorded yet\n",
5722 		 filename);
5723   } else {
5724     /* Now read the lines of the file and add each setting to our request */
5725     errno = 0;
5726     debug_printf("Loading following option settings for printer %s:\n",
5727 		 printer);
5728     while (getline(&opt, &optlen, fp) != -1) {
5729       if (strlen(opt) > 1 && (val = strchr(opt, '=')) != NULL) {
5730 	*val = '\0';
5731 	val ++;
5732 	val[strlen(val)-1] = '\0';
5733 	debug_printf("   %s=%s\n", opt, val);
5734 	num_options = cupsAddOption(opt, val, num_options, options);
5735       }
5736     }
5737     debug_printf("\n");
5738     if (errno != 0)
5739       debug_printf("Failed reading saved options file %s: %s\n",
5740 		   filename, strerror(errno));
5741     free(opt);
5742     fclose(fp);
5743   }
5744   return (num_options);
5745 }
5746 
5747 int
queue_creation_handle_default(const char * printer)5748 queue_creation_handle_default(const char *printer) {
5749   /* No default printer management if we cannot get D-Bus notifications
5750      from CUPS */
5751   if (cups_notifier == NULL)
5752     return 0;
5753   /* If this queue is recorded as the former default queue (and the current
5754      default is local), set it as default (the CUPS notification handler
5755      will record the local default printer then) */
5756   char *recorded_default = retrieve_default_printer(0);
5757   if (recorded_default == NULL || strcasecmp(recorded_default, printer)) {
5758     if (recorded_default) free(recorded_default);
5759     return 0;
5760   }
5761   free(recorded_default);
5762   char *current_default = get_cups_default_printer();
5763   if (current_default == NULL || !is_created_by_cups_browsed(current_default)) {
5764     if (set_cups_default_printer(printer) < 0) {
5765       debug_printf("ERROR: Could not set former default printer %s as default again.\n",
5766 		   printer);
5767       free(current_default);
5768       return -1;
5769     } else {
5770       debug_printf("Former default printer %s re-appeared, set as default again.\n",
5771 		   printer);
5772       invalidate_default_printer(0);
5773     }
5774   }
5775   free(current_default);
5776   return 0;
5777 }
5778 
5779 int
queue_removal_handle_default(const char * printer)5780 queue_removal_handle_default(const char *printer) {
5781   /* No default printer management if we cannot get D-Bus notifications
5782      from CUPS */
5783   if (cups_notifier == NULL)
5784     return 0;
5785   /* If the queue is the default printer, get back
5786      to the recorded local default printer, record this queue for getting the
5787      default set to this queue again if it re-appears. */
5788   /* We call this also if a queue is only conserved because on cups-browsed
5789      shutdown it still has jobs */
5790   if (!is_cups_default_printer(printer))
5791     return 0;
5792   /* Record the fact that this printer was default */
5793   if (record_default_printer(default_printer, 0) < 0) {
5794     /* Delete record file if recording failed */
5795     debug_printf("ERROR: Failed recording remote default printer (%s). Removing the file with possible old recording.\n",
5796 		 printer);
5797     invalidate_default_printer(0);
5798   } else
5799     debug_printf("Recorded the fact that the current printer (%s) is the default printer before deleting the queue and returning to the local default printer.\n",
5800 		 printer);
5801   /* Switch back to a recorded local printer, if available */
5802   char *local_default = retrieve_default_printer(1);
5803   if (local_default != NULL) {
5804     if (set_cups_default_printer(local_default) >= 0) {
5805       debug_printf("Switching back to %s as default printer.\n",
5806 		   local_default);
5807       free(local_default);
5808     } else {
5809       debug_printf("ERROR: Unable to switch back to %s as default printer.\n",
5810 		   local_default);
5811       free(local_default);
5812       return -1;
5813     }
5814   }
5815   invalidate_default_printer(1);
5816   return 0;
5817 }
5818 
5819 static char *
get_local_queue_name(const char * service_name,const char * make_model,const char * resource,const char * remote_host,int * is_cups_queue,const char * exclude)5820 get_local_queue_name(const char *service_name,
5821 		     const char *make_model,
5822 		     const char *resource,
5823 		     const char *remote_host,
5824 		     int *is_cups_queue,
5825 		     const char *exclude) {
5826   char *queue_name = NULL, *backup_queue_name = NULL,
5827     *local_queue_name = NULL, *local_queue_name_lower = NULL;
5828   local_printer_t *local_printer = NULL;
5829   cluster_t *cluster = NULL;
5830   char *member = NULL, *str = NULL;
5831 
5832   if (*is_cups_queue) {
5833     /* This is a remote CUPS printer */
5834     /* Determine the queue name */
5835     if (LocalQueueNamingRemoteCUPS == LOCAL_QUEUE_NAMING_MAKE_MODEL &&
5836 	make_model)
5837       /* Works only with DNS-SD-discovered queues as otherwise we have no
5838 	 make/model info */
5839       queue_name = remove_bad_chars(make_model, 0);
5840     else if (LocalQueueNamingRemoteCUPS == LOCAL_QUEUE_NAMING_REMOTE_NAME)
5841       /* Not directly used in script generation input later, but taken from
5842 	 packet, so better safe than sorry. (consider second loop with
5843 	 backup_queue_name) */
5844       queue_name = remove_bad_chars(strrchr(resource, '/') + 1, 0);
5845     else
5846       /* Convert DNS-SD service name into a CUPS queue name exactly
5847 	 as CUPS would do it, to override CUPS' own temporary queue
5848 	 generation mechanism */
5849       queue_name = remove_bad_chars(service_name, 2);
5850   } else {
5851     /* This is an IPP-based network printer */
5852     /* Determine the queue name */
5853     if (LocalQueueNamingIPPPrinter == LOCAL_QUEUE_NAMING_MAKE_MODEL &&
5854 	make_model)
5855       /* Works only if we actually have make/model info in the DNS-SD record*/
5856       queue_name = remove_bad_chars(make_model, 0);
5857     else
5858       /* Convert DNS-SD service name into a CUPS queue name exactly
5859 	 as CUPS would do it, to override CUPS' own temporary queue
5860 	 generation mechanism */
5861       queue_name = remove_bad_chars(service_name, 2);
5862   }
5863   /* Check if there exists already a CUPS queue with the
5864      requested name Try name@host in such a case and if
5865      this is also taken, ignore the printer */
5866 
5867   /* Get available CUPS queues */
5868   update_local_printers ();
5869 
5870   /* We skip trying to use the queue name purely derived from the
5871      remote CUPS queue name or make and model for remote CUPS queues
5872      when automatic clustering of remote CUPS queues is turned off,
5873      to directly create queues with names containing the server name
5874      to avoid name clashes and with this remote queues skipped by
5875      cups-browsed. */
5876   if ((!*is_cups_queue ||
5877        AutoClustering == 1 ||
5878        LocalQueueNamingRemoteCUPS == LOCAL_QUEUE_NAMING_DNSSD) &&
5879       (!exclude || strcasecmp(queue_name, exclude))) {
5880     /* Is there a local queue with the name of the remote queue? */
5881     local_queue_name_lower = g_ascii_strdown(queue_name, -1);
5882     local_printer = g_hash_table_lookup (local_printers,
5883 					 local_queue_name_lower);
5884     free(local_queue_name_lower);
5885     /* To decide on whether the queue name is already taken, only
5886        consider CUPS queues not created by us. */
5887     if (local_printer && !local_printer->cups_browsed_controlled) {
5888       debug_printf("Queue name %s already taken.\n",
5889 		   queue_name);
5890       local_queue_name = NULL;
5891     } else
5892       local_queue_name = strdup(queue_name);
5893   }
5894   /* Use the originally chosen queue name plus the server name if the
5895      original name is already taken or if we had skipped using it. Do
5896      this only if we do not use DNS-SD-service-name-based naming. */
5897   if (!local_queue_name &&
5898       (!*is_cups_queue ||
5899        LocalQueueNamingRemoteCUPS != LOCAL_QUEUE_NAMING_DNSSD) &&
5900       (is_cups_queue ||
5901        LocalQueueNamingIPPPrinter != LOCAL_QUEUE_NAMING_DNSSD)) {
5902     if ((backup_queue_name = malloc((strlen(queue_name) +
5903 				     strlen(remote_host) + 2) *
5904 				    sizeof(char))) == NULL) {
5905       debug_printf("ERROR: Unable to allocate memory.\n");
5906       exit(1);
5907     }
5908     sprintf(backup_queue_name, "%s@%s", queue_name, remote_host);
5909     local_queue_name = backup_queue_name;
5910     debug_printf("Using fallback queue name: %s\n",
5911 		 local_queue_name);
5912     /* Is there a local queue with the name <queue>@<host>? */
5913     local_queue_name_lower = g_ascii_strdown(local_queue_name, -1);
5914     local_printer = g_hash_table_lookup (local_printers,
5915 					 local_queue_name_lower);
5916     free(local_queue_name_lower);
5917     if ((local_printer && !local_printer->cups_browsed_controlled) ||
5918 	(exclude && !strcasecmp(local_queue_name, exclude))) {
5919       /* Found also a local queue with name <queue>@<host> (or
5920 	 this name is explicitly excluded), so ignore this remote
5921 	 printer */
5922       debug_printf("%s also taken, printer ignored.\n",
5923 		   local_queue_name);
5924       free(backup_queue_name);
5925       local_queue_name = NULL;
5926     }
5927   }
5928   free(queue_name);
5929   if (!local_queue_name) {
5930     debug_printf("No suitable local queue name found, printer ignored.\n");
5931     return NULL;
5932   }
5933 
5934   /* Check whether our new printer matches one of the user-defined
5935      printer clusters */
5936   for (cluster = cupsArrayFirst(clusters);
5937        cluster;
5938        cluster = cupsArrayNext(clusters)) {
5939     if (exclude && !strcasecmp(cluster->local_queue_name, exclude))
5940       continue;
5941     local_queue_name_lower = g_ascii_strdown(cluster->local_queue_name, -1);
5942     local_printer = g_hash_table_lookup (local_printers,
5943 					 local_queue_name_lower);
5944     free(local_queue_name_lower);
5945     if (local_printer && !local_printer->cups_browsed_controlled)
5946       continue;
5947     for (member = cupsArrayFirst(cluster->members);
5948 	 member;
5949 	 member = cupsArrayNext(cluster->members)) {
5950       /* Match remote CUPS queue name */
5951       if ((str = strrchr(resource, '/')) != NULL && strlen(str) > 1) {
5952 	str = remove_bad_chars(str + 1, 2);
5953 	if (strcasecmp(member, str) == 0) /* Match */
5954 	  break;
5955 	free(str);
5956       }
5957       /* Match make and model */
5958       if (make_model) {
5959 	str = remove_bad_chars(make_model, 2);
5960 	if (strcasecmp(member, str) == 0) /* Match */
5961 	  break;
5962 	free(str);
5963       }
5964       /* Match DNS-SD service name */
5965       if (service_name) {
5966 	str = remove_bad_chars(service_name, 2);
5967 	if (strcasecmp(member, str) == 0) /* Match */
5968 	  break;
5969 	free(str);
5970       }
5971     }
5972     if (member)
5973       break;
5974   }
5975   if (cluster) {
5976     local_queue_name = strdup(cluster->local_queue_name);
5977     *is_cups_queue = 2;
5978     free(str);
5979   } else if (AutoClustering) {
5980     /* If we do automatic clustering by matching queue names, do not
5981        add a queue to a manually defined cluster because it matches
5982        the cluster's local queue name. Manually defined clusters can
5983        only be joined by printers which match one of the cluster's
5984        member names */
5985     for (cluster = cupsArrayFirst(clusters);
5986 	 cluster;
5987 	 cluster = cupsArrayNext(clusters)) {
5988       if (strcasecmp(local_queue_name, cluster->local_queue_name) == 0) {
5989 	debug_printf("We have already a manually defined printer cluster with the name %s. Automatic clustering does not add this printer to this cluster as it does not match any of the cluster's member names. Skipping this printer.\n",
5990 		     local_queue_name);
5991 	debug_printf("In cups-browsed.conf try \"LocalQueueNamingRemoteCUPS DNS-SD\" or give another name to your manually defined cluster (\"Cluster\" directive) to avoid name clashes.\n");
5992 	free(local_queue_name);
5993 	return NULL;
5994       }
5995     }
5996   }
5997   return local_queue_name;
5998 }
5999 
6000 int
join_cluster_if_needed(remote_printer_t * p,int is_cups_queue)6001 join_cluster_if_needed(remote_printer_t *p,
6002 		       int is_cups_queue) {
6003 
6004   /* is_cups_queue: -1: Unknown, 0: IPP printer, 1: Remote CUPS queue,
6005      2: Remote CUPS queue in user-defined cluster      */
6006 
6007   remote_printer_t *q;
6008 
6009   for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
6010        q;
6011        q = (remote_printer_t *)cupsArrayNext(remote_printers))
6012     if (q != p &&
6013 	!strcasecmp(q->queue_name, p->queue_name) && /* Queue with same name
6014 							on server */
6015 	!q->slave_of) /* Find the master of the queues with this name,
6016 			 to avoid "daisy chaining" */
6017       break;
6018   if (q && AutoClustering == 0 && (is_cups_queue == 1 ||is_cups_queue == 0) ) {
6019     debug_printf("We have already created a queue with the name %s for another remote CUPS printer but automatic clustering of equally named printers is turned off nor did we find a manually defined cluster this printer belongs to. Skipping this printer.\n", p->queue_name);
6020     debug_printf("In cups-browsed.conf try setting \"AutoClustering On\" to cluster equally-named remote CUPS printers, \"LocalQueueNamingRemoteCUPS DNS-SD\" to avoid queue name clashes, or define clusters with the \"Cluster\" directive.\n");
6021     return -1;
6022   }
6023 
6024   p->slave_of = (q && q->status != STATUS_DISAPPEARED &&
6025 		 q->status != STATUS_UNCONFIRMED &&
6026 		 q->status != STATUS_TO_BE_RELEASED) ? q : NULL;
6027   if (p->slave_of) {
6028     debug_printf("Printer %s already available through host %s, port %d.\n",
6029 		 p->queue_name, q->host, q->port);
6030     /* Update q */
6031     q->status = STATUS_TO_BE_CREATED;
6032     q->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
6033     log_cluster(p);
6034   } else if (q) {
6035     q->slave_of = p;
6036     debug_printf("Unconfirmed/disappeared printer %s already available through host %s, port %d, marking that printer a slave of the newly found one.\n",
6037 		 p->queue_name, q->host, q->port);
6038     log_cluster(p);
6039   }
6040   return (q ? 1 : 0);
6041 }
6042 
6043 static void
on_printer_state_changed(CupsNotifier * object,const gchar * text,const gchar * printer_uri,const gchar * printer,guint printer_state,const gchar * printer_state_reasons,gboolean printer_is_accepting_jobs,gpointer user_data)6044 on_printer_state_changed (CupsNotifier *object,
6045                           const gchar *text,
6046                           const gchar *printer_uri,
6047                           const gchar *printer,
6048                           guint printer_state,
6049                           const gchar *printer_state_reasons,
6050                           gboolean printer_is_accepting_jobs,
6051                           gpointer user_data)
6052 {
6053   char *ptr, buf[2048];
6054 
6055   debug_printf("on_printer_state_changed() in THREAD %ld\n", pthread_self());
6056 
6057   debug_printf("[CUPS Notification] Printer state change on printer %s: %s\n",
6058 	       printer, text);
6059   debug_printf("[CUPS Notification] Printer state reasons: %s\n",
6060 	       printer_state_reasons);
6061 
6062   if (terminating) {
6063     debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n");
6064     return;
6065   }
6066 
6067   if (autoshutdown && autoshutdown_on == NO_JOBS) {
6068     if (check_jobs() == 0) {
6069       /* If auto shutdown is active for triggering on no jobs being left, we
6070 	 schedule the shutdown in autoshutdown_timeout seconds */
6071       if (!autoshutdown_exec_id) {
6072 	debug_printf ("No jobs there any more on printers made available by us, shutting down in %d sec...\n",
6073 		      autoshutdown_timeout);
6074 	autoshutdown_exec_id =
6075 	  g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
6076 				 NULL);
6077       }
6078     } else {
6079       /* If auto shutdown is active for triggering on no jobs being left, we
6080 	 cancel a shutdown in autoshutdown_timeout seconds as there are jobs
6081          again. */
6082       if (autoshutdown_exec_id) {
6083 	debug_printf ("New jobs there on the printers made available by us, killing auto shutdown timer.\n");
6084 	g_source_remove(autoshutdown_exec_id);
6085 	autoshutdown_exec_id = 0;
6086       }
6087     }
6088   }
6089 
6090   if ((ptr = strstr(text, " is now the default printer")) != NULL) {
6091     /* Default printer has changed, we are triggered by the new default
6092        printer */
6093     strncpy(buf, text, ptr - text);
6094     buf[ptr - text] = '\0';
6095     debug_printf("[CUPS Notification] Default printer changed from %s to %s.\n",
6096 		 default_printer, buf);
6097     if (is_created_by_cups_browsed(default_printer)) {
6098       /* Previous default printer created by cups-browsed */
6099       if (!is_created_by_cups_browsed(buf)) {
6100 	/* New default printer local */
6101 	/* Removed backed-up local default printer as we do not have a
6102 	   remote printer as default any more */
6103 	invalidate_default_printer(1);
6104 	debug_printf("Manually switched default printer from a cups-browsed-generated one to a local printer.\n");
6105       }
6106     } else {
6107       /* Previous default printer local */
6108       if (is_created_by_cups_browsed(buf)) {
6109 	/* New default printer created by cups-browsed */
6110 	/* Back up the local default printer to be able to return to it
6111 	   if the remote printer disappears */
6112 	if (record_default_printer(default_printer, 1) < 0) {
6113 	  /* Delete record file if recording failed */
6114 	  debug_printf("ERROR: Failed recording local default printer. Removing the file with possible old recording.\n");
6115 	  invalidate_default_printer(1);
6116 	} else
6117 	  debug_printf("Recorded previous default printer so that if the currently selected cups-browsed-generated one disappears, we can return to the old local one.\n");
6118 	/* Remove a recorded remote printer as after manually selecting
6119 	   another one as default this one is not relevant any more */
6120 	invalidate_default_printer(0);
6121       }
6122     }
6123     if (default_printer != NULL)
6124       free((void *)default_printer);
6125     default_printer = strdup(buf);
6126   } else if ((ptr = strstr(text, " is no longer the default printer"))
6127 	     != NULL) {
6128     /* Default printer has changed, we are triggered by the former default
6129        printer */
6130     strncpy(buf, text, ptr - text);
6131     buf[ptr - text] = '\0';
6132     debug_printf("[CUPS Notification] %s not default printer any more.\n", buf);
6133   }
6134 }
6135 
6136 static void
on_job_state(CupsNotifier * object,const gchar * text,const gchar * printer_uri,const gchar * printer,guint printer_state,const gchar * printer_state_reasons,gboolean printer_is_accepting_jobs,guint job_id,guint job_state,const gchar * job_state_reasons,const gchar * job_name,guint job_impressions_completed,gpointer user_data)6137 on_job_state (CupsNotifier *object,
6138 	      const gchar *text,
6139 	      const gchar *printer_uri,
6140 	      const gchar *printer,
6141 	      guint printer_state,
6142 	      const gchar *printer_state_reasons,
6143 	      gboolean printer_is_accepting_jobs,
6144 	      guint job_id,
6145 	      guint job_state,
6146 	      const gchar *job_state_reasons,
6147 	      const gchar *job_name,
6148 	      guint job_impressions_completed,
6149 	      gpointer user_data)
6150 {
6151   int i, count;
6152   char buf[2048];
6153   remote_printer_t *p, *q, *r, *s=NULL;
6154   http_t *http = NULL;
6155   ipp_t *request, *response, *printer_attributes = NULL;
6156   ipp_attribute_t *attr;
6157   const char *pname = NULL;
6158   ipp_pstate_t pstate = IPP_PRINTER_IDLE;
6159   int paccept = 0;
6160   int num_jobs, min_jobs = 99999999;
6161   char destination_uri[1024];
6162   const char *dest_host = NULL;
6163   int dest_index = 0;
6164   int valid_dest_found = 0;
6165   char uri[HTTP_MAX_URI];
6166   int num_options;
6167   cups_option_t *options;
6168   int num_of_printers;
6169   char* document_format;
6170   int  print_quality = 0;
6171   const char *pdl = NULL;
6172   cups_array_t *pdl_list;
6173   char         resolution[32];
6174   res_t        *max_res = NULL, *min_res = NULL, *res = NULL;
6175   int          xres, yres;
6176   int          got_printer_info;
6177   static const char *pattrs[] =
6178     {
6179      "printer-name",
6180      "printer-state",
6181      "printer-is-accepting-jobs"
6182     };
6183   http_t *conn = NULL;
6184 
6185   debug_printf("on_job_state() in THREAD %ld\n", pthread_self());
6186 
6187   debug_printf("[CUPS Notification] Job state changed on printer %s: %s\n",
6188 	       printer, text);
6189   debug_printf("[CUPS Notification] Printer state reasons: %s\n",
6190 	       printer_state_reasons);
6191   debug_printf("[CUPS Notification] Job ID: %d\n",
6192 	       job_id);
6193   debug_printf("[CUPS Notification] Job State: %s\n",
6194 	       job_state_reasons);
6195   debug_printf("[CUPS Notification] Job is processing: %s\n",
6196 	       job_state == IPP_JOB_PROCESSING ? "Yes" : "No");
6197 
6198   if (terminating) {
6199     debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n");
6200     return;
6201   }
6202 
6203   if (autoshutdown && autoshutdown_on == NO_JOBS) {
6204     if (check_jobs() == 0) {
6205       /* If auto shutdown is active for triggering on no jobs being left, we
6206 	 schedule the shutdown in autoshutdown_timeout seconds */
6207       if (!autoshutdown_exec_id) {
6208 	debug_printf ("No jobs there any more on printers made available by us, shutting down in %d sec...\n", autoshutdown_timeout);
6209 	autoshutdown_exec_id =
6210 	  g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
6211 				 NULL);
6212       }
6213     } else {
6214       /* If auto shutdown is active for triggering on no jobs being left, we
6215 	 cancel a shutdown in autoshutdown_timeout seconds as there are jobs
6216 	 again. */
6217       if (autoshutdown_exec_id) {
6218 	debug_printf ("New jobs there on the printers made available by us, killing auto shutdown timer.\n");
6219 	g_source_remove(autoshutdown_exec_id);
6220 	autoshutdown_exec_id = 0;
6221       }
6222     }
6223   }
6224 
6225   if (job_id != 0 && job_state == IPP_JOB_PROCESSING) {
6226     /* Printer started processing a job, check if it uses the implicitclass
6227        backend and if so, we select the remote queue to which to send the job
6228        in a way so that we get load balancing between all remote queues
6229        associated with this queue.
6230 
6231        There are two methods to do that (configurable in cups-browsed.conf):
6232 
6233        Queuing of jobs on the client (LoadBalancingType = QUEUE_ON_CLIENT):
6234 
6235        Here we check all remote printers assigned to this printer and to its
6236        slaves which is currently accepting jobs and idle. If all are busy,
6237        we send a failure message and the backend will close with an error code
6238        after some seconds of delay, to make the job getting retried making us
6239        checking again here. If we find a destination, we tell the backend
6240        which remote queue this destination is, making the backend printing the
6241        job there immediately.
6242 
6243        With this all waiting jobs get queued up on the client, on the servers
6244        there will only be the jobs which are actually printing, as we do not
6245        send jobs to a server which is already printing. This is also the
6246        method which CUPS uses for classes. Advantage is a more even
6247        distribution of the job workload on the servers, and if a server fails,
6248        there are not several jobs stuck or lost. Disadvantage is that if one
6249        takes the client (laptop, mobile phone, ...) out of the local network,
6250        printing stops with the jobs waiting in the local queue.
6251 
6252        Queuing of jobs on the servers (LoadBalancingType = QUEUE_ON_SERVERS):
6253 
6254        Here we check all remote printers assigned to this printer and to its
6255        slaves which is currently accepting jobs and find the one with the
6256        lowest amount of jobs waiting and send the job to there. So on the
6257        local queue we have never jobs waiting if at least one remote printer
6258        accepts jobs.
6259 
6260        Not having jobs waiting locally has the advantage that we can take the
6261        local machine from the network and all jobs get printed. Disadvantage
6262        is that if a server with a full queue of jobs goes away, the jobs go
6263        away, too.
6264 
6265        Default is queuing the jobs on the client as this is what CUPS does
6266        with classes. */
6267 
6268     debug_printf("[CUPS Notification] %s starts processing a job.\n", printer);
6269     conn = http_connect_local ();
6270     if (conn == NULL) {
6271       debug_printf("Cannot connect to local CUPS to set destination for job in the load-balanced cluster %s.\n",
6272 		   printer);
6273       return;
6274     }
6275     q = printer_record(printer);
6276     /* If we hit a slave and not the master, switch to the master */
6277     if (q && q->slave_of)
6278       q = q->slave_of;
6279     if (q && q->queue_name) {
6280       /* We have remote CUPS queue(s) and so are using the implicitclass
6281 	 backend */
6282       debug_printf("[CUPS Notification] %s is using the \"implicitclass\" CUPS backend, so let us search for a destination for this job.\n", printer);
6283 
6284       /* We keep track of the printer which we used last time and start
6285 	 checking with the next printer this time, to get a "round robin"
6286 	 type of printer usage instead of having most jobs going to the first
6287 	 printer in the list. Method taken from the cupsdFindAvailablePrinter()
6288 	 function of the scheduler/classes.c file of CUPS. */
6289 
6290       if (q->last_printer < 0 ||
6291 	  q->last_printer >= cupsArrayCount(remote_printers))
6292 	q->last_printer = 0;
6293       log_cluster(q);
6294       for (i = q->last_printer + 1; ; i++) {
6295 	if (i >= cupsArrayCount(remote_printers))
6296 	  i = 0;
6297 	p = (remote_printer_t *)cupsArrayIndex(remote_printers, i);
6298 	if (!strcasecmp(p->queue_name, printer) &&
6299 	    p->status == STATUS_CONFIRMED) {
6300 	  num_of_printers = 0;
6301 	  for (r = (remote_printer_t *)cupsArrayFirst(remote_printers);
6302 	       r; r = (remote_printer_t *)cupsArrayNext(remote_printers)) {
6303 	    if (!strcmp(r->queue_name, q->queue_name)) {
6304 	      if(r->status == STATUS_DISAPPEARED ||
6305 		 r->status == STATUS_UNCONFIRMED ||
6306 		 r->status == STATUS_TO_BE_RELEASED )
6307 		continue;
6308 	      num_of_printers ++;
6309 	    }
6310 	  }
6311 
6312 	  /* If we are in a cluster, see whether the printer supports the
6313 	     requested job attributes*/
6314 	  if (num_of_printers > 1) {
6315 	    if (!supports_job_attributes_requested(printer, i, job_id,
6316 						   &print_quality)) {
6317 	      debug_printf("Printer with uri %s in cluster %s doesn't support the requested job attributes\n",
6318 			   p->uri, p->queue_name);
6319 	      if (i == q->last_printer)
6320 		break;
6321 	      else
6322 		continue;
6323 	    }
6324 	  }
6325 	  debug_printf("Checking state of remote printer %s on host %s, IP %s, port %d.\n",
6326 		       p->uri, p->host, p->ip, p->port);
6327 
6328 	  /* Check whether the printer is idle, processing, or disabled */
6329 	  debug_printf("HTTP connection to %s:%d established.\n", p->host,
6330 		       p->port);
6331 	  response = get_printer_attributes(p->uri, pattrs,
6332 					    sizeof(pattrs) / sizeof(pattrs[0]),
6333 					    NULL, 0, 0);
6334 	  debug_log_out(get_printer_attributes_log);
6335 	  if (response != NULL) {
6336 	    debug_printf("IPP request to %s:%d successful.\n", p->host,
6337 			 p->port);
6338 	    pname = NULL;
6339 	    pstate = IPP_PRINTER_IDLE;
6340 	    paccept = 0;
6341 	    for (attr = ippFirstAttribute(response); attr != NULL;
6342 		 attr = ippNextAttribute(response)) {
6343 	      while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
6344 		attr = ippNextAttribute(response);
6345 	      if (attr == NULL)
6346 		break;
6347 	      pname = NULL;
6348 	      pstate = IPP_PRINTER_IDLE;
6349 	      paccept = 0;
6350 	      got_printer_info = 0;
6351 	      while (attr != NULL && ippGetGroupTag(attr) ==
6352 		     IPP_TAG_PRINTER) {
6353 		if (!strcmp(ippGetName(attr), "printer-name") &&
6354 		    ippGetValueTag(attr) == IPP_TAG_NAME) {
6355 		  pname = ippGetString(attr, 0, NULL);
6356 		} else if (!strcmp(ippGetName(attr), "printer-state") &&
6357 			   ippGetValueTag(attr) == IPP_TAG_ENUM)
6358 		  pstate = (ipp_pstate_t)ippGetInteger(attr, 0);
6359 		else if (!strcmp(ippGetName(attr),
6360 				 "printer-is-accepting-jobs") &&
6361 			 ippGetValueTag(attr) == IPP_TAG_BOOLEAN) {
6362 		  paccept = ippGetBoolean(attr, 0);
6363 		  got_printer_info = 1;
6364 		}
6365 		attr = ippNextAttribute(response);
6366 	      }
6367 	      if (got_printer_info == 0) {
6368 		if (attr == NULL)
6369 		  break;
6370 		else
6371 		  continue;
6372 	      }
6373 	      debug_printf("IPP Response contains attributes values printer-name %s, accepting-job %d\n",
6374 			   (pname ? pname : "(Not reported)"), paccept);
6375 	      if (paccept) {
6376 		debug_printf("Printer %s on host %s, port %d is accepting jobs.\n",
6377 			     p->uri, p->host, p->port);
6378 		switch (pstate) {
6379 		case IPP_PRINTER_IDLE:
6380 		  valid_dest_found = 1;
6381 		  dest_host = p->ip ? p->ip : p->host;
6382 		  strncpy(destination_uri, p->uri, sizeof(destination_uri) - 1);
6383 		  printer_attributes = p->prattrs;
6384 		  pdl = p->pdl;
6385 		  s = p;
6386 		  dest_index = i;
6387 		  debug_printf("Printer %s on host %s, port %d is idle, take this as destination and stop searching.\n",
6388 			       p->uri, p->host, p->port);
6389 		  break;
6390 		case IPP_PRINTER_PROCESSING:
6391 		  valid_dest_found = 1;
6392 		  if (LoadBalancingType == QUEUE_ON_SERVERS) {
6393 		    num_jobs = 0;
6394 		    http =
6395 		      httpConnectEncryptShortTimeout (p->ip ? p->ip : p->host,
6396 						      p->port,
6397 						     HTTP_ENCRYPT_IF_REQUESTED);
6398 		    if (http) {
6399 		      num_jobs = get_number_of_jobs(http, p->uri, 0,
6400 						    CUPS_WHICHJOBS_ACTIVE);
6401 		      if (num_jobs >= 0 && num_jobs < min_jobs) {
6402 			min_jobs = num_jobs;
6403 			dest_host = p->ip ? p->ip : p->host;
6404 			strncpy(destination_uri, p->uri,
6405 				sizeof(destination_uri) - 1);
6406 			printer_attributes = p->prattrs;
6407 			pdl = p->pdl;
6408 			s = p;
6409 			dest_index = i;
6410 		      }
6411 		      debug_printf("Printer %s on host %s, port %d is printing and it has %d jobs.\n",
6412 				   p->uri, p->host, p->port,
6413 				   num_jobs);
6414 		      httpClose(http);
6415 		      http = NULL;
6416 		    }
6417 		  } else
6418 		    debug_printf("Printer %s on host %s, port %d is printing.\n",
6419 				 p->uri, p->host, p->port);
6420 		  break;
6421 		case IPP_PRINTER_STOPPED:
6422 		  debug_printf("Printer %s on host %s, port %d is disabled, skip it.\n",
6423 			       p->uri, p->host, p->port);
6424 		  break;
6425 		}
6426 	      } else {
6427 		debug_printf("Printer %s on host %s, port %d is not accepting jobs, skip it.\n",
6428 			     p->uri, p->host, p->port);
6429 	      }
6430 	      break;
6431 	    }
6432 
6433 	    ippDelete(response);
6434 	    response = NULL;
6435 
6436 	    if (pstate == IPP_PRINTER_IDLE && paccept) {
6437 	      q->last_printer = i;
6438 	      break;
6439 	    }
6440 	  } else
6441 	    debug_printf("IPP request to %s:%d failed.\n", p->host,
6442 			 p->port);
6443 	}
6444 	if (i == q->last_printer)
6445 	  break;
6446       }
6447 
6448       /* Write the selected destination host into an option of our implicit
6449 	 class queue (cups-browsed-dest-printer="<dest>") so that the
6450 	 implicitclass backend will pick it up */
6451 
6452       if ((pdl_list = cupsArrayNew3((cups_array_func_t)strcasecmp,
6453 				    NULL, NULL, 0,
6454 				    (cups_acopy_func_t)strdup,
6455 				    (cups_afree_func_t)free)) == NULL){
6456 	debug_printf("Could Not allocate memory for cups Array \n");
6457 	return;
6458       }
6459 
6460       /* Finding the best pdl supported by the printer, we need to send the
6461 	 document format to the implictclass backend */
6462       if (((attr = ippFindAttribute(printer_attributes,
6463 				    "document-format-supported",
6464 				    IPP_TAG_MIMETYPE)) != NULL) ||
6465 	  (pdl && pdl[0] != '\0')) {
6466 	const char *format = pdl;
6467 	i = 0;
6468 	count = ippGetCount(attr);
6469 	while ((attr && i < count) || /* Go through formats in attribute */
6470 	       (!attr && pdl && pdl[0] != '\0' && format[0] != '\0')) {
6471 	  /* Go through formats in pdl string (from DNS-SD record) */
6472 	  /* Pick next format from attribute */
6473           if (attr) format = ippGetString(attr, i, NULL);
6474           /* Add format to list of supported PDLs, skip duplicates */
6475           if (!cupsArrayFind(pdl_list, (void *)format))
6476             cupsArrayAdd(pdl_list, (void *)format);
6477           if (attr)
6478 	    /* Next format in attribute */
6479 	    i ++;
6480 	  else {
6481 	    /* Find the next format in the string pdl, if there is none left,
6482 	       go to the terminating zero */
6483             while (!isspace(*format) && *format != ',' && *format != '\0')
6484               format ++;
6485             while ((isspace(*format) || *format == ',') && *format != '\0')
6486               format ++;
6487 	  }
6488         }
6489       }
6490 
6491       /* The priority order for the PDLs is the same as in the
6492 	 PPD generator in cupsfilters/ppdgenerator.c */
6493       document_format = (char *)malloc(sizeof(char) * 32);
6494       if (cupsArrayFind(pdl_list, "application/vnd.cups-pdf") ||
6495 	  cupsArrayFind(pdl_list, "application/pdf"))
6496 	strcpy(document_format, "pdf");
6497 #ifdef CUPS_RASTER_HAVE_APPLERASTER
6498       else if (cupsArrayFind(pdl_list, "image/urf"))
6499 	strcpy(document_format, "apple-raster");
6500 #endif
6501       else if (cupsArrayFind(pdl_list, "image/pwg-raster"))
6502 	strcpy(document_format, "raster");
6503 #ifdef QPDF_HAVE_PCLM
6504       else if (cupsArrayFind(pdl_list, "application/PCLm"))
6505 	strcpy(document_format, "pclm");
6506 #endif
6507       else if (cupsArrayFind(pdl_list, "application/vnd.hp-pclxl"))
6508 	strcpy(document_format, "pclxl");
6509       else if (cupsArrayFind(pdl_list, "application/vnd.cups-postscript") ||
6510 	       cupsArrayFind(pdl_list, "application/postscript"))
6511 	strcpy(document_format, "postscript");
6512       else if (cupsArrayFind(pdl_list, "application/vnd.hp-pcl") ||
6513 	       cupsArrayFind(pdl_list, "application/pcl") ||
6514 	       cupsArrayFind(pdl_list, "application/x-pcl"))
6515 	strcpy(document_format, "pcl");
6516 
6517       if (pdl_list)
6518         cupsArrayDelete(pdl_list);
6519 
6520       /* Deciding the resolution to be sent with the job */
6521       /* Finding the minimum and maximum resolution supported by the printer */
6522 
6523       max_res = resolutionNew(0, 0);
6524       min_res = resolutionNew(0, 0);
6525 
6526       if (s &&
6527 	  ((attr = ippFindAttribute(s->prattrs, "printer-resolution-supported",
6528 				    IPP_TAG_RESOLUTION)) != NULL)) {
6529 	for (i = 0, count = ippGetCount(attr); i < count; i ++) {
6530 	  if ((res = ippResolutionToRes(attr, i)) != NULL) {
6531 	    debug_printf("%d %d\n",res->x,res->y);
6532 	    if (i == 0) {
6533 	      max_res->x = res->x;
6534 	      max_res->y = res->y;
6535 	      min_res->x = res->x;
6536 	      min_res->y = res->y;
6537 	    } else {
6538 	      if(compare_resolutions((void *)res,(void *)max_res,NULL) > 0) {
6539 		max_res->x = res->x;
6540 		max_res->y = res->y;
6541 	      }
6542 	      if(compare_resolutions((void *)res,(void *)min_res,NULL) < 0) {
6543 		min_res->x = res->x;
6544 		min_res->y = res->y;
6545 	      }
6546 	    }
6547 	    free_resolution(res, NULL);
6548 	    res = NULL;
6549 	  }
6550 	}
6551       }
6552 
6553       /* If we are requesting normal print quality then send default
6554 	 resolution, for draft send minimum resolution and for high,
6555 	 send the maximum resolution */
6556       /* If none of the below dpi is selected then default dpi will be
6557 	 sent as 600 */
6558       snprintf(resolution,sizeof(resolution), "600dpi");
6559       if (s && print_quality == 3) {
6560 	if (min_res != NULL){
6561 	  if (min_res->x == min_res->y)
6562 	    snprintf(resolution,sizeof(resolution), "%ddpi", min_res->x);
6563 	  else
6564 	    snprintf(resolution,sizeof(resolution), "%dx%ddpi", min_res->x,
6565 		     min_res->y);
6566 	}
6567       } else if (s && print_quality == 5) {
6568 	if (max_res != NULL) {
6569 	  if (max_res->x == max_res->y)
6570 	    snprintf(resolution, sizeof(resolution), "%ddpi", max_res->x);
6571 	  else
6572 	    snprintf(resolution, sizeof(resolution), "%dx%ddpi", max_res->x,
6573 		     max_res->y);
6574 	}
6575       } else if (s) {
6576 	if ((attr = ippFindAttribute(s->prattrs, "printer-resolution-default",
6577 				     IPP_TAG_ZERO)) != NULL) {
6578 	  if ((res = ippResolutionToRes(attr, 0)) != NULL) {
6579 	    xres = res->x;
6580 	    yres = res->y;
6581 	    if (xres == yres)
6582 	      snprintf(resolution, sizeof(resolution), "%ddpi", xres);
6583 	    else
6584 	      snprintf(resolution, sizeof(resolution), "%dx%ddpi", xres, yres);
6585 	    free_resolution(res, NULL);
6586 	  }
6587 	}
6588       }
6589 
6590       free_resolution(max_res, NULL);
6591       free_resolution(min_res, NULL);
6592 
6593       request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
6594       httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
6595 		       "localhost", 0, "/printers/%s", printer);
6596       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
6597 		   "printer-uri", NULL, uri);
6598       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
6599 		   "requesting-user-name", NULL, cupsUser());
6600       if (dest_host) {
6601 	q->last_printer = dest_index;
6602 	snprintf(buf, sizeof(buf), "\"%d %s %s %s\"", job_id, destination_uri,
6603 		 document_format, resolution);
6604 	debug_printf("Destination for job %d to %s: %s\n",
6605 		     job_id, printer, destination_uri);
6606       } else if (valid_dest_found == 1) {
6607 	snprintf(buf, sizeof(buf), "\"%d ALL_DESTS_BUSY\"", job_id);
6608 	debug_printf("All destinations busy for job %d to %s\n",
6609 		     job_id, printer);
6610       } else {
6611 	snprintf(buf, sizeof(buf), "\"%d NO_DEST_FOUND\"", job_id);
6612 	debug_printf("No destination found for job %d to %s\n",
6613 		     job_id, printer);
6614       }
6615       num_options = 0;
6616       options = NULL;
6617       num_options = cupsAddOption(CUPS_BROWSED_DEST_PRINTER "-default", buf,
6618 				  num_options, &options);
6619       cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
6620       cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
6621       ippDelete(cupsDoRequest(conn, request, "/admin/"));
6622 
6623       cupsFreeOptions(num_options, options);
6624       free(document_format);
6625 
6626       if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
6627 	debug_printf("ERROR: Unable to set \"" CUPS_BROWSED_DEST_PRINTER
6628 		     "-default\" option to communicate the destination server for this job (%s)!\n",
6629 		     cupsLastErrorString());
6630 	return;
6631       }
6632     }
6633   }
6634 }
6635 
6636 static void
on_printer_deleted(CupsNotifier * object,const gchar * text,const gchar * printer_uri,const gchar * printer,guint printer_state,const gchar * printer_state_reasons,gboolean printer_is_accepting_jobs,gpointer user_data)6637 on_printer_deleted (CupsNotifier *object,
6638 		    const gchar *text,
6639 		    const gchar *printer_uri,
6640 		    const gchar *printer,
6641 		    guint printer_state,
6642 		    const gchar *printer_state_reasons,
6643 		    gboolean printer_is_accepting_jobs,
6644 		    gpointer user_data)
6645 {
6646   remote_printer_t *p;
6647   char *r;
6648   char *local_queue_name_lower = NULL;
6649   local_printer_t *local_printer = NULL;
6650 
6651   debug_printf("on_printer_deleted() in THREAD %ld\n", pthread_self());
6652 
6653   debug_printf("[CUPS Notification] Printer deleted: %s\n",
6654 	       text);
6655 
6656   if (terminating) {
6657     debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n");
6658     return;
6659   }
6660 
6661   if (is_created_by_cups_browsed(printer)) {
6662     /* Get available CUPS queues to check whether the queue did not
6663        already get re-created */
6664     update_local_printers ();
6665     /* Look up print queue in the list */
6666     local_queue_name_lower = g_ascii_strdown(printer, -1);
6667     local_printer = g_hash_table_lookup (local_printers,
6668 					 local_queue_name_lower);
6669     free(local_queue_name_lower);
6670     /* If the queue is there again, do not re-create it */
6671     if (local_printer) {
6672       debug_printf("Printer %s already re-created.\n",
6673 		   printer);
6674       return;
6675     }
6676 
6677     /* a cups-browsed-generated printer got deleted, re-create it */
6678     debug_printf("Printer %s got deleted, re-creating it.\n",
6679 		 printer);
6680     /* If the deleted printer was the default printer, make sure it gets the
6681        default printer again */
6682     if (default_printer && !strcasecmp(printer, default_printer)) {
6683       if (record_default_printer(printer, 0) < 0) {
6684 	/* Delete record file if recording failed */
6685 	debug_printf("ERROR: Failed recording remote default printer. Removing the file with possible old recording.\n");
6686 	invalidate_default_printer(0);
6687       } else
6688 	debug_printf("Recorded %s as remote default printer so that it gets set as default after re-creating.\n",
6689 		     printer);
6690       /* Make sure that a recorded local default printer does not get lost
6691 	 during the recovery operation */
6692       if ((r = retrieve_default_printer(1)) != NULL) {
6693 	if (default_printer != NULL)
6694 	  free((void *)default_printer);
6695 	default_printer = r;
6696       }
6697     }
6698     /* Schedule for immediate creation of the CUPS queue */
6699     p = printer_record(printer);
6700     if (p && p->status != STATUS_DISAPPEARED &&
6701 	p->status != STATUS_UNCONFIRMED &&
6702 	p->status != STATUS_TO_BE_RELEASED) {
6703       p->status = STATUS_TO_BE_CREATED;
6704       p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
6705       if (in_shutdown == 0)
6706 	recheck_timer();
6707     }
6708   }
6709 }
6710 
6711 static int
queue_overwritten(remote_printer_t * p)6712 queue_overwritten (remote_printer_t *p)
6713 {
6714   http_t        *conn = NULL;
6715   ipp_t         *response = NULL;       /* IPP Response */
6716   ipp_attribute_t *attr;                /* Current attribute */
6717   const char    *printername,           /* Print queue name */
6718                 *uri,                   /* Printer URI */
6719                 *device,                /* Printer device URI */
6720                 *makemodel;             /* Printer make and model
6721                                            (equals PPD NickName) */
6722   char          local_queue_uri[1024];
6723   static const char *pattrs[] =        /* Attributes we need for printers... */
6724                 {
6725                   "printer-name",
6726                   "printer-uri-supported",
6727                   "device-uri",
6728                   "printer-make-and-model"
6729                 };
6730   int           overwritten = 0;
6731 
6732   if (p->overwritten)
6733     /* We already have discovered that this queue got overwritten
6734        so we do not repeat the tests and exit positively */
6735     return 1;
6736 
6737   if (p->uri[0] == '\0')
6738     /* Also skip unconfirmed printer entries from queues of the
6739        previous session, they do not have a PPD file registered, so we
6740        cannot compare */
6741     return 0;
6742 
6743   /* Get the device URI which our CUPS queue actually has now, a
6744      change of the URI means a modification or replacement of the
6745      print queue by something user-defined. So we schedule this queue
6746      for release from handling by cups-browsed.
6747 
6748      In a second step get the NickName of the PPD which our CUPS queue
6749      actually uses now, a change of the NickName means a replacement
6750      of the PPD of the print queue by a user-selected one. So we
6751      schedule this queue for release from handling by cups-browsed
6752      also in this case.
6753 
6754      We only need the NickName from the PPD and due to the fact that
6755      the cupsGetPPD2() function does not work when CUPS is on a
6756      non-standard port (!= 631, Bug!) and the NickName is also in the
6757      get-printer-attributes IPP response as "printer-make-and-model",
6758      we go the IPP way here and do not download the printer's PPD. */
6759 
6760   conn = http_connect_local ();
6761   if (conn == NULL) {
6762     debug_printf("Cannot connect to local CUPS to see whether queue %s got overwritten.\n",
6763 		 p->queue_name);
6764     return 0;
6765   }
6766 
6767   /* URI of the local CUPS queue (not the device URI */
6768   httpAssembleURIf(HTTP_URI_CODING_ALL, local_queue_uri,
6769 		   sizeof(local_queue_uri),
6770 		   "ipp", NULL, "localhost", 0,
6771 		   "/printers/%s", p->queue_name);
6772   response = get_printer_attributes2(conn, local_queue_uri,
6773 				     pattrs, sizeof(pattrs) / sizeof(pattrs[0]),
6774 				     pattrs, sizeof(pattrs) / sizeof(pattrs[0]),
6775 				     1);
6776   debug_log_out(get_printer_attributes_log);
6777   if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING) {
6778     debug_printf("lpstat: %s\n", cupsLastErrorString());
6779   } else {
6780     printername = NULL;
6781     device      = NULL;
6782     uri         = NULL;
6783     makemodel   = NULL;
6784     for (attr = ippFirstAttribute(response); attr != NULL;
6785 	 attr = ippNextAttribute(response)) {
6786       if (!strcmp(ippGetName(attr), "printer-name") &&
6787 	  ippGetValueTag(attr) == IPP_TAG_NAME)
6788 	printername = ippGetString(attr, 0, NULL);
6789       if (!strcmp(ippGetName(attr), "printer-uri-supported") &&
6790 	  ippGetValueTag(attr) == IPP_TAG_URI)
6791 	uri = ippGetString(attr, 0, NULL);
6792       if (!strcmp(ippGetName(attr), "device-uri") &&
6793 	  ippGetValueTag(attr) == IPP_TAG_URI)
6794 	device = ippGetString(attr, 0, NULL);
6795       if (!strcmp(ippGetName(attr), "printer-make-and-model") &&
6796 	  ippGetValueTag(attr) == IPP_TAG_TEXT)
6797 	makemodel = ippGetString(attr, 0, NULL);
6798     }
6799     if (printername != NULL &&
6800 	strcasecmp(p->queue_name, printername) == 0) {
6801       if (device == NULL)
6802 	device = uri;
6803       /* Check device URI */
6804       if (device != NULL &&
6805 	  (p->uri == NULL ||
6806 	   (strlen(device) < 16 ||
6807 	    strncmp(device, "implicitclass://", 16)))) {
6808 	/* The printer's device URI is different to what we have
6809 	   assigned, so we got notified because the queue was
6810 	   externally modified and so we will release this printer
6811 	   from the control of cups-browsed */
6812 	debug_printf("Printer %s got modified externally, discovered by a change of its device URI from %s to %s.\n",
6813 		     p->queue_name,
6814 		     (p->uri ? (p->netprinter ? p->uri :
6815 				"implicitclass://...") :
6816 		      "(not yet determined)"),
6817 		     device);
6818 	overwritten = 1;
6819       }
6820       /* Check NickName */
6821       if (p->nickname == NULL || makemodel == NULL ||
6822 	  strcasecmp(p->nickname, makemodel)) {
6823 	/* The PPD file of the queue got replaced which we
6824 	   discovered by comparing the NickName of the PPD with the
6825 	   NickName which the PPD we have used has. So we were
6826 	   notified because the queue was externally modified and so
6827 	   we will release this printer from the control of
6828 	   cups-browsed */
6829 	debug_printf("Printer %s got modified externally, discovered by the NickName of its PPD file having changed from \"%s\" to \"%s\".\n",
6830 		     p->queue_name, (p->nickname ? p->nickname : "(no PPD)"),
6831 		     (makemodel ? makemodel :
6832 		      "(NickName not readable)"));
6833 	overwritten = 1;
6834       }
6835     }
6836   }
6837   if (response) ippDelete(response);
6838 
6839   return overwritten;
6840 }
6841 
6842 static void
on_printer_modified(CupsNotifier * object,const gchar * text,const gchar * printer_uri,const gchar * printer,guint printer_state,const gchar * printer_state_reasons,gboolean printer_is_accepting_jobs,gpointer user_data)6843 on_printer_modified (CupsNotifier *object,
6844 		     const gchar *text,
6845 		     const gchar *printer_uri,
6846 		     const gchar *printer,
6847 		     guint printer_state,
6848 		     const gchar *printer_state_reasons,
6849 		     gboolean printer_is_accepting_jobs,
6850 		     gpointer user_data)
6851 {
6852   remote_printer_t *p;
6853   http_t        *conn = NULL;
6854   ipp_t         *request;               /* IPP Request */
6855   int           re_create, is_cups_queue;
6856   char          *new_queue_name;
6857   cups_array_t  *to_be_renamed;
6858   char          local_queue_uri[1024];
6859   char          *resolved_uri = NULL;
6860 
6861   debug_printf("on_printer_modified() in THREAD %ld\n", pthread_self());
6862 
6863   debug_printf("[CUPS Notification] Printer modified: %s\n",
6864 	       text);
6865 
6866   if (is_created_by_cups_browsed(printer)) {
6867     p = printer_record(printer);
6868     if (p->overwritten)
6869       /* We already have discovered that this queue got overwritten
6870          and are treating the process appropriately, so return now to
6871          avoid an infinite recursion */
6872       return;
6873 
6874     if (queue_overwritten(p)) {
6875       /* Our generated local queue pointing to a remote printer got
6876 	 overwritten by an externally created queue with the same
6877 	 name.
6878 	 We will release control from this queue now and try to
6879 	 re-create our queue under a different name, usually
6880 	 <old_name>@<remote_host>.
6881 	 If we have slaves, we have to do this for them, too. */
6882 
6883       p->overwritten = 1;
6884 
6885       /* First, remove the "cups-browsed=true" from the queue's
6886 	 options, so that cups-browsed considers this queue as created
6887 	 manually */
6888       debug_printf("Removing \"cups-browsed=true\" from CUPS queue %s (%s).\n",
6889 		   p->queue_name, p->uri);
6890       conn = http_connect_local ();
6891       if (conn == NULL)
6892 	debug_printf("Browse send failed to connect to localhost\n");
6893       else {
6894 	request = ippNewRequest(IPP_OP_CUPS_ADD_MODIFY_PRINTER);
6895 	/* Printer URI: ipp://localhost/printers/<queue name> */
6896 	httpAssembleURIf(HTTP_URI_CODING_ALL, local_queue_uri,
6897 			 sizeof(local_queue_uri),
6898 			 "ipp", NULL, "localhost", 0,
6899 			 "/printers/%s", p->queue_name);
6900 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
6901 		     "printer-uri", NULL, local_queue_uri);
6902 	/* Default user */
6903 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
6904 		     "requesting-user-name", NULL, cupsUser());
6905 	/* Option to be removed */
6906 	ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_DELETEATTR,
6907 		      CUPS_BROWSED_MARK "-default", 0);
6908 	/* Do it */
6909 	ippDelete(cupsDoRequest(conn, request, "/admin/"));
6910 	if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
6911 	  debug_printf("Unable to remove \"cups-browsed=true\" from CUPS queue!\n");
6912       }
6913 
6914       /* Now try to rename all our printer entries with this
6915 	 queue name. Drop entries where renaming fails */
6916       to_be_renamed = cupsArrayNew(NULL, NULL);
6917       /* Put the printer entries which need attention into
6918 	 a separate array, as we cannot run two nested loops
6919 	 on one CUPS array, as our printer entry array */
6920       for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
6921 	   p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
6922 	if (strcasecmp(p->queue_name, printer) == 0) {
6923 	  p->overwritten = 1;
6924 	  cupsArrayAdd(to_be_renamed, p);
6925 	}
6926       for (p = (remote_printer_t *)cupsArrayFirst(to_be_renamed);
6927 	   p; p = (remote_printer_t *)cupsArrayNext(to_be_renamed)) {
6928 	is_cups_queue = (p->netprinter == 0 ? 1 : 0);
6929 	re_create = 1;
6930 	/* Is there a local queue with the same URI as the remote queue? */
6931 	if (g_hash_table_find (local_printers,
6932 			       local_printer_is_same_device, p)) {
6933 	  /* Found a local queue with the same URI as our discovered printer
6934 	     would get, so ignore this remote printer */
6935 	  debug_printf("Printer with URI %s (or IPP/IPPS equivalent) already exists, no replacement queue to be created.\n",
6936 		       p->uri);
6937 	  re_create = 0;
6938 	} else if ((new_queue_name = /* Try to find a new queue name */
6939 		    get_local_queue_name(p->service_name, p->make_model,
6940 					 p->resource, p->host,
6941 					 &is_cups_queue,
6942 					 p->queue_name)) == NULL) {
6943 	  /* Not able to find a new name for the queue */
6944 	  debug_printf("No new name for printer found, no replacement queue to be created.\n");
6945 	  re_create = 0;
6946 	} else {
6947 	  free(p->queue_name);
6948 	  p->queue_name = new_queue_name;
6949 	  /* Check whether the queue under its new name will be stand-alone or
6950 	     part of a cluster */
6951 	  if (join_cluster_if_needed(p, is_cups_queue) < 0) {
6952 	    /* There are other cups-browsed-generated queues with the new
6953 	       name, not able to cluster this queue with them */
6954 	    debug_printf("Not able to cluster this queue with equally-named ones.\n");
6955 	    re_create = 0;
6956 	  }
6957 	}
6958 	if (resolved_uri) free(resolved_uri);
6959 	if (re_create) {
6960 	  p->overwritten = 0;
6961 	  p->status = STATUS_TO_BE_CREATED;
6962 	  p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
6963 	  debug_printf("Released CUPS queue %s from the control of cups-browsed. Printer with URI %s renamed to %s.\n",
6964 		       printer, p->uri, p->queue_name);
6965 	} else {
6966 	  /* To remove this entry independent of any other entry we
6967 	     set the slave_of to NULL. This does not lead to an
6968 	     attempt to remove a CUPS queue as we have the status
6969 	     STATUS_TO_BE_RELEASED */
6970 	  p->slave_of = NULL;
6971 	  p->status = STATUS_TO_BE_RELEASED;
6972 	  p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
6973 	  debug_printf("Released CUPS queue %s from the control of cups-browsed. No local queue any more for printer with URI %s.\n",
6974 		       printer, p->uri);
6975 	}
6976       }
6977       cupsArrayDelete(to_be_renamed);
6978       if (in_shutdown == 0)
6979 	recheck_timer();
6980     } else {
6981       if (terminating) {
6982 	debug_printf("[CUPS Notification]: Not saving external option changes because cups-browsed is terminating.\n");
6983 	return;
6984       }
6985       /* The user has changed settings of a printer which we have generated,
6986 	 backup the changes for the case of a crash or unclean shutdown of
6987 	 cups-browsed. */
6988       if (!p->no_autosave) {
6989 	debug_printf("Settings of printer %s got modified, doing backup.\n",
6990 		     p->queue_name);
6991 	p->no_autosave = 1; /* Avoid infinite recursion */
6992 	record_printer_options(p->queue_name);
6993 	p->no_autosave = 0;
6994       }
6995     }
6996   }
6997 }
6998 
6999 
7000 
7001 /* This compare function makes the "lo" (looback) interface always
7002    sorted to the beginning of the array, this way one only needs to
7003    check the first element of the error to find out whether a remote
7004    printer is already available through the loopback interface (preferred
7005    interface) or not.
7006    All other interfaces are sorted alphabetically, the types let IPPS
7007    appear before IPP, and the families numerically (makes IPv4 appear
7008    before IPv6). */
7009 
7010 int
ipp_discovery_cmp(void * va,void * vb,void * data)7011 ipp_discovery_cmp(void *va, void *vb, void *data) {
7012   ipp_discovery_t *a = (ipp_discovery_t *)va;
7013   ipp_discovery_t *b = (ipp_discovery_t *)vb;
7014   int cmp;
7015 
7016   if (!a && !b)
7017     return 0;
7018   if (a && !b)
7019     return -1;
7020   if (!a && b)
7021     return  1;
7022 
7023   if (!strcasecmp(a->interface, "lo") && strcasecmp(b->interface, "lo"))
7024     return -1;
7025   if (strcasecmp(a->interface, "lo") && !strcasecmp(b->interface, "lo"))
7026     return  1;
7027 
7028   cmp = strcasecmp(a->interface, b->interface);
7029   if (cmp)
7030     return cmp;
7031 
7032   if (strcasestr(a->type, "ipps") && !strcasestr(b->type, "ipps"))
7033     return -1;
7034   if (!strcasestr(a->type, "ipps") && strcasestr(b->type, "ipps"))
7035     return  1;
7036 
7037   cmp = strcasecmp(a->type, b->type);
7038   if (cmp)
7039     return cmp;
7040 
7041   if (a->family < b->family)
7042     return -1;
7043   else if (a->family > b->family)
7044     return  1;
7045   else
7046     return 0;
7047 }
7048 
7049 void
ipp_discovery_free(void * ve,void * data)7050 ipp_discovery_free(void *ve, void *data) {
7051   ipp_discovery_t *e = (ipp_discovery_t *)ve;
7052 
7053   if (e) {
7054     if (e->interface)
7055       free(e->interface);
7056     if (e->type)
7057       free(e->type);
7058     free(e);
7059   }
7060 }
7061 
7062 void
ipp_discoveries_list(cups_array_t * a)7063 ipp_discoveries_list(cups_array_t *a) {
7064   ipp_discovery_t *e;
7065 
7066   debug_printf("Printer discovered %d times:\n", cupsArrayCount(a));
7067   for (e = cupsArrayFirst(a); e; e = cupsArrayNext(a))
7068     debug_printf("    %s, %s, %s\n", e->interface, e->type,
7069 		 (e->family == AF_INET ? "IPv4" :
7070 		  (e->family == AF_INET6 ? "IPv6" : "???")));
7071 }
7072 
7073 int
ipp_discoveries_add(cups_array_t * a,const char * interface,const char * type,int family)7074 ipp_discoveries_add(cups_array_t *a,
7075 		    const char *interface,
7076 		    const char *type,
7077 		    int family) {
7078   ipp_discovery_t *e;
7079 
7080   if (!interface || !type)
7081     return 0;
7082   if ((e = (ipp_discovery_t *)calloc(1, sizeof(ipp_discovery_t))) ==
7083       NULL) {
7084     debug_printf("ERROR: Unable to allocate memory.\n");
7085     return 0;
7086   }
7087   e->interface = strdup(interface);
7088   e->type = strdup(type);
7089   e->family = family;
7090   cupsArrayAdd(a, e);
7091   ipp_discoveries_list(a);
7092   return 1;
7093 }
7094 
7095 static remote_printer_t *
create_remote_printer_entry(const char * queue_name,const char * location,const char * info,const char * uri,const char * host,const char * ip,int port,const char * resource,const char * service_name,const char * type,const char * domain,const char * interface,int family,const char * pdl,int color,int duplex,const char * make_model,int is_cups_queue)7096 create_remote_printer_entry (const char *queue_name,
7097 			     const char *location,
7098 			     const char *info,
7099 			     const char *uri,
7100 			     const char *host,
7101 			     const char *ip,
7102 			     int port,
7103 			     const char *resource,
7104 			     const char *service_name,
7105 			     const char *type,
7106 			     const char *domain,
7107 			     const char *interface,
7108 			     int family,
7109 			     const char *pdl,
7110 			     int color,
7111 			     int duplex,
7112 			     const char *make_model,
7113 			     int is_cups_queue)
7114 {
7115   remote_printer_t *p;
7116   remote_printer_t *q;
7117   http_t *http_printer = NULL;
7118 #ifdef HAVE_CUPS_1_6
7119   int i;
7120   ipp_attribute_t *attr;
7121   char valuebuffer[65536];
7122   int is_pwgraster = 0;
7123   int is_appleraster = 0;
7124   int is_pclm = 0;
7125   int is_pdf = 0;
7126 #endif /* HAVE_CUPS_1_6 */
7127 
7128   if (!queue_name || !location || !info || !uri || !host || !resource ||
7129       !service_name || !type || !domain) {
7130     debug_printf("ERROR: create_remote_printer_entry(): Input value missing!\n");
7131     return NULL;
7132   }
7133 
7134   /* Mark this as a queue to be created locally pointing to the printer */
7135   if ((p = (remote_printer_t *)calloc(1, sizeof(remote_printer_t))) == NULL) {
7136     debug_printf("ERROR: Unable to allocate memory.\n");
7137     return NULL;
7138   }
7139 
7140   /* Assure that, if we have forgotten to set a field in the printer
7141      record, that it is set to zero */
7142   memset(p, 0, sizeof(remote_printer_t));
7143 
7144   /* Queue name */
7145   p->queue_name = strdup(queue_name);
7146   if (!p->queue_name)
7147     goto fail;
7148 
7149   p->location = strdup(location);
7150   if (!p->location)
7151     goto fail;
7152 
7153   p->info = strdup(info);
7154   if (!p->info)
7155     goto fail;
7156 
7157   if (make_model)
7158     p->make_model = strdup(make_model);
7159   else
7160     p->make_model = NULL;
7161 
7162   if (pdl)
7163     p->pdl = strdup(pdl);
7164   else
7165     p->pdl = NULL;
7166 
7167   p->color = color;
7168 
7169   p->duplex = duplex;
7170 
7171   p->uri = strdup(uri);
7172   if (!p->uri)
7173     goto fail;
7174 
7175   p->slave_of = NULL;
7176   p->last_printer = -1;
7177 
7178   p->num_options = 0;
7179   p->options = NULL;
7180 
7181   p->host = strdup (host);
7182   if (!p->host)
7183     goto fail;
7184 
7185   p->ip = (ip != NULL ? strdup (ip) : NULL);
7186 
7187   p->port = (port != 0 ? port : 631);
7188 
7189   p->resource = strdup (resource);
7190   if (!p->resource)
7191     goto fail;
7192 
7193   p->service_name = strdup (service_name);
7194   if (!p->service_name)
7195     goto fail;
7196 
7197   /* Record DNS-SD service parameters to identify print queue
7198      entry for removal when service disappears */
7199   p->type = strdup (type);
7200   if (!p->type)
7201     goto fail;
7202 
7203   p->domain = strdup (domain);
7204   if (!p->domain)
7205     goto fail;
7206 
7207   p->ipp_discoveries =
7208     cupsArrayNew3(ipp_discovery_cmp, NULL, NULL, 0, NULL, ipp_discovery_free);
7209   if (p->ipp_discoveries == NULL) {
7210     debug_printf("ERROR: Unable to allocate memory.\n");
7211     return NULL;
7212   }
7213   if (domain != NULL && domain[0] != '\0' &&
7214       type != NULL && type[0] != '\0')
7215     ipp_discoveries_add(p->ipp_discoveries, interface, type, family);
7216 
7217   /* Schedule for immediate creation of the CUPS queue */
7218   p->status = STATUS_TO_BE_CREATED;
7219   p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
7220 
7221   /* Flag which can be set to inhibit automatic saving of option settings
7222      by the on_printer_modified() notification handler function */
7223   p->no_autosave = 0;
7224 
7225   /* Flag to be set when a local queue generated by us was overwritten
7226      by an external process. It serves to avoid that the process to
7227      treat this case is not repeated in an infinite recursion */
7228   p->overwritten = 0;
7229 
7230   /* Flag to mark whether this printer was discovered through a legacy
7231      CUPS broadcast (1) or through DNS-SD (0) */
7232   p->is_legacy = 0;
7233 
7234   /* Initialize field for how many timeouts cups-browsed experienced
7235      in a row during creation of this printer's queue */
7236   p->timeouted = 0;
7237 
7238   /* Initialize nickname array for *Nickname directive from PPD
7239    * - either from CUPS server or from our PPD generator */
7240   p->nickname = NULL;
7241 
7242   /* Remote CUPS printer or local queue remaining from previous cups-browsed
7243      session */
7244   /* is_cups_queue: -1: Unknown, 0: IPP printer, 1: Remote CUPS queue,
7245                      2: Remote CUPS queue in user-defined cluster      */
7246   if (is_cups_queue != 0) {
7247     if (is_cups_queue > 0 && CreateRemoteCUPSPrinterQueues == 0) {
7248       debug_printf("Printer %s (%s) is a remote CUPS printer and cups-browsed is not configured to set up such printers automatically, ignoring this printer.\n",
7249 		   p->queue_name, p->uri);
7250       goto fail;
7251     }
7252     /* For a remote CUPS printer our local queue will be raw or get a
7253        PPD file from the remote CUPS server, so that the driver on the
7254        remote CUPS server gets used. So we will not generate a PPD file
7255        or interface script at this point. */
7256     p->netprinter = 0;
7257     if (p->uri[0] != '\0') {
7258       p->prattrs = get_printer_attributes(p->uri, NULL, 0, NULL, 0, 1);
7259       debug_log_out(get_printer_attributes_log);
7260       if (p->prattrs == NULL)
7261 	debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n",
7262 		     p->queue_name, p->uri);
7263     }
7264   } else {
7265 #ifndef HAVE_CUPS_1_6
7266     /* The following code uses a lot of CUPS >= 1.6 specific stuff.
7267        For older CUPS <= 1.5.4 the following functionality is skipped
7268        which means for CUPS <= 1.5.4 only for CUPS printer broadcasts
7269        there are local queues created which should be sufficient
7270        on systems where traditional CUPS <= 1.5.4 is used. */
7271     goto fail;
7272 #else /* HAVE_CUPS_1_6 */
7273     /* Non-CUPS printer broadcasts are most probably from printers
7274        directly connected to the network and using the IPP protocol.
7275        We check whether we can set them up without a device-specific
7276        driver, only using page description languages which the
7277        operating system provides: PCL 5c/5e/6/XL, PostScript, PDF, PWG
7278        Raster, Apple Raster, PCLm. Especially printers designed for
7279        driverless printing (DNS-SD + IPP 2.x + at least one of PWG
7280        Raster, Apple Raster, PCLm, PDF) will work this way. Making
7281        only driverless queues we can get an easy, configuration-less
7282        way to print from mobile devices, even if there is no CUPS
7283        server with shared printers around. */
7284 
7285     if (CreateIPPPrinterQueues == IPP_PRINTERS_NO) {
7286       debug_printf("Printer %s (%s) is an IPP network printer and cups-browsed is not configured to set up such printers automatically, ignoring this printer.\n",
7287 		   p->queue_name, p->uri);
7288       goto fail;
7289     }
7290 
7291     if (!pdl || pdl[0] == '\0' ||
7292 	(!strcasestr(pdl, "application/postscript") &&
7293 	 !strcasestr(pdl, "application/pdf") &&
7294 	 !strcasestr(pdl, "image/pwg-raster") &&
7295 #ifdef CUPS_RASTER_HAVE_APPLERASTER
7296 	 !strcasestr(pdl, "image/urf") &&
7297 #endif
7298 #ifdef QPDF_HAVE_PCLM
7299 	 !strcasestr(pdl, "application/PCLm") &&
7300 #endif
7301 	 ((!strcasestr(pdl, "application/vnd.hp-PCL") &&
7302 	   !strcasestr(pdl, "application/PCL") &&
7303 	   !strcasestr(pdl, "application/x-pcl")) ||
7304 	  ((!strncasecmp(make_model, "HP", 2) || /* HP inkjets not supported */
7305 	    !strncasecmp(make_model, "Hewlett Packard", 15) ||
7306 	    !strncasecmp(make_model, "Hewlett-Packard", 15)) &&
7307 	   !strcasestr(make_model, "LaserJet") &&
7308 	   !strcasestr(make_model, "Mopier"))) &&
7309 	 !strcasestr(pdl, "application/vnd.hp-PCLXL"))) {
7310       debug_printf("Cannot create remote printer %s (URI: %s, Model: %s, Accepted data formats: %s) as its PDLs are not known, ignoring this printer.\n",
7311 		   p->queue_name, p->uri, make_model, pdl);
7312       debug_printf("Supported PDLs: PWG Raster, %s%sPostScript, PDF, PCL XL, PCL 5c/e (HP inkjets report themselves as PCL printers but their PCL is not supported)\n",
7313 #ifdef CUPS_RASTER_HAVE_APPLERASTER
7314 		   "Apple Raster, ",
7315 #else
7316 		   "",
7317 #endif
7318 #ifdef QPDF_HAVE_PCLM
7319 		   "PCLm, "
7320 #else
7321 		   ""
7322 #endif
7323 		   );
7324       goto fail;
7325     }
7326 
7327     /* Check whether we have an equally named queue already */
7328     for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
7329 	 q;
7330 	 q = (remote_printer_t *)cupsArrayNext(remote_printers))
7331       if (!strcasecmp(q->queue_name, p->queue_name)) {/* Queue with same name */
7332 	debug_printf("We have already created a queue with the name %s for another printer. Skipping this printer.\n", p->queue_name);
7333 	debug_printf("Try setting \"LocalQueueNamingIPPPrinter DNS-SD\" in cups-browsed.conf.\n");
7334 	goto fail;
7335       }
7336 
7337     p->slave_of = NULL;
7338     p->netprinter = 1;
7339     p->prattrs = get_printer_attributes(p->uri, NULL, 0, NULL, 0, 1);
7340     debug_log_out(get_printer_attributes_log);
7341     if (p->prattrs == NULL) {
7342       debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n",
7343 		   p->queue_name, p->uri);
7344       goto fail;
7345     }
7346 
7347     /* If we have opted for only printers designed for driverless use (PWG
7348        Raster + Apple Raster + PCLm + PDF) being set up automatically, we check
7349        first, whether our printer supports IPP 2.0 or newer. If not, we
7350        skip this printer */
7351     if (CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7352       valuebuffer[0] = '\0';
7353       debug_printf("Checking whether printer %s supports IPP 2.x or newer:\n",
7354 		   p->queue_name);
7355       if ((attr = ippFindAttribute(p->prattrs,
7356 				   "ipp-versions-supported",
7357 				   IPP_TAG_KEYWORD)) != NULL) {
7358 	debug_printf("  Attr: %s\n", ippGetName(attr));
7359 	for (i = 0; i < ippGetCount(attr); i ++) {
7360 	  strncpy(valuebuffer, ippGetString(attr, i, NULL),
7361 		  sizeof(valuebuffer) - 1);
7362 	  if (strlen(ippGetString(attr, i, NULL)) > 65535)
7363 	    valuebuffer[65535] = '\0';
7364 	  debug_printf("  Keyword: %s\n", valuebuffer);
7365 	  if (valuebuffer[0] > '1')
7366 	    break;
7367 	}
7368       }
7369       if (!attr || valuebuffer[0] == '\0' || valuebuffer[0] <= '1') {
7370 	debug_printf("  --> cups-browsed is configured to auto-setup only printers which are designed for driverless printing. These printers require IPP 2.x or newer, but this printer only supports IPP 1.x or older. Skipping.\n");
7371 	goto fail;
7372       } else
7373 	debug_printf("  --> Printer supports IPP 2.x or newer.\n");
7374     }
7375 
7376     /* If we have opted for only PWG Raster printers or for only printers
7377        designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF)
7378        being set up automatically, we check whether the printer has a non-empty
7379        string in its "pwg-raster-document-resolution-supported" IPP attribute
7380        to see whether we have a PWG Raster printer. */
7381     if (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER ||
7382 	CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7383       valuebuffer[0] = '\0';
7384       debug_printf("Checking whether printer %s understands PWG Raster:\n",
7385 		   p->queue_name);
7386       if ((attr = ippFindAttribute(p->prattrs,
7387 				   "pwg-raster-document-resolution-supported",
7388 				   IPP_TAG_RESOLUTION)) != NULL) {
7389 	debug_printf("  Attr: %s\n", ippGetName(attr));
7390 	ippAttributeString(attr, valuebuffer, sizeof(valuebuffer));
7391 	debug_printf("  Value: %s\n", valuebuffer);
7392 	if (valuebuffer[0] == '\0') {
7393 	  for (i = 0; i < ippGetCount(attr); i ++) {
7394 	    strncpy(valuebuffer, ippGetString(attr, i, NULL),
7395 		    sizeof(valuebuffer) - 1);
7396 	    if (strlen(ippGetString(attr, i, NULL)) > 65535)
7397 	      valuebuffer[65535] = '\0';
7398 	    debug_printf("  Keyword: %s\n", valuebuffer);
7399 	    if (valuebuffer[0] != '\0')
7400 	      break;
7401 	  }
7402 	}
7403       }
7404       if (attr && valuebuffer[0] != '\0')
7405         is_pwgraster = 1;
7406       debug_printf("  --> Printer %s PWG Raster.\n",
7407 		   is_pwgraster ? "supports" : "does not support");
7408     }
7409 
7410 #ifdef CUPS_RASTER_HAVE_APPLERASTER
7411     /* If we have opted for only Apple Raster printers or for only printers
7412        designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF)
7413        being set up automatically, we check whether the printer has a non-empty
7414        string in its "urf-supported" IPP attribute to see whether we have an
7415        Apple Raster printer. */
7416     if (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER ||
7417 	CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7418       valuebuffer[0] = '\0';
7419       debug_printf("Checking whether printer %s understands Apple Raster:\n",
7420 		   p->queue_name);
7421       if ((attr = ippFindAttribute(p->prattrs, "urf-supported", IPP_TAG_KEYWORD)) != NULL) {
7422 	debug_printf("  Attr: %s\n", ippGetName(attr));
7423 	ippAttributeString(attr, valuebuffer, sizeof(valuebuffer));
7424 	debug_printf("  Value: %s\n", valuebuffer);
7425 	if (valuebuffer[0] == '\0') {
7426 	  for (i = 0; i < ippGetCount(attr); i ++) {
7427 	    strncpy(valuebuffer, ippGetString(attr, i, NULL),
7428 		    sizeof(valuebuffer) - 1);
7429 	    if (strlen(ippGetString(attr, i, NULL)) > 65535)
7430 	      valuebuffer[65535] = '\0';
7431 	    debug_printf("  Keyword: %s\n", valuebuffer);
7432 	    if (valuebuffer[0] != '\0')
7433 	      break;
7434 	  }
7435 	}
7436       }
7437       if (attr && valuebuffer[0] != '\0')
7438         is_appleraster = 1;
7439       debug_printf("  --> Printer %s Apple Raster.\n",
7440 		   is_appleraster ? "supports" : "does not support");
7441     }
7442 #endif
7443 
7444 #ifdef QPDF_HAVE_PCLM
7445     /* If we have opted for only PCLm printers or for only printers
7446        designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF)
7447        being set up automatically, we check whether the printer has a non-empty
7448        string in its "pclm-compression-method-preferred" IPP attribute to see
7449        whether we have a PCLm printer. */
7450     if (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM ||
7451 	CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7452       valuebuffer[0] = '\0';
7453       debug_printf("Checking whether printer %s understands PCLm:\n",
7454 		   p->queue_name);
7455       if ((attr = ippFindAttribute(p->prattrs,
7456 				   "pclm-compression-method-preferred",
7457 				   IPP_TAG_KEYWORD)) != NULL) {
7458 	debug_printf("  Attr: %s\n", ippGetName(attr));
7459 	ippAttributeString(attr, valuebuffer, sizeof(valuebuffer));
7460 	debug_printf("  Value: %s\n", p->queue_name, valuebuffer);
7461 	if (valuebuffer[0] == '\0') {
7462 	  for (i = 0; i < ippGetCount(attr); i ++) {
7463 	    strncpy(valuebuffer, ippGetString(attr, i, NULL),
7464 		    sizeof(valuebuffer) - 1);
7465 	    if (strlen(ippGetString(attr, i, NULL)) > 65535)
7466 	      valuebuffer[65535] = '\0';
7467 	    debug_printf("  Keyword: %s\n", valuebuffer);
7468 	    if (valuebuffer[0] != '\0')
7469 	      break;
7470 	  }
7471 	}
7472       }
7473       if (attr && valuebuffer[0] != '\0')
7474         is_pclm = 1;
7475       debug_printf("  --> Printer %s PCLm.\n",
7476 		   is_pclm ? "supports" : "does not support");
7477     }
7478 #endif
7479 
7480     /* If we have opted for only PDF printers or for only printers
7481        designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF)
7482        being set up automatically, we check whether the printer has
7483        "application/pdf" under its PDLs. */
7484     if (CreateIPPPrinterQueues == IPP_PRINTERS_PDF ||
7485 	CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7486       debug_printf("Checking whether printer %s understands PDF: PDLs: %s\n",
7487 		   p->queue_name, pdl);
7488       if(strcasestr(pdl, "application/pdf"))
7489         is_pdf = 1;
7490       debug_printf("  --> Printer %s PDF.\n",
7491 		   is_pdf ? "supports" : "does not support");
7492     }
7493 
7494     /* If the printer is not the driverless printer we opted for, we skip
7495        this printer. */
7496     if ((CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS &&
7497 	 is_pwgraster == 0 && is_appleraster == 0 && is_pclm == 0 &&
7498 	 is_pdf == 0) ||
7499 	(CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER &&
7500 	 is_pwgraster == 0) ||
7501 	(CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER &&
7502 	 is_appleraster == 0) ||
7503 	(CreateIPPPrinterQueues == IPP_PRINTERS_PCLM &&
7504 	 is_pclm == 0) ||
7505 	(CreateIPPPrinterQueues == IPP_PRINTERS_PDF &&
7506 	 is_pdf == 0)) {
7507       debug_printf("Printer %s (%s%s%s%s%s%s%s%s%s%s%s%s%s) does not support the driverless printing protocol cups-browsed is configured to accept for setting up such printers automatically, ignoring this printer.\n",
7508 		   p->queue_name, p->uri,
7509 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER ||
7510 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7511 		    ", " : ""),
7512 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER ||
7513 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7514 		    (is_pwgraster ? "" : "not ") : ""),
7515 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER ||
7516 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7517 		    "PWG Raster" : ""),
7518 		   (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER ||
7519 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7520 		    ", " : ""),
7521 		   (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER ||
7522 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7523 		    (is_appleraster ? "" : "not ") : ""),
7524 		   (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER ||
7525 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7526 		    "Apple Raster" : ""),
7527 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM ||
7528 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7529 		    ", " : ""),
7530 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM ||
7531 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7532 		    (is_pclm ? "" : "not ") : ""),
7533 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM ||
7534 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7535 		    "PCLm" : ""),
7536 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PDF ||
7537 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7538 		    ", " : ""),
7539 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PDF ||
7540 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7541 		    (is_pdf ? "" : "not ") : ""),
7542 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PDF ||
7543 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7544 		    "PDF" : ""));
7545       goto fail;
7546     }
7547 
7548 #endif /* HAVE_CUPS_1_6 */
7549   }
7550   /* Check whether we have an equally named queue already from another
7551      server and join a cluster if needed */
7552   if (join_cluster_if_needed(p, is_cups_queue) < 0)
7553     goto fail;
7554   /* Add the new remote printer entry */
7555   log_all_printers();
7556   cupsArrayAdd(remote_printers, p);
7557   log_all_printers();
7558 
7559   /* If auto shutdown is active we have perhaps scheduled a timer to shut down
7560      due to not having queues any more to maintain, kill the timer now */
7561   if (autoshutdown && autoshutdown_exec_id &&
7562       autoshutdown_on == NO_QUEUES &&
7563       cupsArrayCount(remote_printers) > 0) {
7564     debug_printf ("New printers there to make available, killing auto shutdown timer.\n");
7565     g_source_remove(autoshutdown_exec_id);
7566     autoshutdown_exec_id = 0;
7567   }
7568 
7569   if (http_printer)
7570     httpClose(http_printer);
7571   return p;
7572 
7573  fail:
7574   debug_printf("ERROR: Unable to create print queue, ignoring printer.\n");
7575   if (p->prattrs) ippDelete(p->prattrs);
7576   if (http_printer)
7577     httpClose(http_printer);
7578   if (p->type) free (p->type);
7579   if (p->service_name) free (p->service_name);
7580   if (p->host) free (p->host);
7581   if (p->resource) free (p->resource);
7582   if (p->domain) free (p->domain);
7583   cupsArrayDelete(p->ipp_discoveries);
7584   if (p->ip) free (p->ip);
7585   cupsFreeOptions(p->num_options, p->options);
7586   if (p->uri) free (p->uri);
7587   if (p->pdl) free (p->pdl);
7588   if (p->make_model) free (p->make_model);
7589   if (p->location) free (p->location);
7590   if (p->info) free (p->info);
7591   if (p->queue_name) free (p->queue_name);
7592   if (p->nickname) free (p->nickname);
7593   free (p);
7594   return NULL;
7595 }
7596 
7597 void
remove_printer_entry(remote_printer_t * p)7598 remove_printer_entry(remote_printer_t *p) {
7599   remote_printer_t *q = NULL, *r;
7600 
7601   if (p == NULL) {
7602     debug_printf ("ERROR: remove_printer_entry(): Supplied printer entry is NULL");
7603     return;
7604   }
7605 
7606   if (!p->slave_of) {
7607     /* Check whether this queue has a slave from another server and
7608        find it */
7609     for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
7610 	 q;
7611 	 q = (remote_printer_t *)cupsArrayNext(remote_printers))
7612       if (q != p && q->slave_of == p &&
7613 	  q->status != STATUS_DISAPPEARED && q->status != STATUS_UNCONFIRMED &&
7614 	  q->status != STATUS_TO_BE_RELEASED)
7615 	break;
7616   }
7617   if (q) {
7618     /* Make q the master of the cluster and p a slave of q. This way
7619        removal of p does not delete the cluster's CUPS queue and update
7620        of q makes sure the cluster's queue gets back into working state */
7621     for (r = (remote_printer_t *)cupsArrayFirst(remote_printers);
7622 	 r;
7623 	 r = (remote_printer_t *)cupsArrayNext(remote_printers))
7624       if (r != q && r->slave_of == p &&
7625 	  r->status != STATUS_DISAPPEARED && r->status != STATUS_UNCONFIRMED &&
7626 	  r->status != STATUS_TO_BE_RELEASED)
7627 	r->slave_of = q;
7628     q->slave_of = NULL;
7629     p->slave_of = q;
7630     q->num_options = p->num_options;
7631     q->options = p->options;
7632     p->num_options = 0;
7633     p->options = NULL;
7634     /* Schedule this printer for updating the CUPS queue */
7635     q->status = STATUS_TO_BE_CREATED;
7636     q->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
7637     debug_printf("Printer %s (%s) diasappeared, replacing by backup on host %s, port %d with URI %s.\n",
7638 		 p->queue_name, p->uri, q->host, q->port, q->uri);
7639   } else
7640     debug_printf("Printer %s (Host: %s, Port: %d, URI: %s) disappeared and no slave available (or it is a slave of another printer), removing entry.\n",
7641 		 p->queue_name, p->host, p->port, p->uri);
7642 
7643   /* Schedule entry and its CUPS queue for removal */
7644   if (p->status != STATUS_TO_BE_RELEASED)
7645     p->status = STATUS_DISAPPEARED;
7646   p->timeout = time(NULL) + TIMEOUT_REMOVE;
7647 }
7648 
update_cups_queues(gpointer unused)7649 gboolean update_cups_queues(gpointer unused) {
7650   remote_printer_t *p, *q, *r, *s, *master;
7651   http_t        *http;
7652   char          uri[HTTP_MAX_URI], device_uri[HTTP_MAX_URI], buf[1024],
7653                 line[1024];
7654   int           num_options;
7655   cups_option_t *options;
7656   int           num_jobs;
7657   cups_job_t    *jobs;
7658   ipp_t         *request;
7659   time_t        current_time;
7660   int           i, ap_remote_queue_id_line_inserted,
7661                 want_raw, num_cluster_printers = 0;
7662   char          *disabled_str;
7663   char          *ppdfile, *ifscript;
7664   int           fd = 0;  /* Script file descriptor */
7665   char          tempfile[1024];  /* Temporary file */
7666   char          buffer[8192];  /* Buffer for creating script */
7667   int           bytes;
7668   const char    *cups_serverbin;  /* CUPS_SERVERBIN environment variable */
7669 #ifdef HAVE_CUPS_1_6
7670   ipp_attribute_t *attr;
7671   int           count, left, right, bottom, top;
7672   const char    *default_page_size = NULL, *best_color_space = NULL,
7673                 *color_space;
7674 #endif
7675   const char    *loadedppd = NULL;
7676   ppd_file_t    *ppd = NULL;
7677   ppd_choice_t  *choice;
7678   cups_file_t   *in, *out;
7679   char          keyword[1024], *keyptr;
7680   const char    *customval;
7681   const char    *val = NULL;
7682   cups_dest_t   *dest = NULL;
7683   int           is_shared;
7684   cups_array_t  *conflicts = NULL;
7685   ipp_t         *printer_attributes = NULL;
7686   cups_array_t  *sizes=NULL;
7687   ipp_t         *printer_ipp_response;
7688   char          *make_model = NULL;
7689   const char    *pdl=NULL;
7690   int           color;
7691   int           duplex;
7692   char          *default_pagesize = NULL;
7693   const char    *default_color = NULL;
7694   int           cups_queues_updated = 0;
7695 
7696   /* Create dummy entry to point slaves at when their master is about to
7697      get removed now (if we point them to NULL, we would try to remove
7698      the already removed CUPS queue again when it comes to the removal
7699      of the slave. */
7700   if (deleted_master == NULL) {
7701     if ((deleted_master =
7702 	 (remote_printer_t *)calloc(1, sizeof(remote_printer_t))) == NULL) {
7703       debug_printf("ERROR: Unable to allocate memory.\n");
7704       if (in_shutdown == 0)
7705 	recheck_timer ();
7706       return FALSE;
7707     }
7708     memset(deleted_master, 0, sizeof(remote_printer_t));
7709     deleted_master->uri = "<DELETED>";
7710   }
7711 
7712   /* Now redirect the slave_of pointers of the masters which get deleted now
7713      to this dummy entry */
7714   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
7715        p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
7716     if ((p->status == STATUS_DISAPPEARED ||
7717 	 p->status == STATUS_TO_BE_RELEASED) &&
7718 	(q = p->slave_of) != NULL && q->queue_name &&
7719 	(q->status == STATUS_DISAPPEARED || q->status == STATUS_TO_BE_RELEASED))
7720       p->slave_of = deleted_master;
7721 
7722   debug_printf("Processing printer list ...\n");
7723   log_all_printers();
7724   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
7725        p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
7726 
7727     /* We need to get the current time as precise as possible for retries
7728        and reset the timeout flag */
7729     current_time = time(NULL);
7730     timeout_reached = 0;
7731 
7732     /* terminating means we have received a signal and should shut down.
7733        in_shutdown means we have exited the main loop.
7734        update_cups_queues() is called after having exited the main loop
7735        in order to remove any queues we have set up */
7736     if (terminating && !in_shutdown) {
7737       debug_printf("Stopping processing printer list because cups-browsed is terminating.\n");
7738       break;
7739     }
7740 
7741     /* We do not necessarily update all local CUPS queues which are
7742        scheduled for creation, update, or removal in a single call of
7743        the update_cups_queues() function, as then we could be stuck in
7744        this function for a long time and other tasks of cups-browsed,
7745        especially directing print jobs to destination printers before
7746        the implicitclass backend times out, will not get done in time.
7747        We schedule a new call of update_cups_queues() after a short
7748        delay to continue with the next local CUPS queues. */
7749     if (!in_shutdown && update_cups_queues_max_per_call > 0 &&
7750 	cups_queues_updated >= update_cups_queues_max_per_call) {
7751       debug_printf("Stopping processing printer list here because the update_cups_queues() function has reached its per-call limit of %d queue updates. Continuing in further calls.\n",
7752 		   update_cups_queues_max_per_call);
7753       break;
7754     }
7755 
7756     switch (p->status) {
7757 
7758       /* Print queue generated by us in a previous session */
7759     case STATUS_UNCONFIRMED:
7760 
7761       /* Only act if the timeout has passed */
7762       if (p->timeout > current_time)
7763 	break;
7764 
7765       /* Queue not reported again by DNS-SD, remove it */
7766       debug_printf("No remote printer named %s available, removing entry from previous session.\n",
7767 		   p->queue_name);
7768       remove_printer_entry(p);
7769 
7770     /* DNS-SD has reported this printer as disappeared or we have replaced
7771        this printer by another one */
7772     case STATUS_DISAPPEARED:
7773     case STATUS_TO_BE_RELEASED:
7774 
7775       /* Only act if the timeout has passed */
7776       if (p->timeout > current_time)
7777 	break;
7778 
7779       debug_printf("Removing entry %s (%s)%s.\n", p->queue_name, p->uri,
7780 		   (p->slave_of ||
7781 		    p->status == STATUS_TO_BE_RELEASED ? "" :
7782 		    " and its CUPS queue"));
7783 
7784       /* Slaves do not have a CUPS queue */
7785       if ((q = p->slave_of) == NULL) {
7786 	if ((http = http_connect_local ()) == NULL) {
7787 	  debug_printf("Unable to connect to CUPS!\n");
7788 	  if (in_shutdown == 0) {
7789             current_time = time(NULL);
7790 	    p->timeout = current_time + TIMEOUT_RETRY;
7791 	  }
7792 	  break;
7793 	}
7794 
7795 	/* Do not auto-save option settings due to the print queue removal
7796 	   process or release process */
7797 	p->no_autosave = 1;
7798 
7799 	/* Record the option settings to retrieve them when the remote
7800 	   queue re-appears later or when cups-browsed gets started again */
7801 	record_printer_options(p->queue_name);
7802 
7803 	if (p->status != STATUS_TO_BE_RELEASED &&
7804 	    !queue_overwritten(p)) {
7805 	  /* Remove the CUPS queue */
7806 
7807 	  /* Check whether there are still jobs and do not remove the queue
7808 	     then */
7809 	  num_jobs = 0;
7810 	  jobs = NULL;
7811 	  num_jobs = cupsGetJobs2(http, &jobs, p->queue_name, 0,
7812 				  CUPS_WHICHJOBS_ACTIVE);
7813 	  if (num_jobs > 0) { /* There are still jobs */
7814 	    debug_printf("Queue has still jobs or CUPS error!\n");
7815 	    cupsFreeJobs(num_jobs, jobs);
7816 	    /* Disable the queue */
7817 #ifdef HAVE_AVAHI
7818 	    if (avahi_present || p->domain == NULL || p->domain[0] == '\0')
7819 	      /* If avahi has got shut down, do not disable queues which are,
7820 		 created based on DNS-SD broadcasts as the server has most
7821 		 probably not gone away */
7822 #endif /* HAVE_AVAHI */
7823 	      disable_printer(p->queue_name,
7824 			      "Printer disappeared or cups-browsed shutdown");
7825 	    /* Schedule the removal of the queue for later */
7826 	    if (in_shutdown == 0) {
7827               current_time = time(NULL);
7828 	      p->timeout = current_time + TIMEOUT_RETRY;
7829 	      p->no_autosave = 0;
7830 	      break;
7831 	    } else
7832 	      /* Make sure queue's list entry gets freed */
7833 	      goto keep_queue;
7834 	  }
7835 
7836 	  /* If this queue was the default printer, note that fact so that
7837 	     it gets the default printer again when it re-appears, also switch
7838 	     back to the last local default printer */
7839 	  queue_removal_handle_default(p->queue_name);
7840 
7841 	  /* If we do not have a subscription to CUPS' D-Bus notifications and
7842 	     so no default printer management, we simply do not remove this
7843 	     CUPS queue if it is the default printer, to not cause a change
7844 	     of the default printer or the loss of the information that this
7845 	     printer is the default printer. */
7846 	  if (cups_notifier == NULL && is_cups_default_printer(p->queue_name)) {
7847 	    /* Schedule the removal of the queue for later */
7848 	    if (in_shutdown == 0) {
7849               current_time = time(NULL);
7850 	      p->timeout = current_time + TIMEOUT_RETRY;
7851 	      p->no_autosave = 0;
7852 	      break;
7853 	    } else
7854 	      /* Make sure queue's list entry gets freed */
7855 	      goto keep_queue;
7856 	  }
7857 
7858 	  /* No jobs, remove the CUPS queue */
7859 	  debug_printf("Removing local CUPS queue %s (%s).\n", p->queue_name,
7860 		       p->uri);
7861 	  request = ippNewRequest(CUPS_DELETE_PRINTER);
7862 	  /* Printer URI: ipp://localhost/printers/<queue name> */
7863 	  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
7864 			   "localhost", 0, "/printers/%s",
7865 			   p->queue_name);
7866 	  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
7867 		       "printer-uri", NULL, uri);
7868 	  /* Default user */
7869 	  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
7870 		       "requesting-user-name", NULL, cupsUser());
7871 	  /* Do it */
7872 	  ippDelete(cupsDoRequest(http, request, "/admin/"));
7873 
7874 	  cups_queues_updated ++;
7875 
7876 	  if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE &&
7877 	      cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND) {
7878 	    debug_printf("Unable to remove CUPS queue! (%s)\n",
7879 			 cupsLastErrorString());
7880 	    if (in_shutdown == 0) {
7881               current_time = time(NULL);
7882 	      p->timeout = current_time + TIMEOUT_RETRY;
7883 	      p->no_autosave = 0;
7884 	      break;
7885 	    }
7886 	  }
7887 	}
7888       }
7889 
7890     keep_queue:
7891 
7892       /* CUPS queue removed or released from cups-browsed, remove the list
7893 	 entry */
7894       /* Note that we do not need to break out of the loop passing through
7895 	 all elements of a CUPS array when we remove an element via the
7896 	 cupsArrayRemove() function, as the function decreases the array-
7897 	 internal index by one and so the cupsArrayNext() call gives us
7898 	 the element right after the deleted element. So no skipping
7899          of an element and especially no reading beyond the end of the
7900          array. */
7901       cupsArrayRemove(remote_printers, p);
7902       if (p->queue_name) free (p->queue_name);
7903       if (p->location) free (p->location);
7904       if (p->info) free (p->info);
7905       if (p->make_model) free (p->make_model);
7906       if (p->pdl) free (p->pdl);
7907       if (p->uri) free (p->uri);
7908       cupsFreeOptions(p->num_options, p->options);
7909       if (p->host) free (p->host);
7910       if (p->ip) free (p->ip);
7911       if (p->resource) free (p->resource);
7912       if (p->service_name) free (p->service_name);
7913       if (p->type) free (p->type);
7914       if (p->domain) free (p->domain);
7915       cupsArrayDelete(p->ipp_discoveries);
7916       if (p->prattrs) ippDelete (p->prattrs);
7917       if (p->nickname) free (p->nickname);
7918       free(p);
7919       p = NULL;
7920 
7921       /* If auto shutdown is active and all printers we have set up got removed
7922 	 again, schedule the shutdown in autoshutdown_timeout seconds
7923          Note that in this case we also do not have jobs any more so if we
7924          auto shutdown on running out of jobs, trigger it here, too. */
7925       if (in_shutdown == 0 && autoshutdown && !autoshutdown_exec_id &&
7926 	  (cupsArrayCount(remote_printers) == 0 ||
7927 	   (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
7928 	debug_printf ("No printers there any more to make available or no jobs, shutting down in %d sec...\n", autoshutdown_timeout);
7929 	autoshutdown_exec_id =
7930 	  g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
7931 				 NULL);
7932       }
7933 
7934       break;
7935 
7936     /* DNS-SD has reported a new remote printer, create a CUPS queue for it,
7937        or upgrade an existing queue, or update a queue to use a backup host
7938        when it has disappeared on the currently used host */
7939     /* (...or, we've just received a CUPS Browsing packet for this queue) */
7940     case STATUS_TO_BE_CREATED:
7941 
7942       /* Do not create a queue for slaves */
7943       if (p->slave_of) {
7944 	master = p->slave_of;
7945 	if (master->queue_name) {
7946 	  p->status = STATUS_CONFIRMED;
7947 	  master->status = STATUS_TO_BE_CREATED;
7948 	  master->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
7949 	  if (p->is_legacy) {
7950 	    p->timeout = time(NULL) + BrowseTimeout;
7951 	    debug_printf("starting BrowseTimeout timer for %s (%ds)\n",
7952 			 p->queue_name, BrowseTimeout);
7953 	  } else
7954 	    p->timeout = (time_t) -1;
7955 	} else {
7956 	  debug_printf("Master for slave %s is invalid (deleted?)\n",
7957 		       p->queue_name);
7958 	  p->status = STATUS_DISAPPEARED;
7959 	  p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
7960 	}
7961 	break;
7962       }
7963 
7964       /* Only act if the timeout has passed */
7965       if (p->timeout > current_time)
7966 	break;
7967 
7968       /* cups-browsed tried to add this print queue unsuccessfully for too
7969 	 many times due to timeouts - Skip print queue creation for this one */
7970       if (p->timeouted >= HttpMaxRetries) {
7971 	debug_printf("Max number of retries (%d) for creating print queue %s reached, skipping it.\n",
7972 		HttpMaxRetries, p->queue_name);
7973 	continue;
7974       }
7975 
7976       debug_printf("Creating/Updating CUPS queue %s\n",
7977 		   p->queue_name);
7978 
7979       /* Make sure to have a connection to the local CUPS daemon */
7980       if ((http = http_connect_local ()) == NULL) {
7981 	debug_printf("Unable to connect to CUPS!\n");
7982         current_time = time(NULL);
7983 	p->timeout = current_time + TIMEOUT_RETRY;
7984 	break;
7985       }
7986       httpSetTimeout(http, HttpLocalTimeout, http_timeout_cb, NULL);
7987 
7988       /* Do not auto-save option settings due to the print queue creation
7989 	 process */
7990       p->no_autosave = 1;
7991 
7992       /* Printer URI: ipp://localhost/printers/<queue name> */
7993       httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
7994 		       "localhost", 0, "/printers/%s", p->queue_name);
7995 
7996       ifscript = NULL;
7997       ppdfile = NULL;
7998 
7999 #ifdef HAVE_CUPS_1_6
8000       /* Check whether there is a temporary CUPS queue which we would
8001          overwrite */
8002       dest = NULL;
8003       if (OnlyUnsupportedByCUPS == 0)
8004         dest = cupsGetNamedDest(http, p->queue_name, NULL);
8005       if (dest) {
8006 	/* CUPS has found a queue with this name.
8007 	   Either CUPS generates a temporary queue here or we have already
8008 	   made this queue permanent. In any case, load the PPD from this
8009 	   queue to conserve the PPD which CUPS has originally generated. */
8010 	if (p->netprinter == 1 && IPPPrinterQueueType == PPD_YES &&
8011 	    UseCUPSGeneratedPPDs) {
8012 	  if (LocalQueueNamingIPPPrinter != LOCAL_QUEUE_NAMING_DNSSD) {
8013 	    debug_printf("Local queue %s: We can replace temporary CUPS queues and keep their PPD file only when we name our queues like them, to avoid duplicate queues to the same printer.\n",
8014 			 p->queue_name);
8015 	    debug_printf("Not loading PPD from temporary CUPS queue for this printer.\n");
8016 	    debug_printf("Try setting \"LocalQueueNamingIPPPrinter DNS-SD\" in cups-browsed.conf.\n");
8017 	  } else {
8018 	    /* This call makes CUPS actually create the queue so that we can
8019 	       grab the PPD. We discard the result of the call. */
8020 	    debug_printf("Establishing dummy connection to make CUPS create the temporary queue.\n");
8021 	    cups_dinfo_t *dinfo = cupsCopyDestInfo(http, dest);
8022 	    if (dinfo == NULL)
8023 	      debug_printf("Unable to connect to destination.\n");
8024 	    else {
8025 	      debug_printf("Temporary queue created, grabbing the PPD.\n");
8026 	      cupsFreeDestInfo(dinfo);
8027 	      loadedppd = NULL;
8028 	      if ((loadedppd = loadPPD(http, p->queue_name)) == NULL)
8029 		debug_printf("Unable to load PPD from local temporary queue %s!\n",
8030 			     p->queue_name);
8031 	      else {
8032 		ppdfile = strdup(loadedppd);
8033 		debug_printf("Loaded PPD file %s from local temporary queue %s.\n",
8034 			     ppdfile, p->queue_name);
8035 	      }
8036 	    }
8037 	  }
8038 	}
8039 	/* If we have already a temporary CUPS queue our local queue we
8040 	   are creating would overwrite the temporary queue, and so the
8041 	   resulting queue will still be considered temporary by CUPS and
8042 	   removed after one minute of inactivity. To avoid this we need
8043 	   to convert the queue into a permanent one and CUPS does this
8044 	   only by sharing the queue (setting its boolean printer-is-shared
8045 	   option. We unset the bit right after that to not actually share
8046 	   the queue (if we want to share the queue we take care about this
8047 	   later).
8048 	   Note that we cannot reliably determine whether we have a
8049 	   temporary queue via the printer-is-temporary attribute,
8050 	   therefore we consider only shared queues as for sure
8051 	   permanent and not shared queues as possibly temporary. To
8052 	   assure we have a permanent queue in the end we set and
8053 	   remove the shared bit on any queue which is not shared.
8054 	   If the temporary queue is pointing to a remote CUPS printer
8055 	   we cannot modify its printer-is-shared option as CUPS prevents
8056 	   this. In this case we remove the temporary queue so that we
8057 	   create a fresh one which will always be permanent.
8058 	   If the temporary queue has still jobs we will not remove it to
8059 	   not loose the jobs and wait with creating our new queue until
8060 	   the jobs are done. */
8061 	val = cupsGetOption ("printer-is-shared",
8062 			     dest->num_options,
8063 			     dest->options);
8064 	is_shared = val && (!strcasecmp (val, "yes") ||
8065 			    !strcasecmp (val, "on") ||
8066 			    !strcasecmp (val, "true"));
8067 	cupsFreeDests(1, dest);
8068 	if (!is_shared) {
8069 	  debug_printf("Our new queue overwrites the possibly temporary CUPS queue %s, so we need to assure the queue gets permanent.\n",
8070 		       p->queue_name);
8071 	  /* We need to modify the printer-is-shared bit twice if we need to
8072 	     make a temporary queue permanent but not share this queue */
8073 	  for (i = 0; i <= 1; i ++) {
8074 	    if (i == 0)
8075 	      debug_printf("Setting printer-is-shared bit to make this queue permanent.\n");
8076 	    else
8077 	      debug_printf("Unsetting printer-is-shared bit.\n");
8078 	    request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
8079 	    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8080 			 "printer-uri", NULL, uri);
8081 	    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8082 			 "requesting-user-name", NULL, cupsUser());
8083 	    num_options = 0;
8084 	    options = NULL;
8085 	    num_options = cupsAddOption("printer-is-shared",
8086 					(i == 0 ? "true" : "false"),
8087 					num_options, &options);
8088 	    num_options = cupsAddOption(CUPS_BROWSED_MARK "-default", "true", num_options, &options);
8089 	    cupsEncodeOptions2(request, num_options, options,
8090 			       IPP_TAG_OPERATION);
8091 	    cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
8092 	    /*
8093 	     * Do IPP request for printer-is-shared option only when we have
8094 	     * network printer or if we have remote CUPS queue, do IPP request
8095 	     * only if we have CUPS older than 2.2.
8096 	     * When you have remote queue, clean up and break from the loop.
8097 	     */
8098 	    if (p->netprinter != 0 || !HAVE_CUPS_2_2 || AllowResharingRemoteCUPSPrinters)
8099 	      ippDelete(cupsDoRequest(http, request, "/admin/"));
8100 	    else {
8101 	      ippDelete(request);
8102 	      cupsFreeOptions(num_options, options);
8103 	      break;
8104 	    }
8105 	    cupsFreeOptions(num_options, options);
8106 	    if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
8107 	      debug_printf("Unable change printer-is-shared bit to %s (%s)!\n",
8108 			   (i == 0 ? "true" : "false"),
8109 			   cupsLastErrorString());
8110 	      break;
8111 	    }
8112 	  }
8113 	  /* Error on modifying printer-is-shared bit, removing possibly
8114 	     temporary queue */
8115 	  if (i <= 1) {
8116 	    debug_printf("Removing the possibly temporary CUPS queue.\n");
8117 	    /* Check whether there are still jobs and do not remove the queue
8118 	       then */
8119 	    num_jobs = 0;
8120 	    jobs = NULL;
8121 	    num_jobs = cupsGetJobs2(http, &jobs, p->queue_name, 0,
8122 				    CUPS_WHICHJOBS_ACTIVE);
8123 	    if (num_jobs > 0) { /* there are still jobs */
8124 	      debug_printf("Temporary queue has still jobs or CUPS error, retrying later.\n");
8125 	      cupsFreeJobs(num_jobs, jobs);
8126 	      /* Schedule the removal of the queue for later */
8127 	      if (in_shutdown == 0) {
8128                 current_time = time(NULL);
8129 		p->timeout = current_time + TIMEOUT_RETRY;
8130 		p->no_autosave = 0;
8131 	      }
8132 	      break;
8133 	    }
8134 	    /* No jobs, remove the CUPS queue */
8135 	    request = ippNewRequest(CUPS_DELETE_PRINTER);
8136 	    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8137 			 "printer-uri", NULL, uri);
8138 	    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8139 			 "requesting-user-name", NULL, cupsUser());
8140 	    ippDelete(cupsDoRequest(http, request, "/admin/"));
8141 	    if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE &&
8142 		cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND) {
8143 	      debug_printf("Unable to remove temporary CUPS queue (%s), retrying later\n",
8144 			   cupsLastErrorString());
8145 	      if (in_shutdown == 0) {
8146                 current_time = time(NULL);
8147 		p->timeout = current_time + TIMEOUT_RETRY;
8148 		p->no_autosave = 0;
8149 		break;
8150 	      }
8151 	    }
8152 	  }
8153 	} else
8154 	  debug_printf("Creating/Updating permanent CUPS queue %s.\n",
8155 		       p ->queue_name);
8156       } else
8157 	debug_printf("Creating permanent CUPS queue %s.\n",
8158 		     p->queue_name);
8159 
8160       /* If we did not already obtain a PPD file from the temporary CUPS queue
8161          or if we want to use a System V interface script for our IPP network
8162 	 printer, we proceed here */
8163       if (p->netprinter == 1) {
8164 	if (p->prattrs == NULL) {
8165 	  p->prattrs = get_printer_attributes(p->uri, NULL, 0, NULL, 0, 1);
8166 	  debug_log_out(get_printer_attributes_log);
8167 	}
8168 	if (p->prattrs == NULL) {
8169 	  debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n",
8170 		       p->queue_name, p->uri);
8171 	  p->status = STATUS_DISAPPEARED;
8172           current_time = time(NULL);
8173 	  p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8174 	  goto cannot_create;
8175 	}
8176 	if (IPPPrinterQueueType == PPD_YES) {
8177 	  num_cluster_printers = 0;
8178 	  for (s = (remote_printer_t *)cupsArrayFirst(remote_printers);
8179 	       s; s = (remote_printer_t *)cupsArrayNext(remote_printers)) {
8180 	    if (!strcmp(s->queue_name, p->queue_name)) {
8181 	      if (s->status == STATUS_DISAPPEARED ||
8182 		  s->status == STATUS_UNCONFIRMED ||
8183 		  s->status == STATUS_TO_BE_RELEASED )
8184 		continue;
8185 	      num_cluster_printers ++;
8186 	    }
8187 	  }
8188 
8189 	  if (num_cluster_printers == 1) {
8190 	    printer_attributes = p->prattrs;
8191 	    conflicts = NULL;
8192 	    default_pagesize = NULL;
8193 	    default_color = NULL;
8194 	    make_model = p->make_model;
8195 	    pdl = p->pdl;
8196 	    color = p->color;
8197 	    duplex = p->duplex;
8198 	    sizes = NULL;
8199 	  } else {
8200 	    make_model = (char*)malloc(sizeof(char) * 256);
8201 	    printer_attributes = get_cluster_attributes(p->queue_name);
8202 	    if ((attr = ippFindAttribute(printer_attributes,
8203 					 "printer-make-and-model",
8204 					 IPP_TAG_TEXT)) != NULL)
8205 	      strncpy(make_model, ippGetString(attr, 0, NULL),
8206 		      sizeof(make_model) - 1);
8207 	    color = 0;
8208 	    duplex = 0;
8209 	    for (r = (remote_printer_t *)cupsArrayFirst(remote_printers);
8210 		 r; r = (remote_printer_t *)cupsArrayNext(remote_printers)) {
8211 	      if (!strcmp(p->queue_name, r->queue_name)) {
8212 		if (r->color == 1)
8213 		  color = 1;
8214 		if (r->duplex == 1)
8215 		  duplex = 1;
8216 	      }
8217 	    }
8218 	    default_pagesize = (char *)malloc(sizeof(char)*32);
8219 	    debug_printf("Generated Merged Attributes for local queue %s\n",
8220 			 p->queue_name);
8221 	    conflicts = generate_cluster_conflicts(p->queue_name,
8222 						   printer_attributes);
8223 	    debug_printf("Generated Constraints for queue %s\n",
8224 			 p->queue_name);
8225 	    sizes = get_cluster_sizes(p->queue_name);
8226 	    get_cluster_default_attributes(&printer_attributes,
8227 					   p->queue_name, default_pagesize,
8228 					   &default_color);
8229 	    debug_printf("Generated Default Attributes for local queue %s\n",
8230 			 p->queue_name);
8231 	  }
8232 	  if (ppdfile == NULL) {
8233 	    /* If we do not want CUPS-generated PPDs or we cannot obtain a
8234 	       CUPS-generated PPD, for example if CUPS does not create a
8235 	       temporary queue for this printer, we generate a PPD by
8236 	       ourselves */
8237 	    printer_ipp_response = (num_cluster_printers == 1) ? p->prattrs :
8238 	      printer_attributes;
8239 	    if (!ppdCreateFromIPP2(buffer, sizeof(buffer), printer_ipp_response,
8240 				   make_model,
8241 				   pdl, color, duplex, conflicts, sizes,
8242 				   default_pagesize, default_color)) {
8243 	      if (errno != 0)
8244 		debug_printf("Unable to create PPD file: %s\n",
8245 			     strerror(errno));
8246 	      else
8247 		debug_printf("Unable to create PPD file: %s\n",
8248 			     ppdgenerator_msg);
8249 	      p->status = STATUS_DISAPPEARED;
8250               current_time = time(NULL);
8251 	      p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8252 	      goto cannot_create;
8253 	    } else {
8254 	      debug_printf("PPD generation successful: %s\n", ppdgenerator_msg);
8255 	      debug_printf("Created temporary PPD file: %s\n", buffer);
8256 	      ppdfile = strdup(buffer);
8257 	    }
8258 	  }
8259 
8260 	  if (num_cluster_printers != 1) {
8261 	    if (default_pagesize != NULL) {
8262 	      free(default_pagesize);
8263 	      default_pagesize = NULL;
8264 	    }
8265 	    if (make_model != NULL) {
8266 	      free(make_model);
8267 	      make_model = NULL;
8268 	    }
8269 	    if (conflicts != NULL) {
8270 	      cupsArrayDelete(conflicts);
8271 	      conflicts = NULL;
8272 	    }
8273 	    if (printer_attributes != NULL) {
8274 	      ippDelete(printer_attributes);
8275 	      printer_attributes = NULL;
8276 	    }
8277 	    if (sizes != NULL) {
8278 	      cupsArrayDelete(sizes);
8279 	      sizes = NULL;
8280 	    }
8281 	  }
8282 	} else if (IPPPrinterQueueType == PPD_NO) {
8283 	  ppdfile = NULL;
8284 
8285 	  /* Find default page size of the printer */
8286 	  attr = ippFindAttribute(p->prattrs,
8287 				  "media-default",
8288 				  IPP_TAG_ZERO);
8289 	  if (attr) {
8290 	    default_page_size = ippGetString(attr, 0, NULL);
8291 	    debug_printf("Default page size: %s\n",
8292 			 default_page_size);
8293 	    p->num_options = cupsAddOption("media-default",
8294 					   default_page_size,
8295 					   p->num_options, &(p->options));
8296 	  } else {
8297 	    attr = ippFindAttribute(p->prattrs,
8298 				    "media-ready",
8299 				    IPP_TAG_ZERO);
8300 	    if (attr) {
8301 	      default_page_size = ippGetString(attr, 0, NULL);
8302 	      debug_printf("Default page size: %s\n",
8303 			   default_page_size);
8304 	      p->num_options = cupsAddOption("media-default",
8305 					     default_page_size,
8306 					     p->num_options, &(p->options));
8307 	    } else
8308 	      debug_printf("No default page size found!\n");
8309 	  }
8310 
8311 	  /* Find maximum unprintable margins of the printer */
8312 	  if ((attr = ippFindAttribute(p->prattrs,
8313 				       "media-bottom-margin-supported",
8314 				       IPP_TAG_INTEGER)) != NULL) {
8315 	    for (i = 1, bottom = ippGetInteger(attr, 0),
8316 		   count = ippGetCount(attr);
8317 		 i < count;
8318 		 i ++)
8319 	      if (ippGetInteger(attr, i) > bottom)
8320 		bottom = ippGetInteger(attr, i);
8321 	  } else
8322 	    bottom = 1270;
8323 	  snprintf(buffer, sizeof(buffer), "%d", bottom);
8324 	  p->num_options = cupsAddOption("media-bottom-margin-default",
8325 					 buffer,
8326 					 p->num_options, &(p->options));
8327 
8328 	  if ((attr = ippFindAttribute(p->prattrs,
8329 				       "media-left-margin-supported",
8330 				       IPP_TAG_INTEGER)) != NULL) {
8331 	    for (i = 1, left = ippGetInteger(attr, 0),
8332 		   count = ippGetCount(attr);
8333 		 i < count;
8334 		 i ++)
8335 	      if (ippGetInteger(attr, i) > left)
8336 		left = ippGetInteger(attr, i);
8337 	  } else
8338 	    left = 635;
8339 	  snprintf(buffer, sizeof(buffer), "%d", left);
8340 	  p->num_options = cupsAddOption("media-left-margin-default",
8341 					 buffer,
8342 					 p->num_options, &(p->options));
8343 
8344 	  if ((attr = ippFindAttribute(p->prattrs,
8345 				       "media-right-margin-supported",
8346 				       IPP_TAG_INTEGER)) != NULL) {
8347 	    for (i = 1, right = ippGetInteger(attr, 0),
8348 		   count = ippGetCount(attr);
8349 		 i < count;
8350 		 i ++)
8351 	      if (ippGetInteger(attr, i) > right)
8352 		right = ippGetInteger(attr, i);
8353 	  } else
8354 	    right = 635;
8355 	  snprintf(buffer, sizeof(buffer), "%d", right);
8356 	  p->num_options = cupsAddOption("media-right-margin-default",
8357 					 buffer,
8358 					 p->num_options, &(p->options));
8359 
8360 	  if ((attr = ippFindAttribute(p->prattrs,
8361 				       "media-top-margin-supported",
8362 				       IPP_TAG_INTEGER)) != NULL) {
8363 	    for (i = 1, top = ippGetInteger(attr, 0),
8364 		   count = ippGetCount(attr);
8365 		 i < count;
8366 		 i ++)
8367 	      if (ippGetInteger(attr, i) > top)
8368 		top = ippGetInteger(attr, i);
8369 	  } else
8370 	    top = 1270;
8371 	  snprintf(buffer, sizeof(buffer), "%d", top);
8372 	  p->num_options = cupsAddOption("media-top-margin-default",
8373 					 buffer,
8374 					 p->num_options, &(p->options));
8375 
8376 	  debug_printf("Margins: Left: %d, Right: %d, Top: %d, Bottom: %d\n",
8377 		       left, right, top, bottom);
8378 
8379 	  /* Find best color space of the printer */
8380 	  attr = ippFindAttribute(p->prattrs,
8381 				  "pwg-raster-document-type-supported",
8382 				  IPP_TAG_ZERO);
8383 	  if (attr) {
8384 	    for (i = 0; i < ippGetCount(attr); i ++) {
8385 	      color_space = ippGetString(attr, i, NULL);
8386 	      debug_printf("Supported color space: %s\n", color_space);
8387 	      if (color_space_score(color_space) >
8388 		  color_space_score(best_color_space))
8389 		best_color_space = color_space;
8390 	    }
8391 	    debug_printf("Best color space: %s\n",
8392 			 best_color_space);
8393 	    p->num_options = cupsAddOption("print-color-mode-default",
8394 					   best_color_space,
8395 					   p->num_options, &(p->options));
8396 	  } else {
8397 	    debug_printf("No info about supported color spaces found!\n");
8398 	    p->num_options = cupsAddOption("print-color-mode-default",
8399 					   p->color == 1 ? "rgb" : "black",
8400 					   p->num_options, &(p->options));
8401 	  }
8402 
8403 	  if (p->duplex)
8404 	    p->num_options = cupsAddOption("sides-default",
8405 					   "two-sided-long-edge",
8406 					   p->num_options, &(p->options));
8407 
8408 	  p->num_options = cupsAddOption("output-format-default",
8409 					 p->pdl,
8410 					 p->num_options, &(p->options));
8411 	  p->num_options = cupsAddOption("make-and-model-default",
8412 					 remove_bad_chars(p->make_model, 0),
8413 					 p->num_options, &(p->options));
8414 
8415 	  if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
8416 	    cups_serverbin = CUPS_SERVERBIN;
8417 
8418 	  if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) {
8419 	    debug_printf("Unable to create interface script file\n");
8420 	    p->status = STATUS_DISAPPEARED;
8421             current_time = time(NULL);
8422 	    p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8423 	    goto cannot_create;
8424 	  }
8425 
8426 	  debug_printf("Creating temp script file \"%s\"\n", tempfile);
8427 
8428 	  snprintf(buffer, sizeof(buffer),
8429 		   "#!/bin/sh\n"
8430 		   "# System V interface script for printer %s generated by cups-browsed\n"
8431 		   "\n"
8432 		   "if [ $# -lt 5 -o $# -gt 6 ]; then\n"
8433 		   "  echo \"ERROR: $0 job-id user title copies options [file]\" >&2\n"
8434 		   "  exit 1\n"
8435 		   "fi\n"
8436 		   "\n"
8437 		   "# Read from given file\n"
8438 		   "if [ -n \"$6\" ]; then\n"
8439 		   "  exec \"$0\" \"$1\" \"$2\" \"$3\" \"$4\" \"$5\" < \"$6\"\n"
8440 		   "fi\n"
8441 		   "\n"
8442 		   "%s/filter/sys5ippprinter \"$1\" \"$2\" \"$3\" \"$4\" \"$5\"\n",
8443 		   p->queue_name, cups_serverbin);
8444 
8445 	  bytes = write(fd, buffer, strlen(buffer));
8446 	  if (bytes != strlen(buffer)) {
8447 	    debug_printf("Unable to write interface script into the file\n");
8448 	    p->status = STATUS_DISAPPEARED;
8449             current_time = time(NULL);
8450 	    p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8451 	    goto cannot_create;
8452 	  }
8453 
8454 	  close(fd);
8455 
8456 	  ifscript = strdup(tempfile);
8457 	}
8458       }
8459 #endif /* HAVE_CUPS_1_6 */
8460 
8461       /* Do we have default option settings in cups-browsed.conf? */
8462       if (DefaultOptions) {
8463 	debug_printf("Applying default option settings to printer %s: %s\n",
8464 		     p->queue_name, DefaultOptions);
8465 	p->num_options = cupsParseOptions(DefaultOptions, p->num_options,
8466 					  &p->options);
8467       }
8468 
8469       /* Loading saved option settings from last session */
8470       p->num_options = load_printer_options(p->queue_name, p->num_options,
8471 					    &p->options);
8472 
8473       /* Determine whether we have an IPP network printer. If not we
8474 	 have remote CUPS queue(s) and so we use an implicit class for
8475 	 load balancing. In this case we will assign an
8476 	 implicitclass://...  device URI, which makes cups-browsed find
8477 	 the best destination for each job. */
8478       loadedppd = NULL;
8479       if (cups_notifier != NULL && p->netprinter == 0) {
8480 	/* We are not an IPP network printer, so we use the device URI
8481 	   implicitclass://<queue name>/
8482 	   We use the httpAssembleURI() function here, to percent-encode
8483 	   the queue name in the URI, so that any allowed character in
8484 	   a queue name, especially the '@' when we add the server name
8485 	   to a remote queue's name, goes safely into the URI.
8486 	   The implicitclass backend uses httpSeparateURI() to decode the
8487 	   queue name.
8488 	   We never use the implicitclass backend if we do not have D-Bus
8489 	   notification from CUPS as we cannot assign a destination printer
8490 	   to an incoming job then. */
8491 	httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
8492 			"implicitclass", NULL, p->queue_name, 0, NULL);
8493 	debug_printf("Print queue %s is for remote CUPS queue(s) and we get notifications from CUPS, using implicit class device URI %s\n",
8494 		     p->queue_name, device_uri);
8495 	if (!ppdfile && !ifscript) {
8496 	  /* Having another backend than the CUPS "ipp" backend the
8497 	     options from the PPD of the queue on the server are not
8498 	     automatically used on the client any more, so we have to
8499 	     explicitly load the PPD from one of the servers, apply it
8500 	     to our local queue, and replace its "*cupsFilter(2): ..."
8501 	     lines by one line making the print data get passed through
8502 	     to the server without filtering on the client (where not
8503 	     necessarily the right filters/drivers are installed) so
8504 	     that it gets filtered on the server. In addition, we prefix
8505 	     the PPD's NickName, so that automatic PPD updating by the
8506 	     distribution's package installation/update infrastructure
8507 	     is suppressed. */
8508 	  /* Generating the ppd file for the remote cups queue */
8509 	  if (p->prattrs == NULL) {
8510 	    p->prattrs = get_printer_attributes(p->uri, NULL, 0, NULL, 0, 1);
8511 	    debug_log_out(get_printer_attributes_log);
8512 	  }
8513 	  if (p->prattrs == NULL) {
8514 	    debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n",
8515 			 p->queue_name, p->uri);
8516 	    goto cannot_create;
8517 	  }
8518 	  num_cluster_printers = 0;
8519 	  for (s = (remote_printer_t *)cupsArrayFirst(remote_printers);
8520 	       s; s = (remote_printer_t *)cupsArrayNext(remote_printers)) {
8521 	    if (!strcmp(s->queue_name, p->queue_name)) {
8522 	      if (s->status == STATUS_DISAPPEARED ||
8523 		  s->status == STATUS_UNCONFIRMED ||
8524 		  s->status == STATUS_TO_BE_RELEASED )
8525 		continue;
8526 	      num_cluster_printers++;
8527 	    }
8528 	  }
8529 	  if (num_cluster_printers == 1) {
8530 	    printer_attributes = p->prattrs;
8531 	    conflicts = NULL;
8532 	    default_pagesize = NULL;
8533 	    default_color = NULL;
8534 	    make_model = p->make_model;
8535 	    pdl = p->pdl;
8536 	    color = p->color;
8537 	    duplex = p->duplex;
8538 	    sizes = NULL;
8539 	  } else {
8540 	    make_model = (char*)malloc(sizeof(char)*256);
8541 	    printer_attributes = get_cluster_attributes(p->queue_name);
8542 	    if((attr = ippFindAttribute(printer_attributes,
8543 					"printer-make-and-model",
8544 					IPP_TAG_TEXT)) != NULL)
8545 	      strncpy(make_model, ippGetString(attr, 0, NULL),
8546 		      sizeof(make_model) - 1);
8547 	    color = 0;
8548 	    duplex = 0;
8549 	    for (r = (remote_printer_t *)cupsArrayFirst(remote_printers);
8550 		 r; r = (remote_printer_t *)cupsArrayNext(remote_printers)) {
8551 	      if (!strcmp(p->queue_name, r->queue_name)) {
8552 		if (r->color == 1)
8553 		  color = 1;
8554 		if (r->duplex == 1)
8555 		  duplex = 1;
8556 	      }
8557 	    }
8558 	    default_pagesize = (char *)malloc(sizeof(char)*32);
8559 	    debug_printf("Generated Merged Attributes for local queue %s\n",
8560 			 p->queue_name);
8561 	    conflicts = generate_cluster_conflicts(p->queue_name,
8562 						   printer_attributes);
8563 	    debug_printf("Generated Constraints for queue %s\n",p->queue_name);
8564 	    sizes = get_cluster_sizes(p->queue_name);
8565 	    get_cluster_default_attributes(&printer_attributes, p->queue_name,
8566 					   default_pagesize,&default_color);
8567 	    debug_printf("Generated Default Attributes for local queue %s\n",
8568 			 p->queue_name);
8569 	  }
8570 	  if (ppdfile == NULL) {
8571 	    /* If we do not want CUPS-generated PPDs or we cannot obtain a
8572 	       CUPS-generated PPD, for example if CUPS does not create a
8573 	       temporary queue for this printer, we generate a PPD by
8574 	       ourselves */
8575 	    printer_ipp_response = (num_cluster_printers == 1) ? p->prattrs :
8576 	      printer_attributes;
8577 	    if (!ppdCreateFromIPP2(buffer, sizeof(buffer), printer_ipp_response,
8578 				   make_model,
8579 				   pdl, color, duplex, conflicts, sizes,
8580 				   default_pagesize, default_color)) {
8581 	      if (errno != 0)
8582 		debug_printf("Unable to create PPD file: %s\n",
8583 			     strerror(errno));
8584 	      else
8585 		debug_printf("Unable to create PPD file: %s\n", ppdgenerator_msg);
8586 	      p->status = STATUS_DISAPPEARED;
8587 	      current_time = time(NULL);
8588 	      p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8589 	      goto cannot_create;
8590 	    } else {
8591 	      debug_printf("PPD generation successful: %s\n", ppdgenerator_msg);
8592 	      debug_printf("Created temporary PPD file: %s\n", buffer);
8593 	      ppdfile = strdup(buffer);
8594 	    }
8595 	  }
8596 	}
8597 
8598 	if (num_cluster_printers != 1) {
8599 	  if (default_pagesize != NULL) {
8600 	    free(default_pagesize);
8601 	    default_pagesize = NULL;
8602 	  }
8603 	  if (make_model != NULL) {
8604 	    free(make_model);
8605 	    make_model = NULL;
8606 	  }
8607 	  if (conflicts != NULL) {
8608 	    cupsArrayDelete(conflicts);
8609 	    conflicts = NULL;
8610 	  }
8611 	  if (printer_attributes != NULL) {
8612 	    ippDelete(printer_attributes);
8613 	    printer_attributes = NULL;
8614 	  }
8615 	  if (sizes != NULL) {
8616 	    cupsArrayDelete(sizes);
8617 	    sizes = NULL;
8618 	  }
8619 	}
8620       } else {
8621 	/* Device URI: using implicitclass backend for IPP network printer */
8622 	httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
8623 			"implicitclass", NULL, p->queue_name, 0, NULL);
8624 	if (strlen(device_uri) > HTTP_MAX_URI-1)
8625 	  device_uri[HTTP_MAX_URI-1] = '\0';
8626 	debug_printf("Print queue %s is for an IPP network printer, using implicitclass backend for the printer: %s\n",
8627 		     p->queue_name, device_uri);
8628       }
8629 
8630       /* PPD readily available */
8631       if (ppdfile) {
8632 	debug_printf("Using PPD %s for queue %s.\n",
8633 		     ppdfile, p->queue_name);
8634 	loadedppd = ppdfile;
8635       }
8636       if (loadedppd) {
8637 	if ((ppd = ppdOpenFile(loadedppd)) == NULL) {
8638 	  int linenum; /* Line number of error */
8639 	  ppd_status_t status = ppdLastError(&linenum);
8640 	  debug_printf("Unable to open PPD \"%s\": %s on line %d.",
8641 		       loadedppd, ppdErrorString(status), linenum);
8642           current_time = time(NULL);
8643 	  p->timeout = current_time + TIMEOUT_RETRY;
8644 	  p->no_autosave = 0;
8645 	  unlink(loadedppd);
8646 	  break;
8647 	}
8648 	ppdMarkDefaults(ppd);
8649 	cupsMarkOptions(ppd, p->num_options, p->options);
8650 	if ((out = cupsTempFile2(buf, sizeof(buf))) == NULL) {
8651 	  debug_printf("Unable to create temporary file!\n");
8652           current_time = time(NULL);
8653 	  p->timeout = current_time + TIMEOUT_RETRY;
8654 	  p->no_autosave = 0;
8655 	  ppdClose(ppd);
8656           ppd = NULL;
8657 	  unlink(loadedppd);
8658 	  break;
8659 	}
8660 	if ((in = cupsFileOpen(loadedppd, "r")) == NULL) {
8661 	  debug_printf("Unable to open the downloaded PPD file!\n");
8662           current_time = time(NULL);
8663 	  p->timeout = current_time + TIMEOUT_RETRY;
8664 	  p->no_autosave = 0;
8665 	  cupsFileClose(out);
8666 	  ppdClose(ppd);
8667           ppd = NULL;
8668 	  unlink(loadedppd);
8669 	  break;
8670 	}
8671 	debug_printf("Editing PPD file %s for printer %s, setting the option defaults of the previous cups-browsed session%s, saving the resulting PPD in %s.\n",
8672 		     loadedppd, p->queue_name,
8673 		     " and doing client-side filtering of the job" ,
8674 		     buf);
8675 	ap_remote_queue_id_line_inserted = 0;
8676 	while (cupsFileGets(in, line, sizeof(line))) {
8677 	  if (!strncmp(line, "*Default", 8)) {
8678 	    strncpy(keyword, line + 8, sizeof(keyword) - 1);
8679 	    if ((strlen(line) + 8) > 1023)
8680 	      keyword[1023] = '\0';
8681 	    for (keyptr = keyword; *keyptr; keyptr ++)
8682 	      if (*keyptr == ':' || isspace(*keyptr & 255))
8683 		break;
8684 	    *keyptr++ = '\0';
8685 	    while (isspace(*keyptr & 255))
8686 	      keyptr ++;
8687 	    if (!strcmp(keyword, "PageRegion") ||
8688 		!strcmp(keyword, "PageSize") ||
8689 		!strcmp(keyword, "PaperDimension") ||
8690 		!strcmp(keyword, "ImageableArea")) {
8691 	      if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) == NULL)
8692 		choice = ppdFindMarkedChoice(ppd, "PageRegion");
8693 	    } else
8694 	      choice = ppdFindMarkedChoice(ppd, keyword);
8695 	    if (choice && strcmp(choice->choice, keyptr)) {
8696 	      if (strcmp(choice->choice, "Custom"))
8697 		cupsFilePrintf(out, "*Default%s: %s\n", keyword,
8698 			       choice->choice);
8699 	      else if ((customval = cupsGetOption(keyword, p->num_options,
8700 						  p->options)) != NULL)
8701 		cupsFilePrintf(out, "*Default%s: %s\n", keyword, customval);
8702 	      else
8703 		cupsFilePrintf(out, "%s\n", line);
8704 	    } else
8705 	      cupsFilePrintf(out, "%s\n", line);
8706 	  } else if (strncmp(line, "*End", 4)) {
8707 	    /* Write an "APRemoteQueueID" line to make this queue marked
8708 	       as remote printer by CUPS */
8709 	    if (p->netprinter == 0 &&
8710 		strncmp(line, "*%", 2) &&
8711 		strncmp(line, "*PPD-Adobe:", 11) &&
8712 		ap_remote_queue_id_line_inserted == 0 &&
8713               !AllowResharingRemoteCUPSPrinters) {
8714 	      ap_remote_queue_id_line_inserted = 1;
8715 	      cupsFilePrintf(out, "*APRemoteQueueID: \"\"\n");
8716 	    }
8717 	    /* Simply write out the line as we read it */
8718 	    cupsFilePrintf(out, "%s\n", line);
8719 	  }
8720 	  /* Save the NickName of the PPD to check whether external
8721 	     manipulations of the print queue have replaced the PPD.
8722 	     Check whether nickname is defined too */
8723 	  if (!strncmp(line, "*NickName:", 10) && p->nickname == NULL) {
8724 	    char *ptr = NULL;
8725 	    char *end_ptr = NULL;
8726 	    int nickname_len = 0;
8727 
8728 	    ptr = strchr(line, '"');
8729 
8730 	    if (ptr == NULL)
8731 	    {
8732 	      debug_printf("Malformed *Nickname directive in PPD - no double quote in line.\n");
8733 	      continue;
8734 	    }
8735 
8736 	    ptr ++;
8737 	    end_ptr = strchr(ptr, '"');
8738 
8739 	    if (end_ptr == NULL)
8740 	    {
8741 	      debug_printf("Malformed *Nickname directive in PPD - no ending double quote\n");
8742 	      continue;
8743 	    }
8744 
8745 	    /* both pointers are null terminated, because cupsFileGets() puts
8746 	     * a null terminator into returned buffer with one line
8747 	     * here as 'line' array) and those two pointers points on two places
8748 	     * in the 'line' array.
8749 	     */
8750 	    nickname_len = strlen(ptr) - strlen(end_ptr);
8751 
8752 	    if (nickname_len == 0)
8753 	    {
8754 	      debug_printf("Malformed *Nickname directive in PPD - empty nickname.\n");
8755 	      continue;
8756 	    }
8757 
8758 	    /* alloc one more space for null terminator, calloc() will initialize
8759 	     * it to null automatically, so then we only copy a string with 'nickname_len'
8760 	     * length to get a proper null terminated p->nickname.
8761 	     */
8762 	    p->nickname = (char*)calloc(nickname_len + 1, sizeof(char));
8763 
8764 	    if (p->nickname != NULL)
8765 	      strncpy(p->nickname, ptr, nickname_len);
8766 	  }
8767 	}
8768 	cupsFilePrintf(out,"*cupsFilter2: \"application/vnd.cups-pdf application/pdf 0 -\"\n");
8769 
8770 	cupsFileClose(in);
8771 	cupsFileClose(out);
8772 	ppdClose(ppd);
8773         ppd = NULL;
8774 	unlink(loadedppd);
8775 	loadedppd = NULL;
8776 	if (ppdfile)
8777         {
8778 	  free(ppdfile);
8779           ppdfile = NULL;
8780         }
8781 	ppdfile = strdup(buf);
8782       }
8783 
8784       /* Create a new CUPS queue or modify the existing queue */
8785       request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
8786       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8787 		   "printer-uri", NULL, uri);
8788       /* Default user */
8789       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8790 		   "requesting-user-name", NULL, cupsUser());
8791       /* Queue should be enabled ... */
8792       ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
8793 		    IPP_PRINTER_IDLE);
8794       /* ... and accepting jobs */
8795       ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
8796       num_options = 0;
8797       options = NULL;
8798       /* Device URI: ipp(s)://<remote host>:631/printers/<remote queue>
8799          OR          implicitclass://<queue name>/ */
8800       num_options = cupsAddOption("device-uri", device_uri,
8801 				  num_options, &options);
8802       /* Option cups-browsed=true, marking that we have created this queue */
8803       num_options = cupsAddOption(CUPS_BROWSED_MARK "-default", "true",
8804 				  num_options, &options);
8805       /* Default option settings from printer entry */
8806       for (i = 0; i < p->num_options; i ++)
8807 	if (strcasecmp(p->options[i].name, "printer-is-shared"))
8808 	  num_options = cupsAddOption(p->options[i].name,
8809 				      p->options[i].value,
8810 				      num_options, &options);
8811       /* Encode option list into IPP attributes */
8812       cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
8813       cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
8814       /* Do it */
8815       if (ppdfile) {
8816 	debug_printf("Non-raw queue %s with PPD file: %s\n", p->queue_name, ppdfile);
8817 	ippDelete(cupsDoFileRequest(http, request, "/admin/", ppdfile));
8818 	want_raw = 0;
8819 	unlink(ppdfile);
8820 	free(ppdfile);
8821 	ppdfile = NULL;
8822       } else if (ifscript) {
8823 	debug_printf("Non-raw queue %s with interface script: %s\n", p->queue_name, ifscript);
8824 	ippDelete(cupsDoFileRequest(http, request, "/admin/", ifscript));
8825 	want_raw = 0;
8826 	unlink(ifscript);
8827 	free(ifscript);
8828 	ifscript = NULL;
8829       } else {
8830 	if (p->netprinter == 0) {
8831 	  debug_printf("Raw queue %s\n", p->queue_name);
8832 	  want_raw = 1;
8833 	} else {
8834 	  debug_printf("Queue %s keeping its current PPD file/interface script\n", p->queue_name);
8835 	  want_raw = 0;
8836 	}
8837 	ippDelete(cupsDoRequest(http, request, "/admin/"));
8838       }
8839       cupsFreeOptions(num_options, options);
8840       cups_queues_updated ++;
8841 
8842       if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
8843 	debug_printf("Unable to create/modify CUPS queue (%s)!\n",
8844 		     cupsLastErrorString());
8845         current_time = time(NULL);
8846 	p->timeout = current_time + TIMEOUT_RETRY;
8847 	p->no_autosave = 0;
8848 	break;
8849       }
8850 
8851       /* Do not share a queue which serves only to point to a remote CUPS
8852 	 printer
8853 
8854 	 We do this in a seperate IPP request as on newer CUPS versions we
8855          get an error when changing the printer-is-shared bit on a queue
8856          pointing to a remote CUPS printer, this way we assure all other
8857 	 settings be applied amd when setting the printer-is-shared to
8858          false amd this errors, we can safely ignore the error as on queues
8859 	 pointing to remote CUPS printers the bit is set to false by default
8860 	 (these printers are never shared)
8861 
8862 	 If our printer is an IPP network printer and not a CUPS queue, we
8863          keep track of whether the user has changed the printer-is-shared
8864          bit and recover this setting. The default setting for a new
8865          queue is configurable via the NewIPPPrinterQueuesShared directive
8866          in cups-browsed.conf */
8867 
8868       request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
8869       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8870 		   "printer-uri", NULL, uri);
8871       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8872 		   "requesting-user-name", NULL, cupsUser());
8873       num_options = 0;
8874       options = NULL;
8875       if (p->netprinter == 1 &&
8876 	  (val = cupsGetOption("printer-is-shared", p->num_options,
8877 			       p->options)) != NULL) {
8878 	num_options = cupsAddOption("printer-is-shared", val,
8879 				    num_options, &options);
8880 	debug_printf("Setting printer-is-shared bit to %s.\n", val);
8881       } else if (p->netprinter == 1 && NewIPPPrinterQueuesShared) {
8882 	num_options = cupsAddOption("printer-is-shared", "true",
8883 				    num_options, &options);
8884 	debug_printf("Setting printer-is-shared bit.\n");
8885       } else if (NewBrowsePollQueuesShared &&
8886       (val = cupsGetOption("printer-to-be-shared", p->num_options,
8887                p->options)) != NULL) {
8888 	num_options = cupsAddOption("printer-is-shared", "true",
8889 				    num_options, &options);
8890 	debug_printf("Setting printer-is-shared bit.\n");
8891       } else {
8892 	num_options = cupsAddOption("printer-is-shared", "false",
8893 				    num_options, &options);
8894 	debug_printf("Unsetting printer-is-shared bit.\n");
8895       }
8896       cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
8897       cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
8898       /*
8899        * Do IPP request for printer-is-shared option only when we have
8900        * network printer or if we have remote CUPS queue, do IPP request
8901        * only if we have CUPS older than 2.2.
8902        */
8903       if (p->netprinter != 0 || !HAVE_CUPS_2_2 || AllowResharingRemoteCUPSPrinters)
8904         ippDelete(cupsDoRequest(http, request, "/admin/"));
8905       else
8906         ippDelete(request);
8907       cupsFreeOptions(num_options, options);
8908       if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
8909 	debug_printf("Unable to modify the printer-is-shared bit (%s)!\n",
8910 		     cupsLastErrorString());
8911 
8912       /* If we are about to create a raw queue or turn a non-raw queue
8913 	 into a raw one, we apply the "ppd-name=raw" option to remove any
8914 	 existing PPD file assigned to the queue.
8915 
8916          Also here we do a separate IPP request as it errors in some
8917          cases. */
8918       if (want_raw) {
8919 	debug_printf("Removing local PPD file for printer %s\n", p->queue_name);
8920 	request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
8921 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8922 		     "printer-uri", NULL, uri);
8923 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8924 		     "requesting-user-name", NULL, cupsUser());
8925 	num_options = 0;
8926 	options = NULL;
8927 	num_options = cupsAddOption("ppd-name", "raw",
8928 				    num_options, &options);
8929 	cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
8930 	cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
8931 	ippDelete(cupsDoRequest(http, request, "/admin/"));
8932 	cupsFreeOptions(num_options, options);
8933 	if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
8934 	  debug_printf("Unable to remove PPD file from the print queue (%s)!\n",
8935 		       cupsLastErrorString());
8936       }
8937 
8938       /* If this queue was the default printer in its previous life, make
8939 	 it the default printer again. */
8940       queue_creation_handle_default(p->queue_name);
8941 
8942       /* If cups-browsed or a failed backend has disabled this
8943 	 queue, re-enable it. */
8944       if ((disabled_str = is_disabled(p->queue_name, "cups-browsed")) != NULL) {
8945 	enable_printer(p->queue_name);
8946 	free(disabled_str);
8947       } else if ((disabled_str =
8948 		  is_disabled(p->queue_name,
8949 			      "Printer stopped due to backend errors")) !=
8950 		 NULL) {
8951 	enable_printer(p->queue_name);
8952 	free(disabled_str);
8953       }
8954 
8955       p->status = STATUS_CONFIRMED;
8956       if (p->is_legacy) {
8957 	p->timeout = time(NULL) + BrowseTimeout;
8958 	debug_printf("starting BrowseTimeout timer for %s (%ds)\n",
8959 		     p->queue_name, BrowseTimeout);
8960       } else
8961 	p->timeout = (time_t) -1;
8962 
8963       /* Check if an HTTP timeout happened during the print queue creation
8964 	 If it does - increment p->timeouted and set status to TO_BE_CREATED
8965 	 because the creation can fall through the process, have state changed
8966 	 to STATUS_CONFIRMED and experience the timeout */
8967       /* If no timeout has happened, clear p->timeouted */
8968       if (timeout_reached == 1) {
8969 	debug_printf("Timeout happened during creation of the queue %s.\n",
8970 		     p->queue_name);
8971 	p->timeouted ++;
8972 	debug_printf("The queue %s already timeouted %d times in a row.\n",
8973 		     p->queue_name, p->timeouted);
8974 	p->status = STATUS_TO_BE_CREATED;
8975 	p->timeout = current_time + TIMEOUT_RETRY;
8976       } else if (p->timeouted != 0) {
8977 	debug_printf("Creating the queue %s went smoothly after %d timeouts.\n",
8978 		     p->queue_name, p->timeouted);
8979 	p->timeouted = 0;
8980       }
8981 
8982       p->no_autosave = 0;
8983       break;
8984 
8985     case STATUS_CONFIRMED:
8986       /* Only act if the timeout has passed */
8987       if (p->timeout > current_time)
8988 	break;
8989 
8990       if (p->is_legacy) {
8991 	/* Remove a queue based on a legacy CUPS broadcast when the
8992 	   broadcast timeout expires without a new broadcast of this
8993 	   queue from the server */
8994 	remove_printer_entry(p);
8995       } else
8996 	p->timeout = (time_t) -1;
8997 
8998       break;
8999 
9000     }
9001   }
9002 
9003   /* If we have printer entries which we did not treat yet because of
9004      update_cups_queues_max_per_call we push their timeouts by the
9005      value of pause_between_cups_queue_updates into the future, so
9006      that they only get worked on then. Also printer entries which are
9007      scheduled in a time less than the value of
9008      pause_between_cups_queue_updates will be pushed, so that
9009      update_cups_queues will run the next time only after this
9010      interval */
9011   if (p && !in_shutdown)
9012     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
9013 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
9014       if (p->timeout <= current_time + pause_between_cups_queue_updates)
9015 	p->timeout = current_time + pause_between_cups_queue_updates;
9016 
9017  cannot_create:
9018   if (printer_attributes != NULL && num_cluster_printers != 1)
9019     ippDelete(printer_attributes);
9020 
9021   if (default_pagesize != NULL && num_cluster_printers != 1)
9022     free(default_pagesize);
9023 
9024   if (conflicts != NULL && num_cluster_printers != 1)
9025     cupsArrayDelete(conflicts);
9026 
9027   if (make_model != NULL && num_cluster_printers != 1)
9028     free(make_model);
9029 
9030   if (sizes != NULL && num_cluster_printers != 1)
9031     cupsArrayDelete(sizes);
9032 
9033   if (p && !in_shutdown)
9034     remove_printer_entry(p);
9035 
9036   log_all_printers();
9037 
9038   if (in_shutdown == 0)
9039     recheck_timer ();
9040 
9041   /* Don't run this callback again */
9042   return FALSE;
9043 }
9044 
9045 static void
recheck_timer(void)9046 recheck_timer (void)
9047 {
9048   remote_printer_t *p;
9049   time_t timeout = (time_t) -1;
9050   time_t now = time(NULL);
9051 
9052   if (!gmainloop)
9053     return;
9054 
9055   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
9056        p;
9057        p = (remote_printer_t *)cupsArrayNext(remote_printers))
9058     if (p->timeout == (time_t) -1)
9059       continue;
9060     else if (now > p->timeout) {
9061       timeout = 0;
9062       break;
9063     } else if (timeout == (time_t) -1 || p->timeout - now < timeout)
9064       timeout = p->timeout - now;
9065 
9066   if (queues_timer_id)
9067     g_source_remove (queues_timer_id);
9068 
9069   if (timeout != (time_t) -1) {
9070     debug_printf("checking queues in %ds\n", timeout);
9071     queues_timer_id =
9072       g_timeout_add_seconds (timeout, update_cups_queues, NULL);
9073   } else {
9074     debug_printf("listening\n");
9075     queues_timer_id = 0;
9076   }
9077 }
9078 
9079 static gboolean
matched_filters(const char * queue_name,const char * host,uint16_t port,const char * service_name,const char * domain,void * txt)9080 matched_filters (const char *queue_name,
9081 		 const char *host,
9082 		 uint16_t port,
9083 		 const char *service_name,
9084 		 const char *domain,
9085 		 void *txt) {
9086   browse_filter_t *filter;
9087   const char *property = NULL;
9088   char buf[10];
9089 #ifdef HAVE_AVAHI
9090   AvahiStringList *entry = NULL;
9091   char *key = NULL, *value = NULL;
9092 #endif /* HAVE_AVAHI */
9093 
9094   debug_printf("Matching printer \"%s\" with properties Host = \"%s\", Port = %d, Service Name = \"%s\", Domain = \"%s\" with the BrowseFilter lines in cups-browsed.conf\n",
9095 	       queue_name, host, port, service_name, domain);
9096   /* Go through all BrowseFilter lines and stop if one line does not match,
9097      rejecting this printer */
9098   for (filter = cupsArrayFirst (browsefilter);
9099        filter;
9100        filter = cupsArrayNext (browsefilter)) {
9101     debug_printf("Matching with line \"BrowseFilter %s%s%s %s\"",
9102 		 (filter->sense == FILTER_NOT_MATCH ? "NOT " : ""),
9103 		 (filter->regexp && !filter->cregexp ? "EXACT " : ""),
9104 		 filter->field, (filter->regexp ? filter->regexp : ""));
9105 #ifdef HAVE_AVAHI
9106     /* Go through the TXT record to see whether this rule applies to a field
9107        in there */
9108     if (txt) {
9109       entry = avahi_string_list_find((AvahiStringList *)txt, filter->field);
9110       if (entry) {
9111 	avahi_string_list_get_pair(entry, &key, &value, NULL);
9112 	if (key) {
9113 	  debug_printf(", TXT record entry: %s = %s",
9114 		       key, (value ? value : ""));
9115 	  if (filter->regexp) {
9116 	    /* match regexp */
9117 	    if (!value)
9118 	      value = strdup("");
9119 	    if ((filter->cregexp &&
9120 		 regexec(filter->cregexp, value, 0, NULL, 0) == 0) ||
9121 		(!filter->cregexp && !strcasecmp(filter->regexp, value))) {
9122 	      if (filter->sense == FILTER_NOT_MATCH) {
9123 		avahi_free(key);
9124 		avahi_free(value);
9125 		goto filter_failed;
9126 	      }
9127 	    } else {
9128 	      if (filter->sense == FILTER_MATCH) {
9129 		avahi_free(key);
9130 		avahi_free(value);
9131 		goto filter_failed;
9132 	      }
9133 	    }
9134 	  } else {
9135 	    /* match boolean value */
9136 	    if (filter->sense == FILTER_MATCH) {
9137 	      if (!value || strcasecmp(value, "T")) {
9138 		avahi_free(key);
9139 		avahi_free(value);
9140 		goto filter_failed;
9141 	      }
9142 	    } else {
9143 	      if (value && !strcasecmp(value, "T")) {
9144 		avahi_free(key);
9145 		avahi_free(value);
9146 		goto filter_failed;
9147 	      }
9148 	    }
9149 	  }
9150 	}
9151 	avahi_free(key);
9152 	avahi_free(value);
9153 	goto filter_matched;
9154       }
9155     }
9156 #endif /* HAVE_AVAHI */
9157 
9158     /* Does one of the properties outside the TXT record match? */
9159     property = buf;
9160     buf[0] = '\0';
9161     if (!strcasecmp(filter->field, "Name") ||
9162 	!strcasecmp(filter->field, "Printer") ||
9163 	!strcasecmp(filter->field, "PrinterName") ||
9164 	!strcasecmp(filter->field, "Queue") ||
9165 	!strcasecmp(filter->field, "QueueName")) {
9166       if (queue_name)
9167 	property = queue_name;
9168     } else if (!strcasecmp(filter->field, "Host") ||
9169 	       !strcasecmp(filter->field, "HostName") ||
9170 	       !strcasecmp(filter->field, "RemoteHost") ||
9171 	       !strcasecmp(filter->field, "RemoteHostName") ||
9172 	       !strcasecmp(filter->field, "Server") ||
9173 	       !strcasecmp(filter->field, "ServerName")) {
9174       if (host)
9175 	property = host;
9176     } else if (!strcasecmp(filter->field, "Port")) {
9177       if (port)
9178 	snprintf(buf, sizeof(buf), "%d", port);
9179     } else if (!strcasecmp(filter->field, "Service") ||
9180 	       !strcasecmp(filter->field, "ServiceName")) {
9181       if (service_name)
9182 	property = service_name;
9183     } else if (!strcasecmp(filter->field, "Domain")) {
9184       if (domain)
9185 	property = domain;
9186     } else
9187       property = NULL;
9188     if (property) {
9189       if (!filter->regexp)
9190 	filter->regexp = "";
9191       if ((filter->cregexp &&
9192 	   regexec(filter->cregexp, property, 0, NULL, 0) == 0) ||
9193 	  (!filter->cregexp && !strcasecmp(filter->regexp, property))) {
9194 	if (filter->sense == FILTER_NOT_MATCH)
9195 	  goto filter_failed;
9196       } else {
9197 	if (filter->sense == FILTER_MATCH)
9198 	  goto filter_failed;
9199       }
9200       goto filter_matched;
9201     }
9202 
9203     debug_printf(": Field not found --> SKIPPED\n");
9204     continue;
9205 
9206   filter_matched:
9207     debug_printf(" --> MATCHED\n");
9208   }
9209 
9210   /* All BrowseFilter lines matching, accept this printer */
9211   debug_printf("All BrowseFilter lines matched or skipped, accepting printer %s\n",
9212 	       queue_name);
9213   return TRUE;
9214 
9215  filter_failed:
9216   debug_printf(" --> FAILED\n");
9217   debug_printf("One BrowseFilter line did not match, ignoring printer %s\n",
9218 	       queue_name);
9219   return FALSE;
9220 }
9221 
9222 static gboolean
update_netifs(gpointer data)9223 update_netifs (gpointer data)
9224 {
9225   struct ifaddrs *ifaddr, *ifa;
9226   netif_t *iface, *iface2;
9227   int i, add_to_netifs, addr_size, dupe, if_found, addr_found;
9228   char *host, buf[HTTP_MAX_HOST], *p, list[65536], *l;
9229 
9230   debug_printf("update_netifs() in THREAD %ld\n", pthread_self());
9231 
9232   update_netifs_sourceid = 0;
9233   if (getifaddrs (&ifaddr) == -1) {
9234     debug_printf("unable to get interface addresses: %s\n",
9235 		 strerror (errno));
9236     return FALSE;
9237   }
9238 
9239   while ((iface = cupsArrayFirst (netifs)) != NULL) {
9240     cupsArrayRemove (netifs, iface);
9241     free (iface->address);
9242     free (iface);
9243   }
9244   while ((host = cupsArrayFirst (local_hostnames)) != NULL) {
9245     cupsArrayRemove (local_hostnames, host);
9246     free (host);
9247   }
9248 
9249   memset(list, 0, sizeof(list));
9250   snprintf(list, sizeof(list) - 1, "Network interfaces: ");
9251   l = list + strlen(list);
9252 
9253   for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
9254     if_found = 0;
9255     addr_found = 0;
9256 
9257     netif_t *iface;
9258 
9259     add_to_netifs = 1;
9260 
9261     if (ifa->ifa_addr == NULL)
9262       continue;
9263 
9264     if (ifa->ifa_broadaddr == NULL)
9265       add_to_netifs = 0;
9266 
9267     if (ifa->ifa_flags & IFF_LOOPBACK)
9268       add_to_netifs = 0;
9269 
9270     if (!(ifa->ifa_flags & IFF_BROADCAST))
9271       add_to_netifs = 0;
9272 
9273     if (ifa->ifa_addr->sa_family == AF_INET)
9274       addr_size = sizeof (struct sockaddr_in);
9275     else if (ifa->ifa_addr->sa_family == AF_INET6)
9276       addr_size = sizeof (struct sockaddr_in6);
9277     else
9278       addr_size = 0;
9279     if (addr_size) {
9280       if (strlen(list) + strlen(ifa->ifa_name) + 1 <=
9281 	  sizeof(list)) {
9282 	snprintf(l, sizeof(list) - strlen(list) - 1,
9283 		 "%s", ifa->ifa_name);
9284 	l = list + strlen(list);
9285 	if_found = 1;
9286       }
9287       for (i = 0; i <= 1; i ++)
9288         if (getnameinfo (ifa->ifa_addr, addr_size,
9289 			 buf, HTTP_MAX_HOST, NULL, 0,
9290 			 i == 0 ? NI_NUMERICHOST : NI_NAMEREQD) == 0)
9291 	  if (buf[0]) {
9292 	    /* Cut off "%..." from IPv6 IP addresses */
9293 	    if (ifa->ifa_addr->sa_family == AF_INET6 && i == 0 &&
9294 		(p = strchr(buf, '%')) != NULL)
9295 	      *p = '\0';
9296 	    /* discard if we already have this name or address */
9297 	    dupe = 0;
9298 	    for (host = (char *)cupsArrayFirst (local_hostnames);
9299 		 host != NULL;
9300 		 host = (char *)cupsArrayNext (local_hostnames))
9301 	      if (strcasecmp(buf, host) == 0) {
9302 		dupe = 1;
9303 		break;
9304 	      }
9305 	    if (dupe == 0) {
9306 	      cupsArrayAdd (local_hostnames, strdup(buf));
9307 	      if (addr_found == 1 && strlen(list) + 3 <=
9308 		  sizeof(list)) {
9309 		snprintf(l, sizeof(list) - strlen(list) - 1,
9310 			 ", ");
9311 		l = list + strlen(list);
9312 	      }
9313 	      if (addr_found == 0 && strlen(list) + 3 <=
9314 		  sizeof(list)) {
9315 		snprintf(l, sizeof(list) - strlen(list) - 1,
9316 			 " (");
9317 		l = list + strlen(list);
9318 		addr_found = 1;
9319 	      }
9320 	      if (strlen(list) + strlen(buf) + 1 <=
9321 		  sizeof(list)) {
9322 		snprintf(l, sizeof(list) - strlen(list) - 1,
9323 			 "%s", buf);
9324 		l = list + strlen(list);
9325 	      }
9326 	    }
9327 	  }
9328     }
9329 
9330     if (add_to_netifs == 0)
9331       goto done;
9332 
9333     iface = malloc (sizeof (netif_t));
9334     if (iface == NULL) {
9335       debug_printf ("malloc failure\n");
9336       exit (1);
9337     }
9338 
9339     iface->address = malloc (HTTP_MAX_HOST);
9340     if (iface->address == NULL) {
9341       free (iface);
9342       debug_printf ("malloc failure\n");
9343       exit (1);
9344     }
9345 
9346     iface->address[0] = '\0';
9347     switch (ifa->ifa_addr->sa_family) {
9348     case AF_INET:
9349       /* copy broadcast addr/fill in port first to faciliate dupe compares */
9350       memcpy (&iface->broadcast, ifa->ifa_broadaddr,
9351 	      sizeof (struct sockaddr_in));
9352       iface->broadcast.ipv4.sin_port = htons (BrowsePort);
9353       /* discard if we already have an interface sharing the broadcast
9354 	 address */
9355       dupe = 0;
9356       for (iface2 = (netif_t *)cupsArrayFirst (netifs);
9357            iface2 != NULL;
9358            iface2 = (netif_t *)cupsArrayNext (netifs)) {
9359 	if (memcmp(&iface2->broadcast, &iface->broadcast,
9360 		   sizeof(struct sockaddr_in)) == 0) {
9361 	  dupe = 1;
9362 	  break;
9363 	}
9364       }
9365       if (dupe) break;
9366       getnameinfo (ifa->ifa_addr, sizeof (struct sockaddr_in),
9367 		   iface->address, HTTP_MAX_HOST,
9368 		   NULL, 0, NI_NUMERICHOST);
9369       break;
9370 
9371     case AF_INET6:
9372       if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *)(ifa->ifa_addr))
9373 				 ->sin6_addr))
9374 	break;
9375 
9376       /* see above for order */
9377       memcpy (&iface->broadcast, ifa->ifa_broadaddr,
9378 	      sizeof (struct sockaddr_in6));
9379       iface->broadcast.ipv6.sin6_port = htons (BrowsePort);
9380       /* discard alias addresses (identical broadcast) */
9381       dupe = 0;
9382       for (iface2 = (netif_t *)cupsArrayFirst (netifs);
9383            iface2 != NULL;
9384            iface2 = (netif_t *)cupsArrayNext (netifs)) {
9385 	if (memcmp(&iface2->broadcast, ifa->ifa_broadaddr,
9386 		   sizeof(struct sockaddr_in6)) == 0) {
9387 	  dupe = 1;
9388 	  break;
9389 	}
9390       }
9391       if (dupe) break;
9392       getnameinfo (ifa->ifa_addr, sizeof (struct sockaddr_in6),
9393 		   iface->address, HTTP_MAX_HOST, NULL, 0, NI_NUMERICHOST);
9394       break;
9395     }
9396 
9397     if (iface->address[0]) {
9398       cupsArrayAdd (netifs, iface);
9399       if (if_found == 1) {
9400 	if (addr_found == 1 && strlen(list) + 3 <= sizeof(list)) {
9401 	  snprintf(l, sizeof(list) - strlen(list) - 1,
9402 		   ", ");
9403 	  l = list + strlen(list);
9404 	}
9405 	if (addr_found == 0 && strlen(list) + 3 <= sizeof(list)) {
9406 	  snprintf(l, sizeof(list) - strlen(list) - 1,
9407 		   " (");
9408 	  l = list + strlen(list);
9409 	  addr_found = 1;
9410 	}
9411 	if (strlen(list) + strlen(iface->address) + 2 <= sizeof(list)) {
9412 	  snprintf(l, sizeof(list) - strlen(list) - 1,
9413 		   "%s*", iface->address);
9414 	  l = list + strlen(list);
9415 	}
9416       }
9417     } else {
9418       free (iface->address);
9419       free (iface);
9420     }
9421 
9422   done:
9423     if (if_found == 1) {
9424       if (addr_found == 1 && strlen(list) + 2 <= sizeof(list)) {
9425 	snprintf(l, sizeof(list) - strlen(list) - 1,
9426 		 ")");
9427 	l = list + strlen(list);
9428       }
9429       if (strlen(list) + 3 <= sizeof(list)) {
9430 	snprintf(l, sizeof(list) - strlen(list) - 1,
9431 		 ", ");
9432 	l = list + strlen(list);
9433       }
9434     }
9435   }
9436 
9437   if ((l = strrchr(list, ')')) != NULL) {
9438     if (strlen(list) + 2 <= sizeof(list))
9439     *(l + 1) = '\0';
9440   } else {
9441     if (strlen(list) + 5 <= sizeof(list))
9442       snprintf(list + strlen(list), sizeof(list) - strlen(list) - 1,
9443 	       "None");
9444   }
9445   debug_printf("%s\n", list);
9446 
9447   freeifaddrs (ifaddr);
9448 
9449   /* If run as a timeout, don't run it again. */
9450   return FALSE;
9451 }
9452 
9453 int
is_local_hostname(const char * host_name)9454 is_local_hostname(const char *host_name) {
9455   char *host;
9456 
9457   if (host_name == NULL)
9458     return 0;
9459 
9460   for (host = (char *)cupsArrayFirst (local_hostnames);
9461        host != NULL;
9462        host = (char *)cupsArrayNext (local_hostnames))
9463     if (strncasecmp(host_name, host, strlen(host)) == 0 &&
9464 	(strlen(host_name) == strlen(host) ||
9465 	 (strlen(host_name) > strlen(host) &&
9466 	  (strcasecmp(host_name + strlen(host), ".local") == 0 ||
9467 	   strcasecmp(host_name + strlen(host), ".local.") == 0))))
9468       return 1;
9469 
9470   return 0;
9471 }
9472 
9473 static remote_printer_t *
examine_discovered_printer_record(const char * host,const char * ip,uint16_t port,char * resource,const char * service_name,const char * location,const char * info,const char * type,const char * domain,const char * interface,int family,void * txt)9474 examine_discovered_printer_record(const char *host,
9475 				  const char *ip,
9476 				  uint16_t port,
9477 				  char *resource,
9478 				  const char *service_name,
9479 				  const char *location,
9480 				  const char *info,
9481 				  const char *type,
9482 				  const char *domain,
9483 				  const char *interface,
9484 				  int family,
9485 				  void *txt) {
9486 
9487   char uri[HTTP_MAX_URI];
9488   char *remote_host = NULL, *pdl = NULL,
9489        *make_model = NULL;
9490   int color = 1, duplex = 1;
9491 #ifdef HAVE_AVAHI
9492   char *fields[] = { "product", "usb_MDL", "ty", NULL }, **f;
9493   AvahiStringList *entry = NULL;
9494   char *key = NULL, *value = NULL;
9495   char *note_value = NULL;
9496   char service_host_name[1024];
9497 #endif /* HAVE_AVAHI */
9498   remote_printer_t *p = NULL, key_rec;
9499   char *local_queue_name = NULL;
9500   int is_cups_queue;
9501   int raw_queue = 0;
9502   char *ptr;
9503 
9504   if (!host || !resource || !service_name || !location || !info || !type ||
9505       !domain) {
9506     debug_printf("ERROR: examine_discovered_printer_record(): Input value missing!\n");
9507     return NULL;
9508   }
9509 
9510   is_cups_queue = 0;
9511   memset(uri, 0, sizeof(uri));
9512 
9513   /* Find the remote host name.
9514      Used in constructing backup queue name, so need to sanitize.
9515      strdup() is called inside remove_bad_chars() and result is free()-able. */
9516   remote_host = remove_bad_chars(host, 1);
9517 
9518   /* If we only want to create queues for printers for which CUPS does
9519      not already auto-create queues, we check here whether we can skip
9520      this printer */
9521   if (OnlyUnsupportedByCUPS) {
9522     if (g_hash_table_find (cups_supported_remote_printers,
9523 			   local_printer_service_name_matches,
9524 			   (gpointer *)service_name)) {
9525       /* Found a DNS-SD-discovered CUPS-supported printer whose service name
9526 	 matches our discovered printer */
9527       debug_printf("Printer with DNS-SD service name \"%s\" does not need to be covered by us as it is already supported by CUPS, skipping.\n",
9528 		   service_name);
9529       goto fail;
9530     }
9531   }
9532 
9533 #ifdef HAVE_AVAHI
9534   if (txt) {
9535     /* Find make and model by the TXT record */
9536     for (f = fields; *f; f ++) {
9537       entry = avahi_string_list_find((AvahiStringList *)txt, *f);
9538       if (entry) {
9539 	avahi_string_list_get_pair(entry, &key, &value, NULL);
9540 	if (key && value && !strcasecmp(key, *f) && strlen(value) >= 3) {
9541 	  if (!strcasecmp(key, "product")) {
9542 	    make_model = strdup(value + 1);
9543 	    make_model[strlen(make_model) - 1] = '\0';
9544 	  } else
9545 	    make_model = strdup(value);
9546 	  avahi_free(key);
9547 	  avahi_free(value);
9548 	  break;
9549 	}
9550 	avahi_free(key);
9551 	avahi_free(value);
9552       }
9553     }
9554     /* Check by the printer-type TXT field whether the discovered printer is a
9555        CUPS queue */
9556     entry = avahi_string_list_find((AvahiStringList *)txt, "printer-type");
9557     if (entry) {
9558       avahi_string_list_get_pair(entry, &key, &value, NULL);
9559       if (key && value && strlen(value) > 1 &&
9560 	  !strcasecmp(key, "printer-type") && value[0] == '0' &&
9561 	  value[1] == 'x') {
9562 	is_cups_queue = 1;
9563       }
9564       avahi_free(key);
9565       avahi_free(value);
9566     }
9567   }
9568 #else
9569   /* Check by the resource whether the discovered printer is a CUPS queue */
9570   if (!strncasecmp(resource, "printers/", 9) ||
9571       !strncasecmp(resource, "classes/", 8))
9572     /* This is a remote CUPS queue or class */
9573     is_cups_queue = 1;
9574 #endif /* HAVE_AVAHI */
9575   /* If we do not have a TXT record the printer was not discovered via
9576      DNS-SD but via CUPS legacy or LDAP, so it is a remote CUPS queue
9577      and not an IPP network printer. */
9578   if (txt == NULL)
9579     is_cups_queue = 1;
9580   if (is_cups_queue)
9581     debug_printf("Found CUPS queue/class: %s on host %s.\n",
9582 		 strrchr(resource, '/') + 1, remote_host);
9583 #ifdef HAVE_AVAHI
9584   if (is_cups_queue) {
9585     /* If the remote queue has a PPD file, the "product" field of the
9586        TXT record is populated. If it has no PPD file the remote queue
9587        is a raw queue and so we do not know enough about the printer
9588        behind it for auto-creating a local queue pointing to it. */
9589     if (txt) {
9590       entry = avahi_string_list_find((AvahiStringList *)txt, "product");
9591       if (entry) {
9592 	avahi_string_list_get_pair(entry, &key, &value, NULL);
9593 	if (!key || !value || strcasecmp(key, "product") || value[0] != '(' ||
9594 	    value[strlen(value) - 1] != ')') {
9595 	  raw_queue = 1;
9596 	}
9597 	avahi_free(key);
9598 	avahi_free(value);
9599       } else
9600 	raw_queue = 1;
9601     } else if (domain && domain[0] != '\0')
9602       raw_queue = 1;
9603     if (raw_queue && CreateRemoteRawPrinterQueues == 0) {
9604       /* The remote CUPS queue is raw, ignore it */
9605       debug_printf("Remote DNS-SD-advertised CUPS queue %s on host %s is raw, ignored.\n",
9606 		   strrchr(resource, '/') + 1, remote_host);
9607       free (remote_host);
9608       if (make_model) free (make_model);
9609       return NULL;
9610     }
9611   } else {
9612     if (txt) {
9613       /* Find out which PDLs the printer understands */
9614       entry = avahi_string_list_find((AvahiStringList *)txt, "pdl");
9615       if (entry) {
9616 	avahi_string_list_get_pair(entry, &key, &value, NULL);
9617 	if (key && value && !strcasecmp(key, "pdl") && strlen(value) >= 3) {
9618 	  pdl = remove_bad_chars(value, 1);
9619 	}
9620 	avahi_free(key);
9621 	avahi_free(value);
9622       }
9623       /* Find out if we have a color printer */
9624       entry = avahi_string_list_find((AvahiStringList *)txt, "Color");
9625       if (entry) {
9626 	avahi_string_list_get_pair(entry, &key, &value, NULL);
9627 	if (key && value && !strcasecmp(key, "Color")) {
9628 	  if (!strcasecmp(value, "T")) color = 1;
9629 	  if (!strcasecmp(value, "F")) color = 0;
9630 	}
9631 	avahi_free(key);
9632 	avahi_free(value);
9633       }
9634       /* Find out if we have a duplex printer */
9635       entry = avahi_string_list_find((AvahiStringList *)txt, "Duplex");
9636       if (entry) {
9637 	avahi_string_list_get_pair(entry, &key, &value, NULL);
9638 	if (key && value && !strcasecmp(key, "Duplex")) {
9639 	  if (!strcasecmp(value, "T")) duplex = 1;
9640 	  if (!strcasecmp(value, "F")) duplex = 0;
9641 	}
9642 	avahi_free(key);
9643 	avahi_free(value);
9644       }
9645     }
9646   }
9647   /* Extract location from DNS-SD TXT record's "note" field */
9648   if (location[0] == '\0') {
9649     if (txt) {
9650       entry = avahi_string_list_find((AvahiStringList *)txt, "note");
9651       if (entry) {
9652 	avahi_string_list_get_pair(entry, &key, &note_value, NULL);
9653 	if (key && note_value && !strcasecmp(key, "note")) {
9654 	  debug_printf("examine_discovered_printer_record: TXT.note: |%s|\n",
9655 		       note_value); /* !! */
9656 	  location = note_value;
9657 	}
9658         avahi_free(key);
9659         /* don't avahi_free(note_value) here! */
9660       }
9661     }
9662   }
9663   /* A NULL location is only passed in from resolve_callback(), which is
9664      HAVE_AVAHI */
9665 #endif /* HAVE_AVAHI */
9666 
9667   /* Determine the device URI of the remote printer */
9668 #ifdef HAVE_AVAHI
9669   if (txt && DNSSDBasedDeviceURIs) {
9670     /* Printer is DNS-SD-discovered, so we can give a DNS-SD-service-name-based
9671        device URI to it (only if DNSSDBasedDeviceURIs config option is set) */
9672     snprintf(service_host_name, sizeof(service_host_name), "%s.%s.%s",
9673 	     service_name, type, domain);
9674     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri) - 1,
9675 		     (strcasestr(type, "_ipps") ? "ipps" : "ipp"), NULL,
9676 		     service_host_name, 0, "/%s",
9677 		     (is_cups_queue ? "cups" : ""));
9678   } else
9679 #endif /* HAVE_AVAHI */
9680     /* Printer is discovered via legacy CUPS or LDAP, so we have to give
9681        a IP-based/host-name-based URI to it ( or for DNS-SD-discovered
9682        printers if DNSSDBasedDeviceURIs config option is not set) */
9683     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri) - 1,
9684 		     (strcasestr(type, "_ipps") ? "ipps" : "ipp"), NULL,
9685 		     (ip != NULL ? ip : host), port, "/%s", resource);
9686 
9687   /* Determine the queue name */
9688   local_queue_name = get_local_queue_name(service_name, make_model, resource,
9689 					  remote_host, &is_cups_queue, NULL);
9690   if (local_queue_name == NULL)
9691     goto fail;
9692 
9693   if (!matched_filters (local_queue_name, remote_host, port, service_name,
9694 			domain, txt)) {
9695     debug_printf("Printer %s does not match BrowseFilter lines in cups-browsed.conf, printer ignored.\n",
9696 		 local_queue_name);
9697     goto fail;
9698   }
9699 
9700 
9701   /* Update network interface info if we were discovered by LDAP
9702      or legacy CUPS, needed for the is_local_hostname() function calls.
9703      During DNS-SD discovery the update is already done by the Avahi
9704      event handler function. */
9705   if (type == NULL || type[0] == '\0')
9706     update_netifs(NULL);
9707 
9708   /* Check if we have already created a queue for the discovered
9709      printer */
9710   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
9711        p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
9712     if (!strcasecmp(p->queue_name, local_queue_name) &&
9713 	(p->host[0] == '\0' ||
9714 	 p->status == STATUS_UNCONFIRMED ||
9715 	 p->status == STATUS_DISAPPEARED ||
9716 	 ((!strcasecmp(p->host, remote_host) ||
9717 	   (is_local_hostname(p->host) && is_local_hostname(remote_host))) &&
9718 	  (p->port == port ||
9719 	   (p->port == 631 && port == 443) ||
9720 	   (p->port == 443 && port == 631)) &&
9721 	  (txt ||
9722 	   (strlen(p->uri) - strlen(resource) > 0 &&
9723 	    !strcasecmp(p->uri + strlen(p->uri) - strlen(resource),
9724 			resource))))))
9725       break;
9726 
9727   /* Is there a local queue with the same URI as the remote queue? */
9728   if (!p) {
9729     memset(&key_rec, 0, sizeof(key_rec));
9730     key_rec.uri = uri;
9731     key_rec.host = remote_host;
9732     key_rec.port = port;
9733     key_rec.resource = resource;
9734     key_rec.service_name = (char *)service_name;
9735     key_rec.type = (char *)type;
9736     key_rec.domain = (char *)domain;
9737     if (g_hash_table_find (local_printers,
9738 			   local_printer_is_same_device, &key_rec)) {
9739       /* Found a local queue with the same URI as our discovered printer
9740 	 would get, so ignore this remote printer */
9741       debug_printf("Printer with URI %s (or IPP/IPPS equivalent) already exists, printer ignored.\n",
9742 		   uri);
9743       goto fail;
9744     }
9745 
9746     /* We need to create a local queue pointing to the
9747        discovered printer */
9748     p = create_remote_printer_entry (local_queue_name, location, info, uri,
9749 				     remote_host, ip, port, resource,
9750 				     service_name ? service_name : "", type,
9751 				     domain, interface, family, pdl, color,
9752 				     duplex, make_model, is_cups_queue);
9753   } else {
9754     debug_printf("Entry for %s (URI: %s) already exists.\n",
9755 		 p->queue_name, p->uri);
9756     /* We have already created a local queue, check whether the
9757        discovered service allows us to upgrade the queue to IPPS
9758        or whether the URI part after ipp(s):// has changed, or
9759        whether the discovered queue is discovered via DNS-SD
9760        having more info in contrary to the existing being
9761        discovered by legacy CUPS or LDAP */
9762 
9763     int downgrade = 0, upgrade = 0;
9764 
9765     /* Get first element of array of interfaces on which this printer
9766        got already discovered, as this one is "lo" when it already got
9767        discovered through the loopback interface (preferred interface) */
9768     ipp_discovery_t *ippdis = cupsArrayFirst(p->ipp_discoveries);
9769 
9770     /* Force upgrade if the found entry is marked unconfirmed or
9771        disappeared */
9772     if (p->status == STATUS_UNCONFIRMED ||
9773 	p->status == STATUS_DISAPPEARED) {
9774       upgrade = 1;
9775       debug_printf("Replacing printer entry %s (Host: %s, Port: %d) as it was marked %s. New URI: %s\n",
9776 		   p->queue_name, remote_host, port,
9777 		   (p->status == STATUS_UNCONFIRMED ? "unconfirmed" :
9778 		    "disappeared"),
9779 		   uri);
9780     /* Check if there is a downgrade */
9781     /* IPPS -> IPP */
9782     } else if ((ptr = strcasestr(type, "_ipp")) != NULL &&
9783 	       *(ptr + 4) != 's' &&
9784 	       !strncasecmp(p->uri, "ipps:", 5)) {
9785       downgrade = 1;
9786       debug_printf("Printer %s: New discovered service from host %s, port %d, URI %s is only IPP, we have already IPPS, skipping\n",
9787 		   p->queue_name, remote_host, port, uri);
9788     /* "lo" -> Any non-"lo" interface */
9789     } else if (strcasecmp(interface, "lo") &&
9790 	       ippdis && !strcasecmp(ippdis->interface, "lo")) {
9791       downgrade = 1;
9792       debug_printf("Printer %s: New discovered service from host %s, port %d, URI %s is from a non-loopback interface, we have already one from the loopback interface, skipping\n",
9793 		   p->queue_name, remote_host, port, uri);
9794     /* DNS-SD -> CUPS Legacy/LDAP */
9795     } else if (p->domain != NULL && p->domain[0] != '\0' &&
9796 	       (domain == NULL || domain[0] == '\0') &&
9797 	       p->type != NULL && p->type[0] != '\0' &&
9798 	       (type == NULL || type[0] == '\0')) {
9799       downgrade = 1;
9800       debug_printf("Printer %s: New discovered service from host %s, port %d, URI %s is only discovered via legacy CUPS or LDAP, we have already a DNS-SD-discovered one, skipping\n",
9801 		   p->queue_name, remote_host, port, uri);
9802     }
9803 
9804     if (downgrade == 0) {
9805       /* Check if there is an upgrade */
9806       /* IPP -> IPPS */
9807       if (strcasestr(type, "_ipps") &&
9808 	  !strncasecmp(p->uri, "ipp:", 4)) {
9809 	upgrade = 1;
9810 	debug_printf("Upgrading printer %s (Host: %s, Port: %d) to IPPS. New URI: %s\n",
9811 		     p->queue_name, remote_host, port, uri);
9812       /* Any non-"lo" interface -> "lo" */
9813       } else if (!strcasecmp(interface, "lo")) {
9814 	upgrade = 1;
9815 	debug_printf("Upgrading printer %s (Host: %s, Port: %d) to use loopback interface \"lo\". New URI: %s\n",
9816 		     p->queue_name, remote_host, port, uri);
9817       /* CUPS Legacy/LDAP -> DNS-SD */
9818       } else if ((p->domain == NULL || p->domain[0] == '\0') &&
9819 		 domain != NULL && domain[0] != '\0' &&
9820 		 (p->type == NULL || p->type[0] == '\0') &&
9821 		 type != NULL && type[0] != '\0') {
9822 	upgrade = 1;
9823 	debug_printf("Discovered printer %s (Host: %s, Port: %d, URI: %s) by DNS-SD now.\n",
9824 		     p->queue_name, remote_host, port, uri);
9825       }
9826     }
9827 
9828     /* Switch local queue over to this newly discovered service */
9829     if (upgrade == 1) {
9830       /* Remove tiemout of legacy CUPS broadcasting */
9831       if (domain != NULL && domain[0] != '\0' &&
9832 	  type != NULL && type[0] != '\0' &&
9833 	  p->is_legacy) {
9834 	p->is_legacy = 0;
9835 	if (p->status == STATUS_CONFIRMED)
9836 	  p->timeout = (time_t) -1;
9837       }
9838       free(p->queue_name);
9839       free(p->location);
9840       free(p->info);
9841       free(p->make_model);
9842       free(p->pdl);
9843       free(p->uri);
9844       free(p->host);
9845       free(p->ip);
9846       free(p->resource);
9847       free(p->service_name);
9848       free(p->type);
9849       free(p->domain);
9850       p->queue_name = strdup(local_queue_name);
9851       p->location = strdup(location);
9852       p->info = strdup(info);
9853       p->make_model = (make_model != NULL ? strdup(make_model) : NULL);
9854       p->pdl = (pdl != NULL ? strdup(pdl) : NULL);
9855       p->color = color;
9856       p->duplex = duplex;
9857       p->uri = strdup(uri);
9858       p->status = STATUS_TO_BE_CREATED;
9859       p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
9860       p->host = strdup(remote_host);
9861       p->ip = (ip != NULL ? strdup(ip) : NULL);
9862       p->port = port;
9863       p->resource = strdup(resource);
9864       p->service_name = strdup(service_name);
9865       p->type = strdup(type);
9866       p->domain = strdup(domain);
9867       debug_printf("Switched over to newly discovered entry for this printer.\n");
9868     } else
9869       debug_printf("Staying with previously discovered entry for this printer.\n");
9870 
9871     /* Mark queue entry as confirmed if the entry
9872        is unconfirmed */
9873     if (p->status == STATUS_UNCONFIRMED ||
9874 	p->status == STATUS_DISAPPEARED) {
9875       debug_printf("Marking entry for %s (URI: %s) as confirmed.\n",
9876 		   p->queue_name, p->uri);
9877       p->status = STATUS_CONFIRMED;
9878       if (p->is_legacy) {
9879 	p->timeout = time(NULL) + BrowseTimeout;
9880 	debug_printf("starting BrowseTimeout timer for %s (%ds)\n",
9881 		     p->queue_name, BrowseTimeout);
9882       } else
9883 	p->timeout = (time_t) -1;
9884       /* If this queue was the default printer in its previous life, make
9885 	 it the default printer again. */
9886       queue_creation_handle_default(p->queue_name);
9887       /* If this queue is disabled, re-enable it. */
9888       enable_printer(p->queue_name);
9889       /* Record the options, to record any changes which happened
9890 	 while cups-browsed was not running */
9891       record_printer_options(p->queue_name);
9892     }
9893 
9894     /* Gather extra info from our new discovery */
9895     if (p->uri[0] == '\0') {
9896       free (p->uri);
9897       p->uri = strdup(uri);
9898     }
9899     if (p->location[0] == '\0') {
9900       free (p->location);
9901       p->location = strdup(location);
9902     }
9903     if (p->info[0] == '\0') {
9904       free (p->info);
9905       p->info = strdup(info);
9906     }
9907     if (p->make_model == NULL || p->make_model[0] == '\0') {
9908       if (p->make_model) free (p->make_model);
9909       p->make_model = (make_model != NULL ? strdup(make_model) : NULL);
9910     }
9911     if (p->pdl == NULL || p->pdl[0] == '\0') {
9912       if (p->pdl) free (p->pdl);
9913       p->pdl = (pdl != NULL ? strdup(pdl) : NULL);
9914     }
9915     p->color = color;
9916     p->duplex = duplex;
9917     if (p->host[0] == '\0') {
9918       free (p->host);
9919       p->host = strdup(remote_host);
9920     }
9921     if (p->ip == NULL || p->ip[0] == '\0') {
9922       if (p->ip) free (p->ip);
9923       p->ip = (ip != NULL ? strdup(ip) : NULL);
9924     }
9925     if (p->port == 0)
9926       p->port = port;
9927     if (p->service_name[0] == '\0' && service_name) {
9928       free (p->service_name);
9929       p->service_name = strdup(service_name);
9930     }
9931     if (p->resource[0] == '\0') {
9932       free (p->resource);
9933       p->resource = strdup(resource);
9934     }
9935     if (p->type[0] == '\0' && type) {
9936       free (p->type);
9937       p->type = strdup(type);
9938     }
9939     if (p->domain[0] == '\0' && domain) {
9940       free (p->domain);
9941       p->domain = strdup(domain);
9942     }
9943     if (domain != NULL && domain[0] != '\0' &&
9944 	type != NULL && type[0] != '\0')
9945       ipp_discoveries_add(p->ipp_discoveries, interface, type, family);
9946     p->netprinter = is_cups_queue ? 0 : 1;
9947   }
9948 
9949  fail:
9950   free (remote_host);
9951   free (pdl);
9952   free (make_model);
9953   free (local_queue_name);
9954 #ifdef HAVE_AVAHI
9955   if (note_value) avahi_free(note_value);
9956 #endif /* HAVE_AVAHI */
9957 
9958   if (p)
9959     debug_printf("DNS-SD IDs: Service name: \"%s\", "
9960 		 "Service type: \"%s\", Domain: \"%s\"\n",
9961 		 p->service_name, p->type, p->domain);
9962 
9963   return p;
9964 }
9965 
9966 static gboolean
allowed(struct sockaddr * srcaddr)9967 allowed (struct sockaddr *srcaddr)
9968 {
9969   allow_t *allow;
9970   int i;
9971   gboolean server_allowed;
9972   allow_sense_t sense;
9973 
9974   if (browse_order == ORDER_DENY_ALLOW)
9975     /* BrowseOrder Deny,Allow: Allow server, then apply BrowseDeny lines,
9976        after that BrowseAllow lines */
9977     server_allowed = TRUE;
9978   else
9979     /* BrowseOrder Allow,Deny: Deny server, then apply BrowseAllow lines,
9980        after that BrowseDeny lines */
9981     server_allowed = FALSE;
9982 
9983   for (i = 0; i <= 1; i ++) {
9984     if (browse_order == ORDER_DENY_ALLOW)
9985       /* Treat BrowseDeny lines first, then BrowseAllow lines */
9986       sense = (i == 0 ? ALLOW_DENY : ALLOW_ALLOW);
9987     else
9988       /* Treat BrowseAllow lines first, then BrowseDeny lines */
9989       sense = (i == 0 ? ALLOW_ALLOW : ALLOW_DENY);
9990 
9991     if (server_allowed == (sense == ALLOW_ALLOW ? TRUE : FALSE))
9992       continue;
9993 
9994     if (browseallow_all && sense == ALLOW_ALLOW) {
9995       server_allowed = TRUE;
9996       continue;
9997     }
9998     if (browsedeny_all && sense == ALLOW_DENY) {
9999       server_allowed = FALSE;
10000       continue;
10001     }
10002 
10003     for (allow = cupsArrayFirst (browseallow);
10004 	 allow;
10005 	 allow = cupsArrayNext (browseallow)) {
10006       if (allow->sense != sense)
10007 	continue;
10008 
10009       switch (allow->type) {
10010       case ALLOW_INVALID:
10011 	break;
10012 
10013       case ALLOW_IP:
10014 	switch (srcaddr->sa_family) {
10015 	case AF_INET:
10016 	  if (((struct sockaddr_in *) srcaddr)->sin_addr.s_addr ==
10017 	      allow->addr.ipv4.sin_addr.s_addr) {
10018 	    server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE);
10019 	    goto match;
10020 	  }
10021 	  break;
10022 
10023 	case AF_INET6:
10024 	  if (!memcmp (&((struct sockaddr_in6 *) srcaddr)->sin6_addr,
10025 		       &allow->addr.ipv6.sin6_addr,
10026 		       sizeof (allow->addr.ipv6.sin6_addr))) {
10027 	    server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE);
10028 	    goto match;
10029 	  }
10030 	  break;
10031 	}
10032 	break;
10033 
10034       case ALLOW_NET:
10035 	switch (srcaddr->sa_family) {
10036 	  struct sockaddr_in6 *src6addr;
10037 
10038 	case AF_INET:
10039 	  if ((((struct sockaddr_in *) srcaddr)->sin_addr.s_addr &
10040 	       allow->mask.ipv4.sin_addr.s_addr) ==
10041 	      allow->addr.ipv4.sin_addr.s_addr) {
10042 	    server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE);
10043 	    goto match;
10044 	  }
10045 	  break;
10046 
10047 	case AF_INET6:
10048 	  src6addr = (struct sockaddr_in6 *) srcaddr;
10049 	  if (((src6addr->sin6_addr.s6_addr[0] &
10050 		allow->mask.ipv6.sin6_addr.s6_addr[0]) ==
10051 	       allow->addr.ipv6.sin6_addr.s6_addr[0]) &&
10052 	      ((src6addr->sin6_addr.s6_addr[1] &
10053 		allow->mask.ipv6.sin6_addr.s6_addr[1]) ==
10054 	       allow->addr.ipv6.sin6_addr.s6_addr[1]) &&
10055 	      ((src6addr->sin6_addr.s6_addr[2] &
10056 		allow->mask.ipv6.sin6_addr.s6_addr[2]) ==
10057 	       allow->addr.ipv6.sin6_addr.s6_addr[2]) &&
10058 	      ((src6addr->sin6_addr.s6_addr[3] &
10059 		allow->mask.ipv6.sin6_addr.s6_addr[3]) ==
10060 	       allow->addr.ipv6.sin6_addr.s6_addr[3])) {
10061 	    server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE);
10062 	    goto match;
10063 	  }
10064 	  break;
10065 	}
10066       }
10067     }
10068   match:
10069     continue;
10070   }
10071 
10072   return server_allowed;
10073 }
10074 
10075 #ifdef HAVE_AVAHI
resolve_callback(AvahiServiceResolver * r,AvahiIfIndex interface,AvahiProtocol protocol,AvahiResolverEvent event,const char * name,const char * type,const char * domain,const char * host_name,const AvahiAddress * address,uint16_t port,AvahiStringList * txt,AvahiLookupResultFlags flags,AVAHI_GCC_UNUSED void * userdata)10076 static void resolve_callback(AvahiServiceResolver *r,
10077 			     AvahiIfIndex interface,
10078 			     AvahiProtocol protocol,
10079 			     AvahiResolverEvent event,
10080 			     const char *name,
10081 			     const char *type,
10082 			     const char *domain,
10083 			     const char *host_name,
10084 			     const AvahiAddress *address,
10085 			     uint16_t port,
10086 			     AvahiStringList *txt,
10087 			     AvahiLookupResultFlags flags,
10088 			     AVAHI_GCC_UNUSED void* userdata) {
10089   char ifname[IF_NAMESIZE];
10090   AvahiStringList *uuid_entry, *printer_type_entry;
10091   char *uuid_key, *uuid_value;
10092 
10093   debug_printf("resolve_callback() in THREAD %ld\n", pthread_self());
10094 
10095   if (r == NULL || name == NULL || type == NULL || domain == NULL)
10096     return;
10097 
10098   /* Get the interface name */
10099   if (!if_indextoname(interface, ifname)) {
10100     debug_printf("Unable to find interface name for interface %d: %s\n",
10101 		 interface, strerror(errno));
10102     strncpy(ifname, "Unknown", sizeof(ifname) - 1);
10103   }
10104 
10105   /* Ignore local queues of the cupsd we are serving for, identifying them
10106      via UUID */
10107   update_netifs(NULL);
10108   if ((flags & AVAHI_LOOKUP_RESULT_LOCAL) || !strcasecmp(ifname, "lo") ||
10109       is_local_hostname(host_name)) {
10110     update_local_printers ();
10111     uuid_value = NULL;
10112     if (txt && (uuid_entry = avahi_string_list_find(txt, "UUID")))
10113       avahi_string_list_get_pair(uuid_entry, &uuid_key, &uuid_value, NULL);
10114     if (uuid_value && g_hash_table_find (local_printers,
10115 					 local_printer_has_uuid,
10116 					 uuid_value)) {
10117       debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s) with UUID %s is from local CUPS, ignored (Avahi lookup result or host name of local machine).\n",
10118 		   name, type, domain, host_name, port, ifname,
10119 		   (address ?
10120 		    (address->proto == AVAHI_PROTO_INET ? "IPv4" :
10121 		     address->proto == AVAHI_PROTO_INET6 ? "IPv6" :
10122 		     "IPv4/IPv6 Unknown") :
10123 		    "IPv4/IPv6 Unknown"), uuid_value);
10124       goto ignore;
10125     }
10126     if (txt &&
10127 	(printer_type_entry = avahi_string_list_find(txt, "printer-type")) &&
10128 	strcasestr(type, "_ipps")) {
10129       debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s) with UUID %s is from another CUPS instance on the local system and uses IPPS, the local CUPS has problems to print on this printer, so we ignore it (Avahi lookup result or host name of local machine).\n",
10130 		   name, type, domain, host_name, port, ifname,
10131 		   (address ?
10132 		    (address->proto == AVAHI_PROTO_INET ? "IPv4" :
10133 		     address->proto == AVAHI_PROTO_INET6 ? "IPv6" :
10134 		     "IPv4/IPv6 Unknown") :
10135 		    "IPv4/IPv6 Unknown"),
10136 		   (uuid_value ? uuid_value : "(unknown)"));
10137       goto ignore;
10138     }
10139   }
10140 
10141   /* Called whenever a service has been resolved successfully or timed out */
10142 
10143   switch (event) {
10144 
10145   /* Resolver error */
10146   case AVAHI_RESOLVER_FAILURE:
10147     debug_printf("Avahi-Resolver: Failed to resolve service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s): %s\n",
10148 		 name, type, domain, host_name, port, ifname,
10149 		 (address ?
10150 		  (address->proto == AVAHI_PROTO_INET ? "IPv4" :
10151 		   address->proto == AVAHI_PROTO_INET6 ? "IPv6" :
10152 		   "IPv4/IPv6 Unknown") :
10153 		  "IPv4/IPv6 Unknown"),
10154 		 avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
10155     break;
10156 
10157   /* New remote printer found */
10158   case AVAHI_RESOLVER_FOUND: {
10159     AvahiStringList *rp_entry, *adminurl_entry;
10160     char *rp_key, *rp_value, *adminurl_key, *adminurl_value;
10161 
10162     debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s).\n",
10163 		 name, type, domain, host_name, port, ifname,
10164 		 (address ?
10165 		  (address->proto == AVAHI_PROTO_INET ? "IPv4" :
10166 		   address->proto == AVAHI_PROTO_INET6 ? "IPv6" :
10167 		   "IPv4/IPv6 Unknown") :
10168 		  "IPv4/IPv6 Unknown"));
10169 
10170     /* Ignore if terminated (by SIGTERM) */
10171     if (terminating) {
10172       debug_printf("Avahi Resolver: Ignoring because cups-browsed is terminating.\n");
10173       break;
10174     }
10175 
10176     if (txt && (rp_entry = avahi_string_list_find(txt, "rp")))
10177       avahi_string_list_get_pair(rp_entry, &rp_key, &rp_value, NULL);
10178     else {
10179       rp_key = strdup("rp");
10180       rp_value = strdup("");
10181     }
10182     if (txt && (adminurl_entry = avahi_string_list_find(txt, "adminurl")))
10183       avahi_string_list_get_pair(adminurl_entry, &adminurl_key,
10184 				 &adminurl_value, NULL);
10185     else {
10186       adminurl_key = strdup("adminurl");
10187       if (host_name && (adminurl_value = malloc(strlen(host_name) + 8)) != NULL)
10188 	sprintf(adminurl_value, "http://%s", host_name);
10189       else
10190 	adminurl_value = strdup("");
10191     }
10192 
10193     /* If we create queues only for local IPP printers (like IPP-over-USB
10194        with ippusbxd) check whether the entry is local and skip if not.
10195        We also check for remote CUPS (with "printer-type" TXT field) as this
10196        option is only for IPP network printers */
10197     if (CreateIPPPrinterQueues == IPP_PRINTERS_LOCAL_ONLY &&
10198 	strcasecmp(ifname, "lo") &&
10199 	(!txt || avahi_string_list_find(txt, "printer-type") == NULL)) {
10200       debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, not a local service.\n",
10201 		   name, type, domain);
10202       goto clean_up;
10203     }
10204 
10205     if (txt && rp_key && rp_value && adminurl_key && adminurl_value &&
10206 	!strcasecmp(rp_key, "rp") && !strcasecmp(adminurl_key, "adminurl")) {
10207       char *p, instance[64];
10208       /* Extract instance from DNSSD service name (to serve as info field) */
10209       p = strstr(name, " @ ");
10210       if (p) {
10211 	int n;
10212 	n = p - name;
10213 	if (n >= sizeof(instance))
10214 	  n = sizeof(instance) - 1;
10215 	strncpy(instance, name, sizeof(instance) - 1);
10216 	instance[n] = '\0';
10217 	debug_printf("Avahi-Resolver: Instance: %s\n", instance); /* !! */
10218       } else {
10219 	instance[0] = '\0';
10220       }
10221       /* Determine the remote printer's IP */
10222       if (IPBasedDeviceURIs != IP_BASED_URIS_NO ||
10223 	  (!browseallow_all && cupsArrayCount(browseallow) > 0)) {
10224 	struct sockaddr saddr;
10225 	struct sockaddr *addr = &saddr;
10226 	char *addrstr;
10227 	int addrlen;
10228 	int addrfound = 0;
10229 	if ((addrstr = calloc(256, sizeof(char))) == NULL) {
10230 	  debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, could not allocate memory to determine IP address.\n",
10231 		       name, type, domain);
10232 	  goto clean_up;
10233 	}
10234 	if (address &&
10235 	    address->proto == AVAHI_PROTO_INET &&
10236 	    IPBasedDeviceURIs != IP_BASED_URIS_IPV6_ONLY) {
10237 	  avahi_address_snprint(addrstr, 256, address);
10238 	  addr->sa_family = AF_INET;
10239 	  if (inet_aton(addrstr,
10240 			&((struct sockaddr_in *) addr)->sin_addr) &&
10241 	      allowed(addr))
10242 	    addrfound = 1;
10243 	} else if (address &&
10244 		   address->proto == AVAHI_PROTO_INET6 &&
10245 		   interface != AVAHI_IF_UNSPEC &&
10246 		   IPBasedDeviceURIs != IP_BASED_URIS_IPV4_ONLY) {
10247 	  strncpy(addrstr, "[v1.", sizeof(addrstr) - 1);
10248 	  avahi_address_snprint(addrstr + 4, 256 - 6, address);
10249 	  addrlen = strlen(addrstr + 4);
10250 	  addr->sa_family = AF_INET6;
10251 	  if (inet_pton(AF_INET6, addrstr + 4,
10252 			&((struct sockaddr_in6 *) addr)->sin6_addr) &&
10253 	      allowed(addr)) {
10254 	    if (!strncasecmp(addrstr + 4, "fe", 2) &&
10255 		(addrstr[6] == '8' || addrstr[6] == '9' ||
10256 		 addrstr[6] == 'A' || addrstr[6] == 'B' ||
10257 		 addrstr[6] == 'a' || addrstr[6] == 'B'))
10258 	      /* Link-local address, needs specification of interface */
10259 	      snprintf(addrstr + addrlen + 4, 256 -
10260 		       addrlen - 4, "%%%s]",
10261 		       ifname);
10262 	    else {
10263 	      addrstr[addrlen + 4] = ']';
10264 	      addrstr[addrlen + 5] = '\0';
10265 	    }
10266 	    addrfound = 1;
10267 	  }
10268 	} else
10269 	  debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s': No IP address information available.\n",
10270 		       name, type, domain);
10271 	if (addrfound == 1) {
10272 	  /* Check remote printer type and create appropriate local queue to
10273 	     point to it */
10274 	  if (IPBasedDeviceURIs != IP_BASED_URIS_NO ||
10275 	      !host_name) {
10276 	    debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with IP address %s.\n",
10277 			 name, type, domain, addrstr);
10278 	    examine_discovered_printer_record((strcasecmp(ifname, "lo") ?
10279 					       host_name : "localhost"),
10280 					      addrstr, port, rp_value, name,
10281 					      "", instance, type, domain,
10282 					      ifname, addr->sa_family, txt);
10283 	  } else
10284 	    examine_discovered_printer_record((strcasecmp(ifname, "lo") ?
10285 					       host_name : "localhost"),
10286 					      NULL, port, rp_value,
10287 					      name, "", instance, type,
10288 					      domain, ifname, addr->sa_family,
10289 					      txt);
10290 	} else
10291 	  debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, could not determine IP address.\n",
10292 		       name, type, domain);
10293 	free(addrstr);
10294       } else {
10295 	/* Check remote printer type and create appropriate local queue to
10296 	   point to it */
10297 	if (host_name)
10298 	  examine_discovered_printer_record((strcasecmp(ifname, "lo") ?
10299 					     host_name : "localhost"),
10300 					    NULL, port, rp_value,
10301 					    name, "", instance, type, domain,
10302 					    ifname,
10303 					    (address->proto ==
10304 					     AVAHI_PROTO_INET ? AF_INET :
10305 					     (address->proto ==
10306 					      AVAHI_PROTO_INET6 ? AF_INET6 :
10307 					      0)),
10308 					    txt);
10309 	else
10310 	  debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, host name not supplied.\n",
10311 		       name, type, domain);
10312       }
10313     }
10314 
10315     clean_up:
10316 
10317     /* Clean up */
10318 
10319     if (rp_entry) {
10320       avahi_free(rp_key);
10321       avahi_free(rp_value);
10322     } else {
10323       free(rp_key);
10324       free(rp_value);
10325     }
10326     if (adminurl_entry) {
10327       avahi_free(adminurl_key);
10328       avahi_free(adminurl_value);
10329     } else {
10330       free(adminurl_key);
10331       free(adminurl_value);
10332     }
10333     break;
10334   }
10335   }
10336 
10337  ignore:
10338   avahi_service_resolver_free(r);
10339 
10340   if (in_shutdown == 0)
10341     recheck_timer ();
10342 }
10343 
browse_callback(AvahiServiceBrowser * b,AvahiIfIndex interface,AvahiProtocol protocol,AvahiBrowserEvent event,const char * name,const char * type,const char * domain,AvahiLookupResultFlags flags,void * userdata)10344 static void browse_callback(AvahiServiceBrowser *b,
10345 			    AvahiIfIndex interface,
10346 			    AvahiProtocol protocol,
10347 			    AvahiBrowserEvent event,
10348 			    const char *name,
10349 			    const char *type,
10350 			    const char *domain,
10351 			    AvahiLookupResultFlags flags,
10352 			    void* userdata) {
10353 
10354   AvahiClient *c = userdata;
10355   char ifname[IF_NAMESIZE];
10356 
10357   debug_printf("browse_callback() in THREAD %ld\n", pthread_self());
10358 
10359   if (b == NULL)
10360     return;
10361 
10362   /* Called whenever a new services becomes available on the LAN or
10363      is removed from the LAN */
10364 
10365   switch (event) {
10366 
10367   /* Avahi browser error */
10368   case AVAHI_BROWSER_FAILURE:
10369 
10370     debug_printf("Avahi Browser: ERROR: %s\n",
10371 		 avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
10372     g_main_loop_quit(gmainloop);
10373     g_main_context_wakeup(NULL);
10374     return;
10375 
10376   /* New service (remote printer) */
10377   case AVAHI_BROWSER_NEW:
10378 
10379     if (c == NULL || name == NULL || type == NULL || domain == NULL)
10380       return;
10381 
10382     /* Get the interface name */
10383     if (!if_indextoname(interface, ifname)) {
10384       debug_printf("Unable to find interface name for interface %d: %s\n",
10385 		   interface, strerror(errno));
10386       strncpy(ifname, "Unknown", sizeof(ifname) - 1);
10387     }
10388 
10389     debug_printf("Avahi Browser: NEW: service '%s' of type '%s' in domain '%s' on interface '%s' (%s)\n",
10390 		 name, type, domain, ifname,
10391 		 protocol != AVAHI_PROTO_UNSPEC ?
10392 		 avahi_proto_to_string(protocol) : "Unknown");
10393 
10394     /* Ignore if terminated (by SIGTERM) */
10395     if (terminating) {
10396       debug_printf("Avahi Browser: Ignoring because cups-browsed is terminating.\n");
10397       break;
10398     }
10399 
10400     /* We ignore the returned resolver object. In the callback
10401        function we free it. If the server is terminated before
10402        the callback function is called the server will free
10403        the resolver for us. */
10404 
10405     if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c)))
10406       debug_printf("Failed to resolve service '%s': %s\n",
10407 		   name, avahi_strerror(avahi_client_errno(c)));
10408     break;
10409 
10410   /* A service (remote printer) has disappeared */
10411   case AVAHI_BROWSER_REMOVE: {
10412     remote_printer_t *p;
10413 
10414     if (name == NULL || type == NULL || domain == NULL)
10415       return;
10416 
10417     /* Get the interface name */
10418     if (!if_indextoname(interface, ifname)) {
10419       debug_printf("Unable to find interface name for interface %d: %s\n",
10420 		   interface, strerror(errno));
10421       strncpy(ifname, "Unknown", sizeof(ifname) - 1);
10422     }
10423 
10424     debug_printf("Avahi Browser: REMOVE: service '%s' of type '%s' in domain '%s' on interface '%s' (%s)\n",
10425 		 name, type, domain, ifname,
10426 		 protocol != AVAHI_PROTO_UNSPEC ?
10427 		 avahi_proto_to_string(protocol) : "Unknown");
10428 
10429     /* Ignore if terminated (by SIGTERM) */
10430     if (terminating) {
10431       debug_printf("Avahi Browser: Ignoring because cups-browsed is terminating.\n");
10432       break;
10433     }
10434 
10435     /* Check whether we have listed this printer */
10436     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
10437 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
10438       if (p->status != STATUS_DISAPPEARED &&
10439 	  p->status != STATUS_TO_BE_RELEASED &&
10440 	  !strcasecmp(p->service_name, name) &&
10441 	  !strcasecmp(p->domain, domain))
10442 	break;
10443     if (p) {
10444       int family =
10445 	(protocol == AVAHI_PROTO_INET ? AF_INET :
10446 	 (protocol == AVAHI_PROTO_INET6 ? AF_INET6 : 0));
10447       if (p->ipp_discoveries) {
10448 	ipp_discovery_t *ippdis;
10449 	for (ippdis = cupsArrayFirst(p->ipp_discoveries); ippdis;
10450 	     ippdis = cupsArrayNext(p->ipp_discoveries))
10451 	  if (!strcasecmp(ippdis->interface, ifname) &&
10452 	      !strcasecmp(ippdis->type, type) &&
10453 	      ippdis->family == family) {
10454 	    debug_printf("Discovered instance for printer with Service name \"%s\", Domain \"%s\" unregistered: Interface \"%s\", Service type: \"%s\", Protocol: \"%s\"\n",
10455 			 p->service_name, p->domain,
10456 			 ippdis->interface, ippdis->type,
10457 			 (ippdis->family == AF_INET ? "IPv4" :
10458 			  (ippdis->family == AF_INET6 ? "IPv6" : "Unknown")));
10459 	    cupsArrayRemove(p->ipp_discoveries, (void *)ippdis);
10460 	    ipp_discoveries_list(p->ipp_discoveries);
10461 	    break;
10462 	  }
10463 	/* Remove the entry if no discovered instances are left */
10464 	if (cupsArrayCount(p->ipp_discoveries) == 0) {
10465 	  debug_printf("Removing printer with Service name \"%s\", Domain \"%s\", all discovered instances disappeared.\n",
10466 		       p->service_name, p->domain);
10467 	  remove_printer_entry(p);
10468 	}
10469       }
10470 
10471       if (in_shutdown == 0)
10472 	recheck_timer ();
10473     }
10474     break;
10475   }
10476 
10477   /* All cached Avahi events are treated now */
10478   case AVAHI_BROWSER_ALL_FOR_NOW:
10479   case AVAHI_BROWSER_CACHE_EXHAUSTED:
10480     debug_printf("Avahi Browser: %s\n",
10481 		 event == AVAHI_BROWSER_CACHE_EXHAUSTED ?
10482 		 "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
10483     break;
10484   }
10485 
10486 }
10487 
avahi_browser_shutdown()10488 void avahi_browser_shutdown() {
10489   remote_printer_t *p;
10490 
10491   avahi_present = 0;
10492 
10493   /* Remove all queues which we have set up based on DNS-SD discovery*/
10494   if (cupsArrayCount(remote_printers) > 0) {
10495     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
10496 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
10497       if (p->type && p->type[0]) {
10498 	if (KeepGeneratedQueuesOnShutdown) {
10499 	  if (p->status != STATUS_TO_BE_RELEASED &&
10500 	      p->status != STATUS_DISAPPEARED) {
10501 	    p->status = STATUS_UNCONFIRMED;
10502 	    p->timeout = time(NULL) + TIMEOUT_CONFIRM;
10503 	  }
10504 	} else {
10505 	  if (p->status != STATUS_TO_BE_RELEASED)
10506 	    p->status = STATUS_DISAPPEARED;
10507 	  p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
10508 	}
10509       }
10510     }
10511     if (in_shutdown == 0)
10512       recheck_timer();
10513     else
10514       update_cups_queues(NULL);
10515   }
10516 
10517   /* Free the data structures for DNS-SD browsing */
10518   if (sb1) {
10519     avahi_service_browser_free(sb1);
10520     sb1 = NULL;
10521   }
10522   if (sb2) {
10523     avahi_service_browser_free(sb2);
10524     sb2 = NULL;
10525   }
10526 
10527   /* Switch on auto shutdown mode */
10528   if (autoshutdown_avahi && in_shutdown == 0) {
10529     autoshutdown = 1;
10530     debug_printf("Avahi server disappeared, switching to auto shutdown mode ...\n");
10531     /* If there are no printers or no jobs schedule the shutdown in
10532        autoshutdown_timeout seconds */
10533     if (!autoshutdown_exec_id &&
10534 	(cupsArrayCount(remote_printers) == 0 ||
10535 	 (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
10536       debug_printf ("We entered auto shutdown mode and no printers are there to make available or no jobs on them, shutting down in %d sec...\n", autoshutdown_timeout);
10537       autoshutdown_exec_id =
10538 	g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
10539 			       NULL);
10540     }
10541   }
10542 }
10543 
avahi_shutdown()10544 void avahi_shutdown() {
10545   avahi_browser_shutdown();
10546   if (client) {
10547     avahi_client_free(client);
10548     client = NULL;
10549   }
10550   if (glib_poll) {
10551     avahi_glib_poll_free(glib_poll);
10552     glib_poll = NULL;
10553   }
10554 }
10555 
client_callback(AvahiClient * c,AvahiClientState state,AVAHI_GCC_UNUSED void * userdata)10556 static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
10557   int error;
10558 
10559   if (c == NULL)
10560     return;
10561 
10562   /* Called whenever the client or server state changes */
10563   switch (state) {
10564 
10565   /* avahi-daemon available */
10566   case AVAHI_CLIENT_S_REGISTERING:
10567   case AVAHI_CLIENT_S_RUNNING:
10568   case AVAHI_CLIENT_S_COLLISION:
10569 
10570     debug_printf("Avahi server connection got available, setting up service browsers.\n");
10571 
10572     /* Create the service browsers */
10573     if (!sb1)
10574       if (!(sb1 =
10575 	    avahi_service_browser_new(c, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
10576 				      "_ipp._tcp", NULL, 0, browse_callback,
10577 				      c))) {
10578 	debug_printf("ERROR: Failed to create service browser for IPP: %s\n",
10579 		     avahi_strerror(avahi_client_errno(c)));
10580       }
10581     if (!sb2)
10582       if (!(sb2 =
10583 	    avahi_service_browser_new(c, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
10584 				      "_ipps._tcp", NULL, 0, browse_callback,
10585 				      c))) {
10586 	debug_printf("ERROR: Failed to create service browser for IPPS: %s\n",
10587 		     avahi_strerror(avahi_client_errno(c)));
10588       }
10589 
10590     avahi_present = 1;
10591 
10592     /* switch off auto shutdown mode */
10593     if (autoshutdown_avahi) {
10594       autoshutdown = 0;
10595       debug_printf("Avahi server available, switching to permanent mode ...\n");
10596       /* If there is still an active auto shutdown timer, kill it */
10597       if (autoshutdown_exec_id) {
10598 	debug_printf ("We have left auto shutdown mode, killing auto shutdown timer.\n");
10599 	g_source_remove(autoshutdown_exec_id);
10600 	autoshutdown_exec_id = 0;
10601       }
10602     }
10603 
10604     break;
10605 
10606   /* Avahi client error */
10607   case AVAHI_CLIENT_FAILURE:
10608 
10609     if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
10610       debug_printf("Avahi server disappeared, shutting down service browsers, removing DNS-SD-discovered print queues.\n");
10611       avahi_browser_shutdown();
10612       /* Renewing client */
10613       avahi_client_free(client);
10614       client = avahi_client_new(avahi_glib_poll_get(glib_poll),
10615 				AVAHI_CLIENT_NO_FAIL,
10616 				client_callback, NULL, &error);
10617       if (!client) {
10618 	debug_printf("ERROR: Failed to create client: %s\n",
10619 		     avahi_strerror(error));
10620 	BrowseRemoteProtocols &= ~BROWSE_DNSSD;
10621 	avahi_shutdown();
10622       }
10623     } else {
10624       debug_printf("ERROR: Avahi server connection failure: %s\n",
10625 		   avahi_strerror(avahi_client_errno(c)));
10626       g_main_loop_quit(gmainloop);
10627       g_main_context_wakeup(NULL);
10628     }
10629     break;
10630 
10631   default:
10632     break;
10633   }
10634 }
10635 
avahi_init()10636 void avahi_init() {
10637   int error;
10638 
10639   if (BrowseRemoteProtocols & BROWSE_DNSSD) {
10640     /* Allocate main loop object */
10641     if (!glib_poll)
10642       if (!(glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT))) {
10643 	debug_printf("ERROR: Failed to create glib poll object.\n");
10644 	goto avahi_init_fail;
10645       }
10646 
10647     /* Allocate a new client */
10648     if (!client)
10649       client = avahi_client_new(avahi_glib_poll_get(glib_poll),
10650 				AVAHI_CLIENT_NO_FAIL,
10651 				client_callback, NULL, &error);
10652 
10653     /* Check wether creating the client object succeeded */
10654     if (!client) {
10655       debug_printf("ERROR: Failed to create client: %s\n",
10656 		   avahi_strerror(error));
10657       goto avahi_init_fail;
10658     }
10659 
10660     return;
10661 
10662   avahi_init_fail:
10663     BrowseRemoteProtocols &= ~BROWSE_DNSSD;
10664     avahi_shutdown();
10665   }
10666 }
10667 #endif /* HAVE_AVAHI */
10668 
10669 /*
10670  * A CUPS printer has been discovered via CUPS Browsing
10671  * or with BrowsePoll
10672  */
10673 void
found_cups_printer(const char * remote_host,const char * uri,const char * location,const char * info)10674 found_cups_printer (const char *remote_host, const char *uri,
10675 		    const char *location, const char *info)
10676 {
10677   char scheme[32];
10678   char username[64];
10679   char host[HTTP_MAX_HOST];
10680   char resource[HTTP_MAX_URI];
10681   int port;
10682   netif_t *iface;
10683   char local_resource[HTTP_MAX_URI];
10684   char service_name[HTTP_MAX_URI];
10685   char *c;
10686   int hl;
10687   remote_printer_t *printer;
10688 
10689   memset(scheme, 0, sizeof(scheme));
10690   memset(username, 0, sizeof(username));
10691   memset(host, 0, sizeof(host));
10692   memset(resource, 0, sizeof(resource));
10693   memset(local_resource, 0, sizeof(local_resource));
10694 
10695   httpSeparateURI (HTTP_URI_CODING_ALL, uri,
10696 		   scheme, sizeof(scheme) - 1,
10697 		   username, sizeof(username) - 1,
10698 		   host, sizeof(host) - 1,
10699 		   &port,
10700 		   resource, sizeof(resource)- 1);
10701 
10702   /* Check this isn't one of our own broadcasts */
10703   for (iface = cupsArrayFirst (netifs);
10704        iface;
10705        iface = cupsArrayNext (netifs))
10706     if (!strcasecmp (host, iface->address))
10707       break;
10708   if (iface) {
10709     debug_printf("ignoring own broadcast on %s\n",
10710 		 iface->address);
10711     return;
10712   }
10713 
10714   if (strncasecmp (resource, "/printers/", 10) &&
10715       strncasecmp (resource, "/classes/", 9)) {
10716     debug_printf("Don't understand URI: %s\n", uri);
10717     return;
10718   }
10719 
10720   strncpy (local_resource, resource + 1, sizeof (local_resource) - 1);
10721   local_resource[sizeof (local_resource) - 1] = '\0';
10722   c = strchr (local_resource, '?');
10723   if (c)
10724     *c = '\0';
10725 
10726   /* Build the DNS-SD service name which CUPS would give to this printer
10727      when DNS-SD-broadcasting it */
10728   snprintf(service_name, sizeof (service_name), "%s @ %s",
10729 	   (info ? info : strchr(local_resource, '/') + 1), host);
10730   /* Cut off trailing ".local" of host name */
10731   hl = strlen(service_name);
10732   if (hl > 6 && !strcasecmp(service_name + hl - 6, ".local"))
10733     service_name[hl - 6] = '\0';
10734   if (hl > 7 && !strcasecmp(service_name + hl - 7, ".local."))
10735     service_name[hl - 7] = '\0';
10736   /* DNS-SD service name has max. 63 characters */
10737   service_name[63] = '\0';
10738 
10739   debug_printf("CUPS browsing: Remote host: %s; Port: %d; Remote queue name: %s; Service Name: %s\n",
10740 	       host, port, strchr(local_resource, '/') + 1, service_name);
10741 
10742   printer = examine_discovered_printer_record(host, NULL, port, local_resource,
10743 					      service_name,
10744 					      location ? location : "",
10745 					      info ? info : "", "", "", "", 0,
10746 					      NULL);
10747 
10748   if (printer &&
10749       (printer->domain == NULL || printer->domain[0] == '\0' ||
10750        printer->type == NULL || printer->type[0] == '\0')) {
10751     printer->is_legacy = 1;
10752 
10753     if (printer->status != STATUS_TO_BE_CREATED) {
10754       printer->timeout = time(NULL) + BrowseTimeout;
10755       debug_printf("starting BrowseTimeout timer for %s (%ds)\n",
10756 		   printer->queue_name, BrowseTimeout);
10757     }
10758   }
10759 
10760   if (printer && NewBrowsePollQueuesShared &&
10761       (HAVE_CUPS_1_6 || (!HAVE_CUPS_1_6 && !printer->is_legacy)))
10762     printer->num_options = cupsAddOption("printer-to-be-shared", "true", printer->num_options, &(printer->options));
10763 
10764 }
10765 
10766 gboolean
process_browse_data(GIOChannel * source,GIOCondition condition,gpointer data)10767 process_browse_data (GIOChannel *source,
10768 		     GIOCondition condition,
10769 		     gpointer data)
10770 {
10771   char packet[2048];
10772   http_addr_t srcaddr;
10773   socklen_t srclen;
10774   ssize_t got;
10775   unsigned int type;
10776   unsigned int state;
10777   char remote_host[256];
10778   char uri[1024];
10779   char location[1024];
10780   char info[1024];
10781   char *c = NULL, *end = NULL;
10782 
10783   debug_printf("process_browse_data() in THREAD %ld\n", pthread_self());
10784 
10785   memset(packet, 0, sizeof(packet));
10786   memset(remote_host, 0, sizeof(remote_host));
10787   memset(uri, 0, sizeof(uri));
10788   memset(info, 0, sizeof(info));
10789 
10790   srclen = sizeof (srcaddr);
10791   got = recvfrom (browsesocket, packet, sizeof (packet) - 1, 0,
10792 		  &srcaddr.addr, &srclen);
10793   if (got == -1) {
10794     debug_printf ("cupsd-browsed: error receiving browse packet: %s\n",
10795 		  strerror (errno));
10796     /* Remove this I/O source */
10797     return FALSE;
10798   }
10799 
10800   packet[got] = '\0';
10801   httpAddrString (&srcaddr, remote_host, sizeof (remote_host) - 1);
10802 
10803   /* Check this packet is allowed */
10804   if (!allowed ((struct sockaddr *) &srcaddr)) {
10805     debug_printf("browse packet from %s disallowed\n",
10806 		 remote_host);
10807     return TRUE;
10808   }
10809 
10810   debug_printf("browse packet received from %s\n",
10811 	       remote_host);
10812 
10813   if (sscanf (packet, "%x%x%1023s", &type, &state, uri) < 3) {
10814     debug_printf("incorrect browse packet format\n");
10815     return TRUE;
10816   }
10817 
10818   info[0] = '\0';
10819 
10820   /* do not read OOB */
10821   end = packet + sizeof(packet);
10822   c = strchr (packet, '\"');
10823   if (c >= end)
10824     return TRUE;
10825 
10826   if (c) {
10827     /* Extract location field */
10828     {
10829       int i;
10830       c++;
10831       for (i = 0;
10832 	   i < sizeof (location) - 1 && *c != '\"' && c < end;
10833 	   i++, c++)
10834 	location[i] = *c;
10835       location[i] = '\0';
10836       debug_printf("process_browse_data: location: |%s|\n", location); /* !! */
10837     }
10838     for (; c < end && *c != '\"'; c++)
10839       ;
10840 
10841     if (c >= end)
10842       return TRUE;
10843 
10844     if (*c == '\"') {
10845       for (c++; c < end && isspace(*c); c++)
10846 	;
10847     }
10848 
10849     if (c >= end)
10850       return TRUE;
10851 
10852     /* Is there an info field? */
10853     if (*c == '\"') {
10854       int i;
10855       c++;
10856       for (i = 0;
10857 	   i < sizeof (info) - 1 && *c != '\"' && c < end;
10858 	   i++, c++)
10859 	info[i] = *c;
10860       info[i] = '\0';
10861       debug_printf("process_browse_data: info: |%s|\n", info); /* !! */
10862     }
10863   }
10864   if (c >= end)
10865     return TRUE;
10866 
10867   if (!(type & CUPS_PRINTER_DELETE))
10868     found_cups_printer (remote_host, uri, location, info);
10869 
10870   if (in_shutdown == 0)
10871     recheck_timer ();
10872 
10873   /* Don't remove this I/O source */
10874   return TRUE;
10875 }
10876 
10877 static void
broadcast_browse_packets(gpointer data,gpointer user_data)10878 broadcast_browse_packets (gpointer data, gpointer user_data)
10879 {
10880   browse_data_t *bdata = data;
10881   netif_t *browse;
10882   char packet[2048];
10883   char uri[HTTP_MAX_URI];
10884   char scheme[32];
10885   char username[64];
10886   char host[HTTP_MAX_HOST];
10887   int port;
10888   char resource[HTTP_MAX_URI];
10889 
10890   debug_printf("broadcast_browse_packets() in THREAD %ld\n", pthread_self());
10891 
10892   for (browse = (netif_t *)cupsArrayFirst (netifs);
10893        browse != NULL;
10894        browse = (netif_t *)cupsArrayNext (netifs)) {
10895     /* Replace 'localhost' with our IP address on this interface */
10896     httpSeparateURI(HTTP_URI_CODING_ALL, bdata->uri,
10897 		    scheme, sizeof(scheme),
10898 		    username, sizeof(username),
10899 		    host, sizeof(host),
10900 		    &port,
10901 		    resource, sizeof(resource));
10902     httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof (uri),
10903 		    scheme, username, browse->address, port, resource);
10904 
10905     if (snprintf (packet, sizeof (packet),
10906 		  "%x "     /* type */
10907 		  "%x "     /* state */
10908 		  "%s "     /* uri */
10909 		  "\"%s\" " /* location */
10910 		  "\"%s\" " /* info */
10911 		  "\"%s\" " /* make-and-model */
10912 		  "lease-duration=%d" /* BrowseTimeout */
10913 		  "%s%s" /* other browse options */
10914 		  "\n",
10915 		  bdata->type,
10916 		  bdata->state,
10917 		  uri,
10918 		  bdata->location,
10919 		  bdata->info,
10920 		  bdata->make_model,
10921 		  BrowseTimeout,
10922 		  bdata->browse_options ? " " : "",
10923 		  bdata->browse_options ? bdata->browse_options : "")
10924 	>= sizeof (packet)) {
10925       debug_printf ("oversize packet not sent\n");
10926       continue;
10927     }
10928 
10929     debug_printf("packet to send:\n%s", packet);
10930 
10931     int err = sendto (browsesocket, packet,
10932 		      strlen (packet), 0,
10933 		      &browse->broadcast.addr,
10934 		      httpAddrLength (&browse->broadcast));
10935     if (err == -1)
10936       debug_printf("cupsd-browsed: sendto returned %d: %s\n",
10937 		   err, strerror (errno));
10938   }
10939 }
10940 
10941 gboolean
send_browse_data(gpointer data)10942 send_browse_data (gpointer data)
10943 {
10944   debug_printf("send_browse_data() in THREAD %ld\n", pthread_self());
10945   update_netifs (NULL);
10946   res_init ();
10947   update_local_printers ();
10948   g_list_foreach (browse_data, broadcast_browse_packets, NULL);
10949   g_timeout_add_seconds (BrowseInterval, send_browse_data, NULL);
10950 
10951   /* Stop this timeout handler, we called a new one */
10952   return FALSE;
10953 }
10954 
10955 static browsepoll_printer_t *
new_browsepoll_printer(const char * uri_supported,const char * location,const char * info)10956 new_browsepoll_printer (const char *uri_supported,
10957 			const char *location,
10958 			const char *info)
10959 {
10960   browsepoll_printer_t *printer = g_malloc (sizeof (browsepoll_printer_t));
10961   printer->uri_supported = g_strdup (uri_supported);
10962   printer->location = g_strdup (location);
10963   printer->info = g_strdup (info);
10964   return printer;
10965 }
10966 
10967 static void
browsepoll_printer_free(gpointer data)10968 browsepoll_printer_free (gpointer data)
10969 {
10970   browsepoll_printer_t *printer = data;
10971   debug_printf("browsepoll_printer_free() in THREAD %ld\n", pthread_self());
10972   free (printer->uri_supported);
10973   free (printer->location);
10974   free (printer->info);
10975   free (printer);
10976 }
10977 
10978 static void
browse_poll_get_printers(browsepoll_t * context,http_t * conn)10979 browse_poll_get_printers (browsepoll_t *context, http_t *conn)
10980 {
10981   static const char * const rattrs[] = { "printer-uri-supported",
10982 					 "printer-location",
10983 					 "printer-info"};
10984   ipp_t *request, *response = NULL;
10985   ipp_attribute_t *attr;
10986   GList *printers = NULL;
10987 
10988   debug_printf ("cups-browsed [BrowsePoll %s:%d]: CUPS-Get-Printers\n",
10989 		context->server, context->port);
10990 
10991   request = ippNewRequest(CUPS_GET_PRINTERS);
10992   if (context->major > 0) {
10993     debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
10994 		 context->server, context->port, context->major,
10995 		 context->minor);
10996     ippSetVersion (request, context->major, context->minor);
10997   }
10998 
10999   ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
11000 		 "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]),
11001 		 NULL,
11002 		 rattrs);
11003 
11004   /* Ask the server to exclude printers that are remote or not shared,
11005      or implicit classes. */
11006   ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
11007 		 "printer-type-mask",
11008 		 CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
11009 		 CUPS_PRINTER_NOT_SHARED);
11010   ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
11011 		 "printer-type", 0);
11012 
11013   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
11014 		"requesting-user-name", NULL, cupsUser ());
11015 
11016   response = cupsDoRequest(conn, request, "/");
11017   if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
11018     debug_printf("cups-browsed [BrowsePoll %s:%d]: failed: %s\n",
11019 		 context->server, context->port, cupsLastErrorString ());
11020     goto fail;
11021   }
11022 
11023   for (attr = ippFirstAttribute(response); attr;
11024        attr = ippNextAttribute(response)) {
11025     browsepoll_printer_t *printer;
11026     const char *uri, *location, *info;
11027 
11028     while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
11029       attr = ippNextAttribute(response);
11030 
11031     if (!attr)
11032       break;
11033 
11034     uri = NULL;
11035     info = NULL;
11036     location = NULL;
11037     while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
11038       if (!strcasecmp (ippGetName(attr), "printer-uri-supported") &&
11039 	  ippGetValueTag(attr) == IPP_TAG_URI)
11040 	uri = ippGetString(attr, 0, NULL);
11041       else if (!strcasecmp (ippGetName(attr), "printer-location") &&
11042 	       ippGetValueTag(attr) == IPP_TAG_TEXT)
11043 	location = ippGetString(attr, 0, NULL);
11044       else if (!strcasecmp (ippGetName(attr), "printer-info") &&
11045 	       ippGetValueTag(attr) == IPP_TAG_TEXT)
11046 	info = ippGetString(attr, 0, NULL);
11047       attr = ippNextAttribute(response);
11048     }
11049 
11050     if (uri) {
11051       found_cups_printer (context->server, uri, location, info);
11052       printer = new_browsepoll_printer (uri, location, info);
11053       printers = g_list_insert (printers, printer, 0);
11054     }
11055 
11056     if (!attr)
11057       break;
11058   }
11059 
11060   g_list_free_full (context->printers, browsepoll_printer_free);
11061   context->printers = printers;
11062 
11063 fail:
11064   if (response)
11065     ippDelete(response);
11066 }
11067 
11068 static void
browse_poll_create_subscription(browsepoll_t * context,http_t * conn)11069 browse_poll_create_subscription (browsepoll_t *context, http_t *conn)
11070 {
11071   static const char * const events[] = { "printer-added",
11072 					 "printer-changed",
11073 					 "printer-config-changed",
11074 					 "printer-modified",
11075 					 "printer-deleted",
11076 					 "printer-state-changed" };
11077   ipp_t *request, *response = NULL;
11078   ipp_attribute_t *attr;
11079 
11080   debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Create-Subscription\n",
11081 		context->server, context->port);
11082 
11083   request = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION);
11084   if (context->major > 0) {
11085     debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
11086 		 context->server, context->port, context->major,
11087 		 context->minor);
11088     ippSetVersion (request, context->major, context->minor);
11089   }
11090 
11091   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
11092 		"printer-uri", NULL, "/");
11093   ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
11094 		"notify-pull-method", NULL, "ippget");
11095   ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_CHARSET,
11096 		"notify-charset", NULL, "utf-8");
11097   ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
11098 		"requesting-user-name", NULL, cupsUser ());
11099   ippAddStrings (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
11100 		 "notify-events", sizeof (events) / sizeof (events[0]),
11101 		 NULL, events);
11102   ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
11103 		 "notify-time-interval", BrowseInterval);
11104 
11105   response = cupsDoRequest (conn, request, "/");
11106   if (!response ||
11107       ippGetStatusCode (response) > IPP_STATUS_OK_EVENTS_COMPLETE) {
11108     debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n",
11109 		 context->server, context->port, cupsLastErrorString ());
11110     context->subscription_id = -1;
11111     context->can_subscribe = FALSE;
11112     goto fail;
11113   }
11114 
11115   for (attr = ippFirstAttribute(response); attr;
11116        attr = ippNextAttribute(response)) {
11117     if (ippGetGroupTag (attr) == IPP_TAG_SUBSCRIPTION) {
11118       if (ippGetValueTag (attr) == IPP_TAG_INTEGER &&
11119 	  !strcasecmp (ippGetName (attr), "notify-subscription-id")) {
11120 	context->subscription_id = ippGetInteger (attr, 0);
11121 	debug_printf("cups-browsed [BrowsePoll %s:%d]: subscription ID=%d\n",
11122 		     context->server, context->port, context->subscription_id);
11123 	break;
11124       }
11125     }
11126   }
11127 
11128   if (!attr) {
11129     debug_printf("cups-browsed [BrowsePoll %s:%d]: no ID returned\n",
11130 		 context->server, context->port);
11131     context->subscription_id = -1;
11132     context->can_subscribe = FALSE;
11133   }
11134 
11135 fail:
11136   if (response)
11137     ippDelete(response);
11138 }
11139 
11140 static void
browse_poll_cancel_subscription(browsepoll_t * context)11141 browse_poll_cancel_subscription (browsepoll_t *context)
11142 {
11143   ipp_t *request, *response = NULL;
11144   http_t *conn = httpConnectEncryptShortTimeout (context->server, context->port,
11145 						 HTTP_ENCRYPT_IF_REQUESTED);
11146 
11147   if (conn == NULL) {
11148     debug_printf("cups-browsed [BrowsePoll %s:%d]: connection failure "
11149 		 "attempting to cancel\n", context->server, context->port);
11150     return;
11151   }
11152 
11153   httpSetTimeout(conn, HttpRemoteTimeout, http_timeout_cb, NULL);
11154 
11155   debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Cancel-Subscription\n",
11156 		context->server, context->port);
11157 
11158   request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
11159   if (context->major > 0) {
11160     debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
11161 		 context->server, context->port, context->major,
11162 		 context->minor);
11163     ippSetVersion (request, context->major, context->minor);
11164   }
11165 
11166   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
11167 		"printer-uri", NULL, "/");
11168   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
11169 		"requesting-user-name", NULL, cupsUser ());
11170   ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
11171 		 "notify-subscription-id", context->subscription_id);
11172 
11173   response = cupsDoRequest (conn, request, "/");
11174   if (!response ||
11175       ippGetStatusCode (response) > IPP_STATUS_OK_EVENTS_COMPLETE)
11176     debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n",
11177 		 context->server, context->port, cupsLastErrorString ());
11178 
11179   if (response)
11180     ippDelete(response);
11181   if (conn)
11182     httpClose (conn);
11183 }
11184 
11185 static gboolean
browse_poll_get_notifications(browsepoll_t * context,http_t * conn)11186 browse_poll_get_notifications (browsepoll_t *context, http_t *conn)
11187 {
11188   ipp_t *request, *response = NULL;
11189   ipp_status_t status;
11190   gboolean get_printers = FALSE;
11191 
11192   debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Get-Notifications\n",
11193 		context->server, context->port);
11194 
11195   request = ippNewRequest(IPP_GET_NOTIFICATIONS);
11196   if (context->major > 0) {
11197     debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
11198 		 context->server, context->port, context->major,
11199 		 context->minor);
11200     ippSetVersion (request, context->major, context->minor);
11201   }
11202 
11203   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
11204 		"printer-uri", NULL, "/");
11205   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
11206 		"requesting-user-name", NULL, cupsUser ());
11207   ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
11208 		 "notify-subscription-ids", context->subscription_id);
11209   ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
11210 		 "notify-sequence-numbers", context->sequence_number + 1);
11211 
11212   response = cupsDoRequest (conn, request, "/");
11213   if (!response)
11214     status = cupsLastError ();
11215   else
11216     status = ippGetStatusCode (response);
11217 
11218   if (status == IPP_STATUS_ERROR_NOT_FOUND) {
11219     /* Subscription lease has expired. */
11220     debug_printf ("cups-browsed [BrowsePoll %s:%d]: Lease expired\n",
11221 		  context->server, context->port);
11222     browse_poll_create_subscription (context, conn);
11223     get_printers = TRUE;
11224   } else if (status > IPP_STATUS_OK_EVENTS_COMPLETE) {
11225     debug_printf("cups-browsed [BrowsePoll %s:%d]: failed: %s\n",
11226 		 context->server, context->port, cupsLastErrorString ());
11227     context->can_subscribe = FALSE;
11228     browse_poll_cancel_subscription (context);
11229     context->subscription_id = -1;
11230     context->sequence_number = 0;
11231     get_printers = TRUE;
11232   }
11233 
11234   if (!get_printers) {
11235     ipp_attribute_t *attr;
11236     gboolean seen_event = FALSE;
11237     int last_seq = context->sequence_number;
11238     if (response == NULL)
11239       return FALSE;
11240     for (attr = ippFirstAttribute(response); attr;
11241 	 attr = ippNextAttribute(response))
11242       if (ippGetGroupTag (attr) == IPP_TAG_EVENT_NOTIFICATION) {
11243 	/* There is a printer-* event here. */
11244 	seen_event = TRUE;
11245 
11246 	if (!strcmp (ippGetName (attr), "notify-sequence-number") &&
11247 	    ippGetValueTag (attr) == IPP_TAG_INTEGER)
11248 	  last_seq = ippGetInteger (attr, 0);
11249       }
11250 
11251     if (seen_event) {
11252       debug_printf("cups-browsed [BrowsePoll %s:%d]: printer-* event\n",
11253 		   context->server, context->port);
11254       context->sequence_number = last_seq;
11255       get_printers = TRUE;
11256     } else
11257       debug_printf("cups-browsed [BrowsePoll %s:%d]: no events\n",
11258 		   context->server, context->port);
11259   }
11260 
11261   if (response)
11262     ippDelete (response);
11263 
11264   return get_printers;
11265 }
11266 
11267 static void
browsepoll_printer_keepalive(gpointer data,gpointer user_data)11268 browsepoll_printer_keepalive (gpointer data, gpointer user_data)
11269 {
11270   browsepoll_printer_t *printer = data;
11271   const char *server = user_data;
11272   debug_printf("browsepoll_printer_keepalive() in THREAD %ld\n",
11273 	       pthread_self());
11274   found_cups_printer (server, printer->uri_supported, printer->location,
11275 		      printer->info);
11276 }
11277 
11278 gboolean
browse_poll(gpointer data)11279 browse_poll (gpointer data)
11280 {
11281   browsepoll_t *context = data;
11282   http_t *conn = NULL;
11283   gboolean get_printers = FALSE;
11284 
11285   debug_printf("browse_poll() in THREAD %ld\n", pthread_self());
11286 
11287   debug_printf ("browse polling %s:%d\n",
11288 		context->server, context->port);
11289 
11290   res_init ();
11291 
11292   conn = httpConnectEncryptShortTimeout (context->server, context->port,
11293 					 HTTP_ENCRYPT_IF_REQUESTED);
11294   if (conn == NULL) {
11295     debug_printf("cups-browsed [BrowsePoll %s:%d]: failed to connect\n",
11296 		 context->server, context->port);
11297     goto fail;
11298   }
11299 
11300   httpSetTimeout(conn, HttpRemoteTimeout, http_timeout_cb, NULL);
11301 
11302   if (context->can_subscribe) {
11303     if (context->subscription_id == -1) {
11304       /* The first time this callback is run we need to create the IPP
11305        * subscription to watch to printer-* events. */
11306       browse_poll_create_subscription (context, conn);
11307       get_printers = TRUE;
11308     } else
11309       /* On subsequent runs, check for notifications using our
11310        * subscription. */
11311       get_printers = browse_poll_get_notifications (context, conn);
11312   }
11313   else
11314     get_printers = TRUE;
11315 
11316   update_local_printers ();
11317   inhibit_local_printers_update = TRUE;
11318   if (get_printers)
11319     browse_poll_get_printers (context, conn);
11320   else
11321     g_list_foreach (context->printers, browsepoll_printer_keepalive,
11322 		    context->server);
11323 
11324   inhibit_local_printers_update = FALSE;
11325 
11326   if (in_shutdown == 0)
11327     recheck_timer ();
11328 
11329  fail:
11330 
11331   if (conn)
11332     httpClose (conn);
11333 
11334   /* Call a new timeout handler so that we run again */
11335   g_timeout_add_seconds (BrowseInterval, browse_poll, data);
11336 
11337   /* Stop this timeout handler, we called a new one */
11338   return FALSE;
11339 }
11340 
11341 #ifdef HAVE_LDAP
11342 gboolean
browse_ldap_poll(gpointer data)11343 browse_ldap_poll (gpointer data)
11344 {
11345   char                  *tmpFilter;     /* Query filter */
11346   int                   filterLen;
11347 
11348   debug_printf("browse_ldap_poll() in THREAD %ld\n", pthread_self());
11349 
11350   /* do real stuff here */
11351   if (!BrowseLDAPDN) {
11352     debug_printf("Need to set BrowseLDAPDN to use LDAP browsing!\n");
11353     BrowseLocalProtocols &= ~BROWSE_LDAP;
11354     BrowseRemoteProtocols &= ~BROWSE_LDAP;
11355 
11356     return FALSE;
11357   } else {
11358     if (!BrowseLDAPInitialised) {
11359       BrowseLDAPInitialised = TRUE;
11360       /*
11361        * Query filter string
11362        */
11363       if (BrowseLDAPFilter)
11364 	filterLen = snprintf(NULL, 0, "(&%s%s)", LDAP_BROWSE_FILTER,
11365 			     BrowseLDAPFilter);
11366       else
11367 	filterLen = strlen(LDAP_BROWSE_FILTER);
11368 
11369       tmpFilter = (char *)malloc(filterLen + 1);
11370       if (!tmpFilter) {
11371 	debug_printf("Could not allocate memory for LDAP browse query filter!\n");
11372 	BrowseLocalProtocols &= ~BROWSE_LDAP;
11373 	BrowseRemoteProtocols &= ~BROWSE_LDAP;
11374 	return FALSE;
11375       }
11376 
11377       if (BrowseLDAPFilter) {
11378 	snprintf(tmpFilter, filterLen + 1, "(&%s%s)", LDAP_BROWSE_FILTER,
11379 		 BrowseLDAPFilter);
11380 	free(BrowseLDAPFilter);
11381 	BrowseLDAPFilter = NULL;
11382       } else
11383 	strcpy(tmpFilter, LDAP_BROWSE_FILTER);
11384 
11385       BrowseLDAPFilter = tmpFilter;
11386 
11387       /*
11388        * Open LDAP handle...
11389        */
11390 
11391       BrowseLDAPHandle = ldap_new_connection();
11392     }
11393 
11394     cupsdUpdateLDAPBrowse();
11395     if (in_shutdown == 0)
11396       recheck_timer();
11397   }
11398 
11399   /* Call a new timeout handler so that we run again */
11400   g_timeout_add_seconds (BrowseInterval, browse_ldap_poll, data);
11401 
11402   /* Stop this timeout handler, we called a new one */
11403   return FALSE;
11404 }
11405 #endif /* HAVE_LDAP */
11406 
11407 static void
sigterm_handler(int sig)11408 sigterm_handler(int sig) {
11409   (void)sig;    /* remove compiler warnings... */
11410 
11411   if (terminating) {
11412     debug_printf("Caught signal %d while already terminating.\n", sig);
11413     return;
11414   }
11415   terminating = 1; /* ignore any further callbacks and break loops */
11416   /* Flag that we should stop and return... */
11417   g_main_loop_quit(gmainloop);
11418   g_main_context_wakeup(NULL);
11419   debug_printf("Caught signal %d, shutting down ...\n", sig);
11420 }
11421 
11422 static void
sigusr1_handler(int sig)11423 sigusr1_handler(int sig) {
11424   (void)sig;    /* remove compiler warnings... */
11425 
11426   /* Turn off auto shutdown mode... */
11427   autoshutdown = 0;
11428   debug_printf("Caught signal %d, switching to permanent mode ...\n", sig);
11429   /* If there is still an active auto shutdown timer, kill it */
11430   if (autoshutdown_exec_id) {
11431     debug_printf ("We have left auto shutdown mode, killing auto shutdown timer.\n");
11432     g_source_remove(autoshutdown_exec_id);
11433     autoshutdown_exec_id = 0;
11434   }
11435 }
11436 
11437 static void
sigusr2_handler(int sig)11438 sigusr2_handler(int sig) {
11439   (void)sig;    /* remove compiler warnings... */
11440 
11441   /* Turn on auto shutdown mode... */
11442   autoshutdown = 1;
11443   debug_printf("Caught signal %d, switching to auto shutdown mode ...\n", sig);
11444   /* If there are no printers or no jobs schedule the shutdown in
11445      autoshutdown_timeout seconds */
11446   if (!autoshutdown_exec_id &&
11447       (cupsArrayCount(remote_printers) == 0 ||
11448        (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
11449     debug_printf ("We entered auto shutdown mode and no printers are there to make available or no jobs on them, shutting down in %d sec...\n", autoshutdown_timeout);
11450     autoshutdown_exec_id =
11451       g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
11452              NULL);
11453   }
11454 }
11455 
11456 static int
read_browseallow_value(const char * value,allow_sense_t sense)11457 read_browseallow_value (const char *value, allow_sense_t sense)
11458 {
11459   char *p;
11460   struct in_addr addr;
11461   allow_t *allow;
11462 
11463   if (value && !strcasecmp (value, "all")) {
11464     if (sense == ALLOW_ALLOW) {
11465       browseallow_all = TRUE;
11466       return 0;
11467     } else if (sense == ALLOW_DENY) {
11468       browsedeny_all = TRUE;
11469       return 0;
11470     } else
11471       return 1;
11472   }
11473 
11474   allow = calloc (1, sizeof (allow_t));
11475   allow->sense = sense;
11476   if (value == NULL)
11477     goto fail;
11478   p = strchr (value, '/');
11479   if (p) {
11480     char *s = strdup (value);
11481     s[p - value] = '\0';
11482 
11483     if (!inet_aton (s, &addr)) {
11484       free (s);
11485       goto fail;
11486     }
11487 
11488     free (s);
11489     allow->type = ALLOW_NET;
11490     allow->addr.ipv4.sin_addr.s_addr = addr.s_addr;
11491 
11492     p++;
11493     if (strchr (p, '.')) {
11494       if (inet_aton (p, &addr))
11495 	allow->mask.ipv4.sin_addr.s_addr = addr.s_addr;
11496       else
11497 	goto fail;
11498     } else {
11499       char *endptr;
11500       unsigned long bits = strtoul (p, &endptr, 10);
11501       if (p == endptr)
11502 	goto fail;
11503 
11504       if (bits > 32)
11505 	goto fail;
11506 
11507       allow->mask.ipv4.sin_addr.s_addr = htonl (((0xffffffff << (32 - bits)) &
11508 						 0xffffffff));
11509     }
11510   } else if (inet_aton (value, &addr)) {
11511     allow->type = ALLOW_IP;
11512     allow->addr.ipv4.sin_addr.s_addr = addr.s_addr;
11513   } else
11514     goto fail;
11515 
11516   cupsArrayAdd (browseallow, allow);
11517   return 0;
11518 
11519 fail:
11520   allow->type = ALLOW_INVALID;
11521   cupsArrayAdd (browseallow, allow);
11522   return 1;
11523 }
11524 
11525 void
read_configuration(const char * filename)11526 read_configuration (const char *filename)
11527 {
11528   cups_file_t *fp;
11529   int i, linenum = 0;
11530   char line[HTTP_MAX_BUFFER];
11531   char *value = NULL, *ptr, *ptr2, *start;
11532   const char *delim = " \t,";
11533   int browse_allow_line_found = 0;
11534   int browse_deny_line_found = 0;
11535   int browse_order_line_found = 0;
11536   int browse_line_found = 0;
11537   browse_filter_t *filter = NULL;
11538   int browse_filter_options, exact_match, err;
11539   char errbuf[1024];
11540   cluster_t *cluster = NULL;
11541 
11542   if (!filename)
11543     filename = CUPS_SERVERROOT "/cups-browsed.conf";
11544 
11545   if ((fp = cupsFileOpen(filename, "r")) == NULL) {
11546     debug_printf("unable to open configuration file; "
11547 		 "using defaults\n");
11548     return;
11549   }
11550 
11551   i = 0;
11552   linenum = -1;
11553   /* First, we read the option settings supplied on the command line via
11554      "-o ..." in the order given on the command line, then we read the lines
11555      of the configuration file. This means that if there are contradicting
11556      settings on the command line and in the configuration file, the setting
11557      in the configuration file is used. */
11558   while ((i < cupsArrayCount(command_line_config) &&
11559 	  (value = cupsArrayIndex(command_line_config, i++)) &&
11560 	  strncpy(line, value, sizeof(line) - 1) &&
11561 	  ((strlen(value) > HTTP_MAX_BUFFER-1) ?
11562 	   line[HTTP_MAX_BUFFER-1] = '\0':  1)) ||
11563 	 cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) {
11564     if (linenum < 0) {
11565       /* We are still reading options from the command line ("-o ..."),
11566 	 separate key (line) and value (value) */
11567       value = line;
11568       while (*value && !isspace(*value) && !(*value == '='))
11569 	value ++;
11570       if (*value) {
11571 	*value = '\0';
11572 	value ++;
11573 	while (*value && (isspace(*value) || (*value == '=')))
11574 	  value ++;
11575       }
11576     }
11577 
11578     debug_printf("Reading config%s: %s %s\n",
11579 		 (linenum < 0 ? " (from command line)" : ""), line, value);
11580     if (!strcasecmp(line, "DebugLogging") && value) {
11581       char *p, *saveptr;
11582       p = strtok_r (value, delim, &saveptr);
11583       while (p) {
11584 	if (!strcasecmp(p, "file")) {
11585 	  if (debug_logfile == 0) {
11586 	    debug_logfile = 1;
11587 	    start_debug_logging();
11588 	  }
11589 	} else if (!strcasecmp(p, "stderr"))
11590 	  debug_stderr = 1;
11591 	else if (strcasecmp(p, "none"))
11592 	  debug_printf("Unknown debug logging mode '%s'\n", p);
11593 
11594 	p = strtok_r (NULL, delim, &saveptr);
11595       }
11596     } else if (!strcasecmp(line, "CacheDir") && value) {
11597       if (value[0] != '\0')
11598 	strncpy(cachedir, value, sizeof(cachedir) - 1);
11599     } else if (!strcasecmp(line, "LogDir") && value) {
11600       if (value[0] != '\0')
11601 	strncpy(logdir, value, sizeof(logdir) - 1);
11602     } else if ((!strcasecmp(line, "BrowseProtocols") ||
11603 		!strcasecmp(line, "BrowseLocalProtocols") ||
11604 		!strcasecmp(line, "BrowseRemoteProtocols")) && value) {
11605       int protocols = 0;
11606       char *p, *saveptr;
11607       p = strtok_r (value, delim, &saveptr);
11608       while (p) {
11609 	if (!strcasecmp(p, "dnssd"))
11610 	  protocols |= BROWSE_DNSSD;
11611 	else if (!strcasecmp(p, "cups"))
11612 	  protocols |= BROWSE_CUPS;
11613 	else if (!strcasecmp(p, "ldap"))
11614 	  protocols |= BROWSE_LDAP;
11615 	else if (strcasecmp(p, "none"))
11616 	  debug_printf("Unknown protocol '%s'\n", p);
11617 
11618 	p = strtok_r (NULL, delim, &saveptr);
11619       }
11620 
11621       if (!strcasecmp(line, "BrowseLocalProtocols"))
11622 	BrowseLocalProtocols = protocols;
11623       else if (!strcasecmp(line, "BrowseRemoteProtocols"))
11624 	BrowseRemoteProtocols = protocols;
11625       else
11626 	BrowseLocalProtocols = BrowseRemoteProtocols = protocols;
11627     } else if (!strcasecmp(line, "BrowsePoll") && value) {
11628       browsepoll_t **old = BrowsePoll;
11629       BrowsePoll = realloc (BrowsePoll,
11630 			    (NumBrowsePoll + 1) *
11631 			    sizeof (browsepoll_t *));
11632       if (!BrowsePoll) {
11633 	debug_printf("unable to realloc: ignoring BrowsePoll line\n");
11634 	BrowsePoll = old;
11635       } else {
11636 	char *colon, *slash;
11637 	browsepoll_t *b = g_malloc0 (sizeof (browsepoll_t));
11638 	debug_printf("Adding BrowsePoll server: %s\n", value);
11639 	b->server = strdup (value);
11640 	b->port = BrowsePort;
11641 	b->can_subscribe = TRUE; /* first assume subscriptions work */
11642 	b->subscription_id = -1;
11643 	slash = strchr (b->server, '/');
11644 	if (slash) {
11645 	  *slash++ = '\0';
11646 	  if (!strcasecmp (slash, "version=1.0")) {
11647 	    b->major = 1;
11648 	    b->minor = 0;
11649 	  } else if (!strcasecmp (slash, "version=1.1")) {
11650 	    b->major = 1;
11651 	    b->minor = 1;
11652 	  } else if (!strcasecmp (slash, "version=2.0")) {
11653 	    b->major = 2;
11654 	    b->minor = 0;
11655 	  } else if (!strcasecmp (slash, "version=2.1")) {
11656 	    b->major = 2;
11657 	    b->minor = 1;
11658 	  } else if (!strcasecmp (slash, "version=2.2")) {
11659 	    b->major = 2;
11660 	    b->minor = 2;
11661 	  } else {
11662 	    debug_printf ("ignoring unknown server option: %s\n", slash);
11663 	  }
11664 	} else
11665 	  b->major = 0;
11666 
11667 	colon = strchr (b->server, ':');
11668 	if (colon) {
11669 	  char *endptr;
11670 	  unsigned long n;
11671 	  *colon++ = '\0';
11672 	  n = strtoul (colon, &endptr, 10);
11673 	  if (endptr != colon && n < INT_MAX)
11674 	    b->port = (int) n;
11675 	}
11676 
11677 	BrowsePoll[NumBrowsePoll++] = b;
11678       }
11679     } else if (!strcasecmp(line, "BrowseAllow")) {
11680       if (read_browseallow_value (value, ALLOW_ALLOW))
11681 	debug_printf ("BrowseAllow value \"%s\" not understood\n",
11682 		      value);
11683       else {
11684 	browse_allow_line_found = 1;
11685 	browse_line_found = 1;
11686       }
11687     } else if (!strcasecmp(line, "BrowseDeny")) {
11688       if (read_browseallow_value (value, ALLOW_DENY))
11689 	debug_printf ("BrowseDeny value \"%s\" not understood\n",
11690 		      value);
11691       else {
11692 	browse_deny_line_found = 1;
11693 	browse_line_found = 1;
11694       }
11695     } else if (!strcasecmp(line, "BrowseOrder") && value) {
11696       if (!strncasecmp(value, "Allow", 5) &&
11697 	  strcasestr(value, "Deny")) { /* Allow,Deny */
11698 	browse_order = ORDER_ALLOW_DENY;
11699 	browse_order_line_found = 1;
11700 	browse_line_found = 1;
11701       } else if (!strncasecmp(value, "Deny", 4) &&
11702 		 strcasestr(value, "Allow")) { /* Deny,Allow */
11703 	browse_order = ORDER_DENY_ALLOW;
11704 	browse_order_line_found = 1;
11705 	browse_line_found = 1;
11706       } else
11707 	debug_printf ("BrowseOrder value \"%s\" not understood\n",
11708 		      value);
11709     } else if (!strcasecmp(line, "BrowseFilter") && value) {
11710       ptr = value;
11711       /* Skip white space */
11712       while (*ptr && isspace(*ptr)) ptr ++;
11713       /* Premature line end */
11714       if (!*ptr) goto browse_filter_fail;
11715       filter = calloc (1, sizeof (browse_filter_t));
11716       if (!filter) goto browse_filter_fail;
11717       browse_filter_options = 1;
11718       filter->sense = FILTER_MATCH;
11719       exact_match = 0;
11720       while (browse_filter_options) {
11721 	if (!strncasecmp(ptr, "NOT", 3) && *(ptr + 3) &&
11722 	    isspace(*(ptr + 3))) {
11723 	  /* Accept remote printers where regexp does NOT match or where
11724 	     the boolean field is false */
11725 	  filter->sense = FILTER_NOT_MATCH;
11726 	  ptr += 4;
11727 	  /* Skip white space until next word */
11728 	  while (*ptr && isspace(*ptr)) ptr ++;
11729 	  /* Premature line end without field name */
11730 	  if (!*ptr) goto browse_filter_fail;
11731 	} else if (!strncasecmp(ptr, "EXACT", 5) && *(ptr + 5) &&
11732 		   isspace(*(ptr + 5))) {
11733 	  /* Consider the rest of the line after the field name a string which
11734 	     has to match the field exactly */
11735 	  exact_match = 1;
11736 	  ptr += 6;
11737 	  /* Skip white space until next word */
11738 	  while (*ptr && isspace(*ptr)) ptr ++;
11739 	  /* Premature line end without field name */
11740 	  if (!*ptr) goto browse_filter_fail;
11741 	} else
11742 	  /* No more options, consider next word the name of the field which
11743 	     should match the regexp */
11744 	  browse_filter_options = 0;
11745       }
11746       start = ptr;
11747       while (*ptr && !isspace(*ptr)) ptr ++;
11748       if (*ptr) {
11749 	/* Mark end of the field name */
11750 	*ptr = '\0';
11751 	/* Skip white space until regexp or line end */
11752 	ptr ++;
11753 	while (*ptr && isspace(*ptr)) ptr ++;
11754       }
11755       filter->field = strdup(start);
11756       if (!*ptr) {
11757 	/* Only field name and no regexp is given, so this rule is
11758 	   about matching a boolean value */
11759 	filter->regexp = NULL;
11760 	filter->cregexp = NULL;
11761       } else {
11762 	/* The rest of the line is the regexp, store and compile it */
11763 	filter->regexp = strdup(ptr);
11764 	if (!exact_match) {
11765 	  /* Compile the regexp only if the line does not require an exact
11766 	     match (using the EXACT option */
11767 	  filter->cregexp = calloc(1, sizeof (regex_t));
11768 	  if ((err = regcomp(filter->cregexp, filter->regexp,
11769 			     REG_EXTENDED | REG_ICASE)) != 0) {
11770 	    regerror(err, filter->cregexp, errbuf, sizeof(errbuf));
11771 	    debug_printf ("BrowseFilter line with error in regular expression \"%s\": %s\n",
11772 			  filter->regexp, errbuf);
11773 	    goto browse_filter_fail;
11774 	  }
11775 	} else
11776 	  filter->cregexp = NULL;
11777       }
11778       cupsArrayAdd (browsefilter, filter);
11779       continue;
11780     browse_filter_fail:
11781       if (filter) {
11782 	if (filter->field)
11783 	  free(filter->field);
11784 	if (filter->regexp)
11785 	  free(filter->regexp);
11786 	if (filter->cregexp)
11787 	  regfree(filter->cregexp);
11788 	free(filter);
11789 	filter = NULL;
11790       }
11791     } else if ((!strcasecmp(line, "BrowseInterval") || !strcasecmp(line, "BrowseTimeout")) && value) {
11792       int t = atoi(value);
11793       if (t >= 0) {
11794 	if (!strcasecmp(line, "BrowseInterval"))
11795 	  BrowseInterval = t;
11796 	else if (!strcasecmp(line, "BrowseTimeout"))
11797 	  BrowseTimeout = t;
11798 
11799 	debug_printf("Set %s to %d sec.\n",
11800 		     line, t);
11801       } else
11802 	debug_printf("Invalid %s value: %d\n",
11803 		     line, t);
11804     } else if (!strcasecmp(line, "DomainSocket") && value) {
11805       if (value[0] != '\0') {
11806 	if (DomainSocket != NULL)
11807 	  free(DomainSocket);
11808 	DomainSocket = strdup(value);
11809       }
11810     } else if ((!strcasecmp(line, "HttpLocalTimeout") || !strcasecmp(line, "HttpRemoteTimeout")) && value) {
11811       int t = atoi(value);
11812       if (t >= 0) {
11813 	if (!strcasecmp(line, "HttpLocalTimeout"))
11814 	  HttpLocalTimeout = t;
11815 	else if (!strcasecmp(line, "HttpRemoteTimeout"))
11816 	  HttpRemoteTimeout = t;
11817 
11818 	debug_printf("Set %s to %d sec.\n",
11819 		     line, t);
11820       } else
11821 	debug_printf("Invalid %s value: %d\n",
11822 		     line, t);
11823     } else if (!strcasecmp(line, "NotifLeaseDuration") && value) {
11824       int t = atoi(value);
11825       if (t >= 300) {
11826 	  notify_lease_duration = t;
11827 	debug_printf("Set %s to %d sec.\n",
11828 		     line, t);
11829       } else
11830 	debug_printf("Invalid %s value: %d\n",
11831 		     line, t);
11832     } else if (!strcasecmp(line, "HttpMaxRetries") && value) {
11833       int t = atoi(value);
11834       if (t > 0) {
11835 	HttpMaxRetries = t;
11836 
11837 	debug_printf("Set %s to %d retries.\n",
11838 		     line, t);
11839       } else
11840 	debug_printf("Invalid %s value: %d\n",
11841 		     line, t);
11842     } else if (!strcasecmp(line, "DNSSDBasedDeviceURIs") && value) {
11843       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11844 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11845 	DNSSDBasedDeviceURIs = 1;
11846       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11847 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11848 	DNSSDBasedDeviceURIs = 0;
11849     } else if (!strcasecmp(line, "IPBasedDeviceURIs") && value) {
11850       if (!strcasecmp(value, "IPv4") || !strcasecmp(value, "IPv4Only"))
11851 	IPBasedDeviceURIs = IP_BASED_URIS_IPV4_ONLY;
11852       else if (!strcasecmp(value, "IPv6") || !strcasecmp(value, "IPv6Only"))
11853 	IPBasedDeviceURIs = IP_BASED_URIS_IPV6_ONLY;
11854       else if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11855 	       !strcasecmp(value, "on") || !strcasecmp(value, "1") ||
11856 	       !strcasecmp(value, "IP") || !strcasecmp(value, "IPAddress"))
11857 	IPBasedDeviceURIs = IP_BASED_URIS_ANY;
11858       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11859 	       !strcasecmp(value, "off") || !strcasecmp(value, "0") ||
11860 	       !strcasecmp(value, "Name") || !strcasecmp(value, "HostName"))
11861 	IPBasedDeviceURIs = IP_BASED_URIS_NO;
11862     } else if (!strcasecmp(line, "LocalQueueNamingRemoteCUPS") && value) {
11863       if (strcasestr(value, "DNSSD") || strcasestr(value, "DNS-SD"))
11864 	LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_DNSSD;
11865       else if (strcasestr(value, "Make") && strcasestr(value, "Model"))
11866 	LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_MAKE_MODEL;
11867       else if (strcasestr(value, "Remote") || strcasestr(value, "Name"))
11868 	LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_REMOTE_NAME;
11869     } else if (!strcasecmp(line, "LocalQueueNamingIPPPrinter") && value) {
11870       if (strcasestr(value, "DNSSD") || strcasestr(value, "DNS-SD"))
11871 	LocalQueueNamingIPPPrinter = LOCAL_QUEUE_NAMING_DNSSD;
11872       else if (strcasestr(value, "Make") && strcasestr(value, "Model"))
11873 	LocalQueueNamingIPPPrinter = LOCAL_QUEUE_NAMING_MAKE_MODEL;
11874     } else if (!strcasecmp(line, "OnlyUnsupportedByCUPS") && value) {
11875       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11876 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11877 	OnlyUnsupportedByCUPS = 1;
11878       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11879 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11880 	OnlyUnsupportedByCUPS = 0;
11881     } else if (!strcasecmp(line, "UseCUPSGeneratedPPDs") && value) {
11882       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11883 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11884 	UseCUPSGeneratedPPDs = 1;
11885       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11886 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11887 	UseCUPSGeneratedPPDs = 0;
11888     } else if (!strcasecmp(line, "CreateRemoteRawPrinterQueues") && value) {
11889       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11890 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11891 	CreateRemoteRawPrinterQueues = 1;
11892       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11893 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11894 	CreateRemoteRawPrinterQueues = 0;
11895     } else if (!strcasecmp(line, "CreateRemoteCUPSPrinterQueues") && value) {
11896       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11897 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11898 	CreateRemoteCUPSPrinterQueues = 1;
11899       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11900 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11901 	CreateRemoteCUPSPrinterQueues = 0;
11902     } else if (!strcasecmp(line, "CreateIPPPrinterQueues") && value) {
11903       if (!strcasecmp(value, "all") ||
11904 	  !strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11905 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11906 	CreateIPPPrinterQueues = IPP_PRINTERS_ALL;
11907       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11908 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11909 	CreateIPPPrinterQueues = IPP_PRINTERS_NO;
11910       else if (strcasestr(value, "local") || strcasestr(value, "usb"))
11911 	CreateIPPPrinterQueues = IPP_PRINTERS_LOCAL_ONLY;
11912       else if (strcasestr(value, "driver") && strcasestr(value, "less"))
11913 	CreateIPPPrinterQueues = IPP_PRINTERS_DRIVERLESS;
11914       else if (strcasestr(value, "every") || strcasestr(value, "pwg"))
11915 	CreateIPPPrinterQueues = IPP_PRINTERS_PWGRASTER;
11916       else if (strcasestr(value, "apple") || strcasestr(value, "air"))
11917 	CreateIPPPrinterQueues = IPP_PRINTERS_APPLERASTER;
11918       else if (strcasestr(value, "pclm") || strcasestr(value, "pcl-m"))
11919 	CreateIPPPrinterQueues = IPP_PRINTERS_PCLM;
11920       else if (strcasestr(value, "pdf"))
11921 	CreateIPPPrinterQueues = IPP_PRINTERS_PDF;
11922     } else if (!strcasecmp(line, "IPPPrinterQueueType") && value) {
11923       if (!strncasecmp(value, "Auto", 4))
11924 	IPPPrinterQueueType = PPD_YES;
11925       else if (!strncasecmp(value, "PPD", 3))
11926 	IPPPrinterQueueType = PPD_YES;
11927       else if (!strncasecmp(value, "NoPPD", 5))
11928 	IPPPrinterQueueType = PPD_NO;
11929       else if (!strncasecmp(value, "Interface", 9))
11930 	IPPPrinterQueueType = PPD_NO;
11931     } else if (!strcasecmp(line, "NewIPPPrinterQueuesShared") && value) {
11932       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11933 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11934 	NewIPPPrinterQueuesShared = 1;
11935       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11936 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11937 	NewIPPPrinterQueuesShared = 0;
11938     } else if(!strcasecmp(line, "DebugLogFileSize") && value) {
11939       int val = atoi(value);
11940       if(val<=0){
11941         DebugLogFileSize=0;
11942       }
11943       else DebugLogFileSize=val;
11944     } else if (!strcasecmp(line, "AllowResharingRemoteCUPSPrinters") && value) {
11945       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11946 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11947 	AllowResharingRemoteCUPSPrinters = 1;
11948       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11949 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11950 	AllowResharingRemoteCUPSPrinters = 0;
11951     } else if (!strcasecmp(line, "NewBrowsePollQueuesShared") && value) {
11952       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11953 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11954 	NewBrowsePollQueuesShared = 1;
11955       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11956 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11957 	NewBrowsePollQueuesShared = 0;
11958     } else if (!strcasecmp(line, "KeepGeneratedQueuesOnShutdown") && value) {
11959       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11960 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11961 	KeepGeneratedQueuesOnShutdown = 1;
11962       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11963 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11964 	KeepGeneratedQueuesOnShutdown = 0;
11965     } else if (!strcasecmp(line, "AutoClustering") && value) {
11966       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11967 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11968 	AutoClustering = 1;
11969       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11970 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11971 	AutoClustering = 0;
11972     } else if (!strcasecmp(line, "Cluster") && value) {
11973       ptr = value;
11974       ptr2 = NULL;
11975       /* Skip white space */
11976       while (*ptr && isspace(*ptr)) ptr ++;
11977       /* Premature line end */
11978       if (!*ptr) goto cluster_fail;
11979       /* Find the local queue name for the cluster */
11980       start = ptr;
11981       while (*ptr && !isspace(*ptr) && *ptr != ':') ptr ++;
11982       if (*ptr) {
11983 	/* Mark end of the local queue name */
11984 	*ptr = '\0';
11985 	/* Skip colon and white space until next word or line end */
11986 	ptr ++;
11987 	while (*ptr && (isspace(*ptr) || *ptr == ':')) ptr ++;
11988       }
11989       /* Empty queue name */
11990       if (strlen(start) <= 0)
11991 	goto cluster_fail;
11992       /* Clean queue name */
11993       ptr2 = remove_bad_chars(start, 0);
11994       /* Check whether we have already a cluster with this name */
11995       for (cluster = cupsArrayFirst(clusters);
11996 	   cluster;
11997 	   cluster = cupsArrayNext(clusters))
11998 	if (!strcasecmp(ptr2, cluster->local_queue_name)) {
11999 	  debug_printf("Duplicate cluster with queue name \"%s\".\n",
12000 		       ptr2);
12001 	  cluster = NULL;
12002 	  goto cluster_fail;
12003 	}
12004       /* Create the new cluster definition */
12005       cluster = calloc (1, sizeof (cluster_t));
12006       if (!cluster) goto cluster_fail;
12007       cluster->local_queue_name = ptr2;
12008       cluster->members = cupsArrayNew(NULL, NULL);
12009       ptr2 = NULL;
12010       if (!*ptr) {
12011 	/* Only local queue name given, so assume this name as the only
12012 	   member name (only remote queues with this name match) */
12013 	cupsArrayAdd(cluster->members, remove_bad_chars(ptr2, 2));
12014       } else {
12015 	/* The rest of the line lists one or more member queue names */
12016 	while (*ptr) {
12017 	  start = ptr;
12018 	  while (*ptr && !isspace(*ptr)) ptr ++;
12019 	  if (*ptr) {
12020 	    /* Mark end of the current word */
12021 	    *ptr = '\0';
12022 	    /* Skip white space until next word or line end */
12023 	    ptr ++;
12024 	    while (*ptr && isspace(*ptr)) ptr ++;
12025 	  }
12026 	  /* Add member queue name to the list */
12027 	  if (strlen(start) > 0)
12028 	    cupsArrayAdd(cluster->members, remove_bad_chars(start, 2));
12029 	}
12030       }
12031       cupsArrayAdd (clusters, cluster);
12032       if (ptr2 != NULL) {
12033         free(ptr2);
12034         ptr2 = NULL;
12035       }
12036       continue;
12037     cluster_fail:
12038       if (cluster) {
12039 	if (cluster->local_queue_name)
12040 	  free(cluster->local_queue_name);
12041 	if (cluster->members) {
12042 	  while ((ptr = cupsArrayFirst (cluster->members)) != NULL) {
12043 	    cupsArrayRemove (cluster->members, ptr);
12044 	    free (ptr);
12045 	  }
12046 	  cupsArrayDelete (cluster->members);
12047 	}
12048 	free(cluster);
12049         cluster = NULL;
12050       }
12051       if (ptr2 != NULL) {
12052         free(ptr2);
12053         ptr2 = NULL;
12054       }
12055     } else if (!strcasecmp(line, "LoadBalancing") && value) {
12056       if (!strncasecmp(value, "QueueOnClient", 13))
12057 	LoadBalancingType = QUEUE_ON_CLIENT;
12058       else if (!strncasecmp(value, "QueueOnServers", 14))
12059 	LoadBalancingType = QUEUE_ON_SERVERS;
12060     } else if (!strcasecmp(line, "DefaultOptions") && value) {
12061       if (DefaultOptions == NULL && strlen(value) > 0)
12062 	DefaultOptions = strdup(value);
12063     } else if (!strcasecmp(line, "AutoShutdown") && value) {
12064       char *p, *saveptr;
12065       p = strtok_r (value, delim, &saveptr);
12066       while (p) {
12067 	if (!strcasecmp(p, "On") || !strcasecmp(p, "Yes") ||
12068 	    !strcasecmp(p, "True") || !strcasecmp(p, "1")) {
12069 	  autoshutdown = 1;
12070 	  debug_printf("Turning on auto shutdown mode.\n");
12071 	} else if (!strcasecmp(p, "Off") || !strcasecmp(p, "No") ||
12072 		   !strcasecmp(p, "False") || !strcasecmp(p, "0")) {
12073 	  autoshutdown = 0;
12074 	  debug_printf("Turning off auto shutdown mode (permanent mode).\n");
12075 	} else if (!strcasecmp(p, "avahi")) {
12076 	  autoshutdown_avahi = 1;
12077 	  debug_printf("Turning on auto shutdown control by appearing and disappearing of the Avahi server.\n");
12078 	} else if (strcasecmp(p, "none"))
12079 	  debug_printf("Unknown mode '%s'\n", p);
12080 	p = strtok_r (NULL, delim, &saveptr);
12081       }
12082     } else if (!strcasecmp(line, "AutoShutdownTimeout") && value) {
12083       int t = atoi(value);
12084       if (t >= 0) {
12085 	autoshutdown_timeout = t;
12086 	debug_printf("Set auto shutdown timeout to %d sec.\n",
12087 		     t);
12088       } else
12089 	debug_printf("Invalid auto shutdown timeout value: %d\n",
12090 		     t);
12091     } else if (!strcasecmp(line, "AutoShutdownOn") && value) {
12092       int success = 0;
12093       if (!strncasecmp(value, "no", 2)) {
12094 	if (strcasestr(value + 2, "queue")) {
12095 	  autoshutdown_on = NO_QUEUES;
12096 	  success = 1;
12097 	} else if (strcasestr(value + 2, "job")) {
12098 	  autoshutdown_on = NO_JOBS;
12099 	  success = 1;
12100 	}
12101       }
12102       if (success)
12103 	debug_printf("Set auto shutdown inactivity type to no %s.\n",
12104 		     autoshutdown_on == NO_QUEUES ? "queues" : "jobs");
12105       else
12106 	debug_printf("Invalid auto shutdown inactivity type value: %s\n",
12107 		     value);
12108     } else if (!strcasecmp(line, "UpdateCUPSQueuesMaxPerCall") && value) {
12109       int n = atoi(value);
12110       if (n >= 0) {
12111 	update_cups_queues_max_per_call = n;
12112 	if (n > 0)
12113 	  debug_printf("Set maximum of CUPS queue updates per call of update_cups_queues() to %d.\n",
12114 		       n);
12115 	else
12116 	  debug_printf("Do not limit the number of CUPS queue updates per call of update_cups_queues().\n");
12117       } else
12118 	debug_printf("Invalid value for maximum number of CUPS queue updates per call of update_cups_queues(): %d\n",
12119 		     n);
12120     } else if (!strcasecmp(line, "PauseBetweenCUPSQueueUpdates") && value) {
12121       int t = atoi(value);
12122       if (t >= 0) {
12123 	pause_between_cups_queue_updates = t;
12124 	debug_printf("Set pause between calls of update_cups_queues() to %d sec.\n",
12125 		     t);
12126       } else
12127 	debug_printf("Invalid value for pause between calls of update_cups_queues(): %d\n",
12128 		     t);
12129     }
12130 #ifdef HAVE_LDAP
12131     else if (!strcasecmp(line, "BrowseLDAPBindDN") && value) {
12132       if (value[0] != '\0')
12133 	BrowseLDAPBindDN = strdup(value);
12134     }
12135 #  ifdef HAVE_LDAP_SSL
12136     else if (!strcasecmp(line, "BrowseLDAPCACertFile") && value) {
12137       if (value[0] != '\0')
12138 	BrowseLDAPCACertFile = strdup(value);
12139     }
12140 #  endif /* HAVE_LDAP_SSL */
12141     else if (!strcasecmp(line, "BrowseLDAPDN") && value) {
12142       if (value[0] != '\0')
12143 	BrowseLDAPDN = strdup(value);
12144     } else if (!strcasecmp(line, "BrowseLDAPPassword") && value) {
12145       if (value[0] != '\0')
12146 	BrowseLDAPPassword = strdup(value);
12147     } else if (!strcasecmp(line, "BrowseLDAPServer") && value) {
12148       if (value[0] != '\0')
12149 	BrowseLDAPServer = strdup(value);
12150     } else if (!strcasecmp(line, "BrowseLDAPFilter") && value) {
12151       if (value[0] != '\0')
12152 	BrowseLDAPFilter = strdup(value);
12153     }
12154 #endif /* HAVE_LDAP */
12155   }
12156 
12157   if (browse_line_found == 0) {
12158     /* No "Browse..." lines at all */
12159     browseallow_all = 1;
12160     browse_order = ORDER_DENY_ALLOW;
12161     debug_printf("No \"Browse...\" line at all, accept all servers (\"BrowseOrder Deny,Allow\").\n");
12162   } else if (browse_order_line_found == 0) {
12163     /* No "BrowseOrder" line */
12164     if (browse_allow_line_found == 0) {
12165       /* Only "BrowseDeny" lines */
12166       browse_order = ORDER_DENY_ALLOW;
12167       debug_printf("No \"BrowseOrder\" line and only \"BrowseDeny\" lines, accept all except what matches the \"BrowseDeny\" lines  (\"BrowseOrder Deny,Allow\").\n");
12168     } else if (browse_deny_line_found == 0) {
12169       /* Only "BrowseAllow" lines */
12170       browse_order = ORDER_ALLOW_DENY;
12171       debug_printf("No \"BrowseOrder\" line and only \"BrowseAllow\" lines, deny all except what matches the \"BrowseAllow\" lines  (\"BrowseOrder Allow,Deny\").\n");
12172     } else {
12173       /* Default for "BrowseOrder" */
12174       browse_order = ORDER_DENY_ALLOW;
12175       debug_printf("No \"BrowseOrder\" line, use \"BrowseOrder Deny,Allow\" as default.\n");
12176     }
12177   }
12178 
12179   cupsFileClose(fp);
12180 }
12181 
12182 static void
defer_update_netifs(void)12183 defer_update_netifs (void)
12184 {
12185   if (update_netifs_sourceid)
12186     g_source_remove (update_netifs_sourceid);
12187 
12188   update_netifs_sourceid = g_timeout_add_seconds (10, update_netifs, NULL);
12189 }
12190 
12191 static void
nm_properties_changed(GDBusProxy * proxy,GVariant * changed_properties,const gchar * const * invalidated_properties,gpointer user_data)12192 nm_properties_changed (GDBusProxy *proxy,
12193 		       GVariant *changed_properties,
12194 		       const gchar *const *invalidated_properties,
12195 		       gpointer user_data)
12196 {
12197   GVariantIter *iter;
12198   const gchar *key;
12199   GVariant *value;
12200   debug_printf("nm_properties_changed() in THREAD %ld\n", pthread_self());
12201   g_variant_get (changed_properties, "a{sv}", &iter);
12202   while (g_variant_iter_loop (iter, "{&sv}", &key, &value)) {
12203     if (!strcmp (key, "ActiveConnections")) {
12204       debug_printf ("NetworkManager ActiveConnections changed\n");
12205       defer_update_netifs ();
12206       break;
12207     }
12208   }
12209 
12210   g_variant_iter_free (iter);
12211 }
12212 
12213 static void
find_previous_queue(gpointer key,gpointer value,gpointer user_data)12214 find_previous_queue (gpointer key,
12215 		     gpointer value,
12216 		     gpointer user_data)
12217 {
12218   const char *name = key;
12219   const local_printer_t *printer = value;
12220   remote_printer_t *p;
12221   debug_printf("find_previous_queue() in THREAD %ld\n", pthread_self());
12222   if (printer->cups_browsed_controlled) {
12223     /* Queue found, add to our list */
12224     p = create_remote_printer_entry (name, "", "", "", "", "",
12225 				     0, "", "", "", "", "", 0, NULL, 0, 0, NULL,
12226 				     -1);
12227     if (p) {
12228       /* Mark as unconfirmed, if no Avahi report of this queue appears
12229 	 in a certain time frame, we will remove the queue */
12230       p->status = STATUS_UNCONFIRMED;
12231 
12232       if (BrowseRemoteProtocols & BROWSE_CUPS)
12233 	p->timeout = time(NULL) + BrowseInterval * 3 / 2;
12234       else
12235 	p->timeout = time(NULL) + TIMEOUT_CONFIRM;
12236 
12237       p->slave_of = NULL;
12238       debug_printf("Found CUPS queue %s (URI: %s) from previous session.\n",
12239 		   p->queue_name, p->uri);
12240     } else
12241       debug_printf("ERROR: Unable to create print queue entry for printer of previous session: %s (%s).\n",
12242 		   name, printer->device_uri);
12243   }
12244 }
12245 
main(int argc,char * argv[])12246 int main(int argc, char*argv[]) {
12247   int ret = 1;
12248   http_t *http;
12249   int i;
12250   char *val;
12251   remote_printer_t *p;
12252   GDBusProxy *proxy = NULL;
12253   GError *error = NULL;
12254   int subscription_id = 0;
12255 
12256   /* Initialise the command_line_config array */
12257   command_line_config = cupsArrayNew(NULL, NULL);
12258 
12259   /* Initialise the browseallow array */
12260   browseallow = cupsArrayNew(NULL, NULL);
12261 
12262   /* Initialise the browsefilter array */
12263   browsefilter = cupsArrayNew(NULL, NULL);
12264 
12265   /* Initialise the clusters array */
12266   clusters = cupsArrayNew(NULL, NULL);
12267 
12268   /* Read command line options */
12269   if (argc >= 2) {
12270     for (i = 1; i < argc; i++)
12271       if (!strcasecmp(argv[i], "--debug") || !strcasecmp(argv[i], "-d") ||
12272 	  !strncasecmp(argv[i], "-v", 2)) {
12273 	/* Turn on debug output mode if requested */
12274 	debug_stderr = 1;
12275 	debug_printf("Reading command line option %s, turning on debug mode (Log on standard error).\n",
12276 		     argv[i]);
12277       } else if (!strcasecmp(argv[i], "--logfile") ||
12278 		 !strcasecmp(argv[i], "-l")) {
12279 	/* Turn on debug log file mode if requested */
12280 	if (debug_logfile == 0) {
12281 	  debug_logfile = 1;
12282 	  start_debug_logging();
12283 	  debug_printf("Reading command line option %s, turning on debug mode (Log into log file %s).\n",
12284 		       argv[i], debug_log_file);
12285 	}
12286       } else if (!strncasecmp(argv[i], "-c", 2)) {
12287 	/* Alternative configuration file */
12288 	val = argv[i] + 2;
12289 	if (strlen(val) == 0) {
12290 	  i ++;
12291 	  if (i < argc && *argv[i] != '-')
12292 	    val = argv[i];
12293 	  else
12294 	    val = NULL;
12295 	}
12296 	if (val) {
12297 	  alt_config_file = strdup(val);
12298 	  debug_printf("Reading command line option -c %s, using alternative configuration file.\n",
12299 		       alt_config_file);
12300 	} else {
12301 	  fprintf(stderr,
12302 		  "Reading command line option -c, no alternative configuration file name supplied.\n\n");
12303 	  goto help;
12304 	}
12305       } else if (!strncasecmp(argv[i], "-o", 2)) {
12306 	/* Configuration option via command line */
12307 	val = argv[i] + 2;
12308 	if (strlen(val) == 0) {
12309 	  i ++;
12310 	  if (i < argc && *argv[i] != '-')
12311 	    val = argv[i];
12312 	  else
12313 	    val = NULL;
12314 	}
12315 	if (val) {
12316 	  cupsArrayAdd (command_line_config, strdup(val));
12317 	  debug_printf("Reading command line option -o %s, applying extra configuration option.\n",
12318 		       val);
12319 	} else {
12320 	  fprintf(stderr,
12321 		  "Reading command line option -o, no extra configuration option supplied.\n\n");
12322 	  goto help;
12323 	}
12324       } else if (!strncasecmp(argv[i], "--autoshutdown-timeout", 22)) {
12325 	debug_printf("Reading command line: %s\n", argv[i]);
12326 	if (argv[i][22] == '=' && argv[i][23])
12327 	  val = argv[i] + 23;
12328 	else if (!argv[i][22] && i < argc -1) {
12329 	  i++;
12330 	  debug_printf("Reading command line: %s\n", argv[i]);
12331 	  val = argv[i];
12332 	} else {
12333 	  fprintf(stderr, "Expected auto shutdown timeout setting after \"--autoshutdown-timeout\" option.\n\n");
12334 	  goto help;
12335 	}
12336 	int t = atoi(val);
12337 	if (t >= 0) {
12338 	  autoshutdown_timeout = t;
12339 	  debug_printf("Set auto shutdown timeout to %d sec.\n",
12340 		       t);
12341 	} else {
12342 	  fprintf(stderr, "Invalid auto shutdown timeout value: %d\n\n",
12343 		  t);
12344 	  goto help;
12345 	}
12346       } else if (!strncasecmp(argv[i], "--autoshutdown-on", 17)) {
12347 	debug_printf("Reading command line: %s\n", argv[i]);
12348 	if (argv[i][17] == '=' && argv[i][18])
12349 	  val = argv[i] + 18;
12350 	else if (!argv[i][17] && i < argc - 1) {
12351 	  i++;
12352 	  debug_printf("Reading command line: %s\n", argv[i]);
12353 	  val = argv[i];
12354 	} else {
12355 	  fprintf(stderr, "Expected auto shutdown inactivity type (\"no-queues\" or \"no-jobs\") after \"--autoshutdown-on\" option.\n\n");
12356 	  goto help;
12357 	}
12358 	int success = 0;
12359 	if (!strncasecmp(val, "no", 2)) {
12360 	  if (strcasestr(val + 2, "queue")) {
12361 	    autoshutdown_on = NO_QUEUES;
12362 	    success = 1;
12363 	  } else if (strcasestr(val + 2, "job")) {
12364 	    autoshutdown_on = NO_JOBS;
12365 	    success = 1;
12366 	  }
12367 	}
12368 	if (success)
12369 	  debug_printf("Set auto shutdown inactivity type to no %s.\n",
12370 		       autoshutdown_on == NO_QUEUES ? "queues" : "jobs");
12371 	else
12372 	  debug_printf("Invalid auto shutdown inactivity type value: %s\n",
12373 		       val);
12374       } else if (!strncasecmp(argv[i], "--autoshutdown", 14)) {
12375 	debug_printf("Reading command line: %s\n", argv[i]);
12376 	if (argv[i][14] == '=' && argv[i][15])
12377 	  val = argv[i] + 15;
12378 	else if (!argv[i][14] && i < argc -1) {
12379 	  i++;
12380 	  debug_printf("Reading command line: %s\n", argv[i]);
12381 	  val = argv[i];
12382 	} else {
12383 	  fprintf(stderr, "Expected auto shutdown setting after \"--autoshutdown\" option.\n\n");
12384 	  goto help;
12385 	}
12386 	if (!strcasecmp(val, "On") || !strcasecmp(val, "Yes") ||
12387 	    !strcasecmp(val, "True") || !strcasecmp(val, "1")) {
12388 	  autoshutdown = 1;
12389 	  debug_printf("Turning on auto shutdown mode.\n");
12390 	} else if (!strcasecmp(val, "Off") || !strcasecmp(val, "No") ||
12391 		   !strcasecmp(val, "False") || !strcasecmp(val, "0")) {
12392 	  autoshutdown = 0;
12393 	  debug_printf("Turning off auto shutdown mode (permanent mode).\n");
12394 	} else if (!strcasecmp(val, "avahi")) {
12395 	  autoshutdown_avahi = 1;
12396 	  debug_printf("Turning on auto shutdown control by appearing and disappearing of the Avahi server.\n");
12397 	} else if (strcasecmp(val, "none")) {
12398 	  fprintf(stderr, "Unknown mode '%s'\n\n", val);
12399 	  goto help;
12400 	}
12401       } else if (!strcasecmp(argv[i], "--version") ||
12402 		 !strcasecmp(argv[i], "--help") || !strcasecmp(argv[i], "-h")) {
12403 	/* Help!! */
12404 	goto help;
12405       } else {
12406 	/* Unknown option */
12407 	fprintf(stderr,
12408 		"Reading command line option %s, unknown command line option.\n\n",
12409 		argv[i]);
12410         goto help;
12411       }
12412   }
12413 
12414   debug_printf("cups-browsed of cups-filters version "VERSION" starting.\n");
12415 
12416   /* Read in cups-browsed.conf */
12417   read_configuration (alt_config_file);
12418 
12419   /* Set the paths of the auxiliary files */
12420   if (cachedir[0] == '\0')
12421     strncpy(cachedir, DEFAULT_CACHEDIR, sizeof(cachedir) - 1);
12422   if (logdir[0] == '\0')
12423     strncpy(logdir, DEFAULT_LOGDIR, sizeof(logdir) - 1);
12424   strncpy(local_default_printer_file, cachedir,
12425 	  sizeof(local_default_printer_file) - 1);
12426   strncpy(local_default_printer_file + strlen(cachedir),
12427 	  LOCAL_DEFAULT_PRINTER_FILE,
12428 	  sizeof(local_default_printer_file) - strlen(cachedir) - 1);
12429   strncpy(remote_default_printer_file, cachedir,
12430 	  sizeof(remote_default_printer_file) - 1);
12431   strncpy(remote_default_printer_file + strlen(cachedir),
12432 	  REMOTE_DEFAULT_PRINTER_FILE,
12433 	  sizeof(remote_default_printer_file) - strlen(cachedir) - 1);
12434   strncpy(save_options_file, cachedir,
12435 	  sizeof(save_options_file) - 1);
12436   strncpy(save_options_file + strlen(cachedir),
12437 	  SAVE_OPTIONS_FILE,
12438 	  sizeof(save_options_file) - strlen(cachedir) - 1);
12439   strncpy(debug_log_file, logdir,
12440 	  sizeof(debug_log_file) - 1);
12441   strncpy(debug_log_file + strlen(logdir),
12442 	  DEBUG_LOG_FILE,
12443 	  sizeof(debug_log_file) - strlen(logdir) - 1);
12444 
12445   strncpy(debug_log_file_bckp, logdir,
12446 	  sizeof(debug_log_file_bckp) - 1);
12447   strncpy(debug_log_file_bckp + strlen(logdir),
12448 	  DEBUG_LOG_FILE_2,
12449 	  sizeof(debug_log_file_bckp) - strlen(logdir) - 1);
12450 
12451   if (debug_logfile == 1)
12452     start_debug_logging();
12453 
12454   debug_printf("main() in THREAD %ld\n", pthread_self());
12455 
12456   /* If a port is selected via the IPP_PORT environment variable,
12457      set this first */
12458   if (getenv("IPP_PORT") != NULL) {
12459     snprintf(local_server_str, sizeof(local_server_str) - 1,
12460 	     "localhost:%s", getenv("IPP_PORT"));
12461     local_server_str[sizeof(local_server_str) - 1] = '\0';
12462     cupsSetServer(local_server_str);
12463     debug_printf("Set port on which CUPS is listening via env variable: IPP_PORT=%s\n",
12464 		 getenv("IPP_PORT"));
12465   }
12466 
12467   /* Point to selected CUPS server or domain socket via the CUPS_SERVER
12468      environment variable or DomainSocket configuration file option.
12469      Default to localhost:631 (and not to CUPS default to override
12470      client.conf files as cups-browsed works only with a local CUPS
12471      daemon, not with remote ones. */
12472   local_server_str[0] = '\0';
12473   if (getenv("CUPS_SERVER") != NULL) {
12474     strncpy(local_server_str, getenv("CUPS_SERVER"),
12475 	    sizeof(local_server_str) - 1);
12476     local_server_str[sizeof(local_server_str) - 1] = '\0';
12477     cupsSetServer(local_server_str);
12478     debug_printf("Set host/port/domain socket which CUPS is listening via env variable: CUPS_SERVER=%s\n",
12479 		 getenv("CUPS_SERVER"));
12480   } else {
12481     if (DomainSocket != NULL) {
12482       debug_printf("Set host/port/domain socket on which CUPS is listening via cups-browsed directive DomainSocket: %s\n",
12483 		   DomainSocket);
12484       struct stat sockinfo;               /* Domain socket information */
12485       if (strcasecmp(DomainSocket, "None") != 0 &&
12486 	  strcasecmp(DomainSocket, "Off") != 0 &&
12487 	  !stat(DomainSocket, &sockinfo) &&
12488 	  (sockinfo.st_mode & S_IROTH) != 0 &&
12489 	  (sockinfo.st_mode & S_IWOTH) != 0) {
12490 	strncpy(local_server_str, DomainSocket,
12491 		sizeof(local_server_str) - 1);
12492 	local_server_str[sizeof(local_server_str) - 1] = '\0';
12493 	cupsSetServer(local_server_str);
12494       } else
12495 	debug_printf("DomainSocket %s not accessible: %s\n",
12496 		     DomainSocket, strerror(errno));
12497     }
12498   }
12499   if (local_server_str[0])
12500     setenv("CUPS_SERVER", local_server_str, 1);
12501 
12502   if (BrowseLocalProtocols & BROWSE_DNSSD) {
12503     debug_printf("Local support for DNSSD not implemented\n");
12504     BrowseLocalProtocols &= ~BROWSE_DNSSD;
12505   }
12506 
12507   if (BrowseLocalProtocols & BROWSE_LDAP) {
12508     debug_printf("Local support for LDAP not implemented\n");
12509     BrowseLocalProtocols &= ~BROWSE_LDAP;
12510   }
12511 
12512 #ifndef HAVE_AVAHI
12513   if (BrowseRemoteProtocols & BROWSE_DNSSD) {
12514     debug_printf("Remote support for DNSSD not supported\n");
12515     BrowseRemoteProtocols &= ~BROWSE_DNSSD;
12516   }
12517 #endif /* HAVE_AVAHI */
12518 
12519 #ifndef HAVE_LDAP
12520   if (BrowseRemoteProtocols & BROWSE_LDAP) {
12521     debug_printf("Remote support for LDAP not supported\n");
12522     BrowseRemoteProtocols &= ~BROWSE_LDAP;
12523   }
12524 #endif /* HAVE_LDAP */
12525 
12526   /* Wait for CUPS daemon to start */
12527   while ((http = http_connect_local ()) == NULL)
12528     sleep(1);
12529 
12530   /* Initialise the array of network interfaces */
12531   netifs = cupsArrayNew(NULL, NULL);
12532   local_hostnames = cupsArrayNew(NULL, NULL);
12533   update_netifs (NULL);
12534 
12535   local_printers = g_hash_table_new_full (g_str_hash,
12536 					  g_str_equal,
12537 					  g_free,
12538 					  free_local_printer);
12539   cups_supported_remote_printers = g_hash_table_new_full (g_str_hash,
12540 							  g_str_equal,
12541 							  g_free,
12542 							  free_local_printer);
12543 
12544   /* Read out the currently defined CUPS queues and find the ones which we
12545      have added in an earlier session */
12546   update_local_printers ();
12547   if ((val = get_cups_default_printer()) != NULL) {
12548     default_printer = strdup(val);
12549     free(val);
12550   }
12551   remote_printers = cupsArrayNew(NULL, NULL);
12552   g_hash_table_foreach (local_printers, find_previous_queue, NULL);
12553 
12554   /* Redirect SIGINT and SIGTERM so that we do a proper shutdown, removing
12555      the CUPS queues which we have created
12556      Use SIGUSR1 and SIGUSR2 to turn off and turn on auto shutdown mode
12557      resp. */
12558 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
12559   sigset(SIGTERM, sigterm_handler);
12560   sigset(SIGINT, sigterm_handler);
12561   sigset(SIGUSR1, sigusr1_handler);
12562   sigset(SIGUSR2, sigusr2_handler);
12563   debug_printf("Using signal handler SIGSET\n");
12564 #elif defined(HAVE_SIGACTION)
12565   struct sigaction action; /* Actions for POSIX signals */
12566   memset(&action, 0, sizeof(action));
12567   sigemptyset(&action.sa_mask);
12568   sigaddset(&action.sa_mask, SIGTERM);
12569   action.sa_handler = sigterm_handler;
12570   sigaction(SIGTERM, &action, NULL);
12571   sigemptyset(&action.sa_mask);
12572   sigaddset(&action.sa_mask, SIGINT);
12573   action.sa_handler = sigterm_handler;
12574   sigaction(SIGINT, &action, NULL);
12575   sigemptyset(&action.sa_mask);
12576   sigaddset(&action.sa_mask, SIGUSR1);
12577   action.sa_handler = sigusr1_handler;
12578   sigaction(SIGUSR1, &action, NULL);
12579   sigemptyset(&action.sa_mask);
12580   sigaddset(&action.sa_mask, SIGUSR2);
12581   action.sa_handler = sigusr2_handler;
12582   sigaction(SIGUSR2, &action, NULL);
12583   debug_printf("Using signal handler SIGACTION\n");
12584 #else
12585   signal(SIGTERM, sigterm_handler);
12586   signal(SIGINT, sigterm_handler);
12587   signal(SIGUSR1, sigusr1_handler);
12588   signal(SIGUSR2, sigusr2_handler);
12589   debug_printf("Using signal handler SIGNAL\n");
12590 #endif /* HAVE_SIGSET */
12591 
12592 #ifdef HAVE_AVAHI
12593   if (autoshutdown_avahi)
12594     autoshutdown = 1;
12595   avahi_init();
12596 #endif /* HAVE_AVAHI */
12597 
12598   if (autoshutdown == 1) {
12599     /* If there are no printers or no jobs schedule the shutdown in
12600        autoshutdown_timeout seconds */
12601     if (!autoshutdown_exec_id &&
12602 	(cupsArrayCount(remote_printers) == 0 ||
12603 	 (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
12604       debug_printf ("We set auto shutdown mode and no printers are there to make available or no jobs on them, shutting down in %d sec...\n", autoshutdown_timeout);
12605       autoshutdown_exec_id =
12606 	g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
12607 			       NULL);
12608     }
12609   }
12610 
12611   if (BrowseLocalProtocols & BROWSE_CUPS ||
12612       BrowseRemoteProtocols & BROWSE_CUPS) {
12613     /* Set up our CUPS Browsing socket */
12614     browsesocket = socket (AF_INET, SOCK_DGRAM, 0);
12615     if (browsesocket == -1) {
12616       debug_printf("failed to create CUPS Browsing socket: %s\n",
12617 		   strerror (errno));
12618     } else {
12619       struct sockaddr_in addr;
12620       memset (&addr, 0, sizeof (addr));
12621       addr.sin_addr.s_addr = htonl (INADDR_ANY);
12622       addr.sin_family = AF_INET;
12623       addr.sin_port = htons (BrowsePort);
12624       if (bind (browsesocket, (struct sockaddr *)&addr, sizeof (addr))) {
12625 	debug_printf("failed to bind CUPS Browsing socket: %s\n",
12626 		     strerror (errno));
12627 	close (browsesocket);
12628 	browsesocket = -1;
12629       } else {
12630 	int on = 1;
12631 	if (setsockopt (browsesocket, SOL_SOCKET, SO_BROADCAST,
12632 			&on, sizeof (on))) {
12633 	  debug_printf("failed to allow broadcast: %s\n",
12634 		       strerror (errno));
12635 	  BrowseLocalProtocols &= ~BROWSE_CUPS;
12636 	}
12637       }
12638     }
12639 
12640     if (browsesocket == -1) {
12641       BrowseLocalProtocols &= ~BROWSE_CUPS;
12642       BrowseRemoteProtocols &= ~BROWSE_CUPS;
12643     }
12644   }
12645 
12646   if (BrowseLocalProtocols == 0 &&
12647       BrowseRemoteProtocols == 0 &&
12648       !BrowsePoll) {
12649     debug_printf("nothing left to do\n");
12650     ret = 0;
12651     goto fail;
12652   }
12653 
12654   /* Override the default password callback so we don't end up
12655    * prompting for it. */
12656   cupsSetPasswordCB2 (password_callback, NULL);
12657 
12658   /* Watch NetworkManager for network interface changes */
12659   proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
12660 					 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
12661 					 NULL, /* GDBusInterfaceInfo */
12662 					 "org.freedesktop.NetworkManager",
12663 					 "/org/freedesktop/NetworkManager",
12664 					 "org.freedesktop.NetworkManager",
12665 					 NULL, /* GCancellable */
12666 					 NULL); /* GError */
12667 
12668   if (proxy)
12669     g_signal_connect (proxy,
12670 		      "g-properties-changed",
12671 		      G_CALLBACK (nm_properties_changed),
12672 		      NULL);
12673 
12674   /* Run the main loop */
12675   gmainloop = g_main_loop_new (NULL, FALSE);
12676   recheck_timer ();
12677 
12678   if (BrowseRemoteProtocols & BROWSE_CUPS) {
12679     GIOChannel *browse_channel = g_io_channel_unix_new (browsesocket);
12680     g_io_channel_set_close_on_unref (browse_channel, FALSE);
12681     g_io_add_watch (browse_channel, G_IO_IN, process_browse_data, NULL);
12682   }
12683 
12684   if (BrowseLocalProtocols & BROWSE_CUPS) {
12685     debug_printf ("will send browse data every %ds\n",
12686 		  BrowseInterval);
12687     g_idle_add (send_browse_data, NULL);
12688   }
12689 
12690 #ifdef HAVE_LDAP
12691   if (BrowseRemoteProtocols & BROWSE_LDAP) {
12692     debug_printf ("will browse poll LDAP every %ds\n",
12693 		  BrowseInterval);
12694     g_idle_add (browse_ldap_poll, NULL);
12695   }
12696 #endif /* HAVE_LDAP */
12697 
12698   if (BrowsePoll) {
12699     size_t index;
12700     for (index = 0;
12701 	 index < NumBrowsePoll;
12702 	 index++) {
12703       debug_printf ("will browse poll %s every %ds\n",
12704 		    BrowsePoll[index]->server, BrowseInterval);
12705       g_idle_add (browse_poll, BrowsePoll[index]);
12706     }
12707   }
12708 
12709   /* Subscribe to CUPS' D-Bus notifications and create a proxy to receive
12710      the notifications */
12711   subscription_id = create_subscription ();
12712   g_timeout_add_seconds (notify_lease_duration / 2,
12713 			 renew_subscription_timeout,
12714 			 &subscription_id);
12715   cups_notifier = cups_notifier_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
12716 							0,
12717 							NULL,
12718 							CUPS_DBUS_PATH,
12719 							NULL,
12720 							&error);
12721   if (error) {
12722     fprintf (stderr, "Error creating cups notify handler: %s", error->message);
12723     g_error_free (error);
12724     cups_notifier = NULL;
12725   }
12726   if (cups_notifier != NULL) {
12727     g_signal_connect (cups_notifier, "printer-state-changed",
12728 		      G_CALLBACK (on_printer_state_changed), NULL);
12729     g_signal_connect (cups_notifier, "job-state",
12730 		      G_CALLBACK (on_job_state), NULL);
12731     g_signal_connect (cups_notifier, "printer-deleted",
12732 		      G_CALLBACK (on_printer_deleted), NULL);
12733     g_signal_connect (cups_notifier, "printer-modified",
12734 		      G_CALLBACK (on_printer_modified), NULL);
12735   }
12736 
12737   /* If auto shutdown is active and we do not find any printers initially,
12738      schedule the shutdown in autoshutdown_timeout seconds */
12739   if (autoshutdown && !autoshutdown_exec_id &&
12740       cupsArrayCount(remote_printers) == 0) {
12741     debug_printf ("No printers found to make available, shutting down in %d sec...\n",
12742 		  autoshutdown_timeout);
12743     autoshutdown_exec_id =
12744       g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute, NULL);
12745   }
12746 
12747   g_main_loop_run (gmainloop);
12748 
12749   debug_printf("main loop exited\n");
12750   g_main_loop_unref (gmainloop);
12751   gmainloop = NULL;
12752   ret = 0;
12753 
12754 fail:
12755 
12756   /* Clean up things */
12757 
12758   in_shutdown = 1;
12759 
12760   if (proxy)
12761     g_object_unref (proxy);
12762 
12763   /* Remove all queues which we have set up */
12764   if (KeepGeneratedQueuesOnShutdown == 0)
12765     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
12766 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
12767       if (p->status != STATUS_TO_BE_RELEASED)
12768 	p->status = STATUS_DISAPPEARED;
12769       p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
12770     }
12771   update_cups_queues(NULL);
12772 
12773   cancel_subscription (subscription_id);
12774   if (cups_notifier)
12775     g_object_unref (cups_notifier);
12776 
12777   if (BrowsePoll) {
12778     size_t index;
12779     for (index = 0;
12780 	 index < NumBrowsePoll;
12781 	 index++) {
12782       if (BrowsePoll[index]->can_subscribe &&
12783 	  BrowsePoll[index]->subscription_id != -1)
12784 	browse_poll_cancel_subscription (BrowsePoll[index]);
12785 
12786       free (BrowsePoll[index]->server);
12787       g_list_free_full (BrowsePoll[index]->printers,
12788 			browsepoll_printer_free);
12789       free (BrowsePoll[index]);
12790     }
12791 
12792     free (BrowsePoll);
12793   }
12794 
12795   if (local_printers_context) {
12796     browse_poll_cancel_subscription (local_printers_context);
12797 #ifdef HAVE_CUPS_2_0
12798     free(local_printers_context->server);
12799 #endif
12800     g_list_free_full (local_printers_context->printers,
12801 		      browsepoll_printer_free);
12802     free (local_printers_context);
12803   }
12804 
12805   http_close_local ();
12806 
12807 #ifdef HAVE_AVAHI
12808   avahi_shutdown();
12809 #endif /* HAVE_AVAHI */
12810 
12811 #ifdef HAVE_LDAP
12812   if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP) &&
12813       BrowseLDAPHandle) {
12814     ldap_disconnect(BrowseLDAPHandle);
12815     BrowseLDAPHandle = NULL;
12816   }
12817 #endif /* HAVE_LDAP */
12818 
12819   if (browsesocket != -1)
12820     close (browsesocket);
12821 
12822   g_hash_table_destroy (local_printers);
12823   g_hash_table_destroy (cups_supported_remote_printers);
12824 
12825   if (BrowseLocalProtocols & BROWSE_CUPS)
12826     g_list_free_full (browse_data, browse_data_free);
12827 
12828   /* Close log file if we have one */
12829   if (debug_logfile == 1)
12830     stop_debug_logging();
12831 
12832   if (deleted_master != NULL)
12833     free(deleted_master);
12834   if (DefaultOptions != NULL)
12835     free(DefaultOptions);
12836   if (DomainSocket != NULL)
12837     free(DomainSocket);
12838 
12839   return ret;
12840 
12841  help:
12842 
12843   fprintf(stderr,
12844 	  "cups-browsed of cups-filters version "VERSION"\n\n"
12845 	  "Usage: cups-browsed [options]\n"
12846 	  "Options:\n"
12847 	  "  -c cups-browsed.conf    Set alternative cups-browsed.conf file to use.\n"
12848 	  "  -d\n"
12849 	  "  -v\n"
12850 	  "  --debug                 Run in debug mode (logging to stderr).\n"
12851 	  "  -l\n"
12852 	  "  --logfile               Run in debug mode (logging into file).\n"
12853 	  "  -h\n"
12854 	  "  --help\n"
12855 	  "  --version               Show this usage message.\n"
12856 	  "  -o Option=Value         Supply configuration option via command line,\n"
12857 	  "                          options are the same as in cups-browsed.conf.\n"
12858 	  "  --autoshutdown=<mode>   Automatically shut down cups-browsed when inactive:\n"
12859 	  "                          <mode> can be set to Off, On, or avahi, where Off\n"
12860 	  "                          means that cups-browsed stays running permanently\n"
12861 	  "                          (default), On means that it shuts down after 30\n"
12862 	  "                          seconds (or any given timeout) of inactivity, and\n"
12863 	  "                          avahi means that cups-browsed shuts down when\n"
12864 	  "                          avahi-daemon shuts down.\n"
12865 	  "  --autoshutdown-timout=<time> Timeout (in seconds) for auto-shutdown.\n"
12866 	  "  --autoshutdown-on=<type> Type of inactivity which leads to an auto-\n"
12867 	  "                          shutdown: If <type> is \"no-queues\", the shutdown\n"
12868 	  "                          is triggered by not having any cups-browsed-created\n"
12869 	  "                          print queue any more. With <type> being \"no-jobs\"\n"
12870 	  "                          shutdown is initiated by no job being printed\n"
12871 	  "                          on any cups-browsed-generated print queue any more.\n"
12872 	  "                          \"no-queues\" is the default.\n"
12873 	  );
12874 
12875   return 1;
12876 }
12877