• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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__)
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 <ifaddrs.h>
34 #include <resolv.h>
35 #include <stdio.h>
36 #include <sys/stat.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   /* Cut off trailing separators */
3624   while (strlen(str) > 0 && str[strlen(str) - 1] == sep)
3625     str[strlen(str) - 1] = '\0';
3626 
3627   /* Cut off leading separators */
3628   i = 0;
3629   while (str[i] == sep)
3630     i ++;
3631 
3632   /* Keep a free()-able string. +1 for trailing \0 */
3633   return memmove(str, str + i, strlen(str) - i + 1);
3634 }
3635 
3636 
3637 static local_printer_t *
new_local_printer(const char * device_uri,const char * uuid,gboolean cups_browsed_controlled)3638 new_local_printer (const char *device_uri,
3639 		   const char *uuid,
3640 		   gboolean cups_browsed_controlled)
3641 {
3642   local_printer_t *printer = g_malloc (sizeof (local_printer_t));
3643   printer->device_uri = strdup (device_uri);
3644   printer->uuid = (char*)uuid;
3645   printer->cups_browsed_controlled = cups_browsed_controlled;
3646   return printer;
3647 }
3648 
3649 static void
free_local_printer(gpointer data)3650 free_local_printer (gpointer data)
3651 {
3652   local_printer_t *printer = data;
3653   debug_printf("free_local_printer() in THREAD %ld\n", pthread_self());
3654   free (printer->device_uri);
3655   if (printer->uuid) free (printer->uuid);
3656   free (printer);
3657 }
3658 
3659 static gboolean
local_printer_is_same_device(gpointer key,gpointer value,gpointer user_data)3660 local_printer_is_same_device (gpointer key,
3661 			      gpointer value,
3662 			      gpointer user_data)
3663 {
3664   local_printer_t *lprinter = value;
3665   remote_printer_t *p = user_data;
3666   char    lhost[HTTP_MAX_URI],     /* Local printer: Hostname */
3667           lresource[HTTP_MAX_URI], /* Local printer: Resource path */
3668           lscheme[32],             /* Local printer: URI's scheme */
3669           lusername[64],           /* Local printer: URI's username */
3670           *ltype = NULL,           /* Local printer: If URI DNS-SD-based */
3671           *ldomain = NULL;         /* pointers into lhost for components*/
3672   int     lport = 0;               /* Local printer: URI's port number */
3673 
3674   debug_printf("local_printer_is_same_device() in THREAD %ld\n",
3675 	       pthread_self());
3676   if (!lprinter || !lprinter->device_uri || !p)
3677     return (0);
3678   /* Separate the local printer's URI into their components */
3679   memset(lscheme, 0, sizeof(lscheme));
3680   memset(lusername, 0, sizeof(lusername));
3681   memset(lhost, 0, sizeof(lhost));
3682   memset(lresource, 0, sizeof(lresource));
3683   httpSeparateURI(HTTP_URI_CODING_ALL, lprinter->device_uri,
3684 		  lscheme, sizeof(lscheme) - 1,
3685 		  lusername, sizeof(lusername) - 1,
3686 		  lhost, sizeof(lhost) - 1,
3687 		  &lport,
3688 		  lresource, sizeof(lresource) - 1);
3689   if ((ltype = strstr(lhost, "._ipp._tcp.")) != NULL ||
3690       (ltype = strstr(lhost, "._ipps._tcp.")) != NULL) {
3691     *ltype = '\0';
3692     ltype ++;
3693     ldomain = strchr(ltype + 9, '.');
3694     *ldomain = '\0';
3695     ldomain ++;
3696     if (*ldomain && ldomain[strlen(ldomain) - 1] == '.')
3697       ldomain[strlen(ldomain) - 1] = '\0';
3698   }
3699   /* Consider not only absolutely equal URIs as equal
3700      but alo URIs which differ only by use of IPP or
3701      IPPS and/or have the IPP standard port 631
3702      replaced by the HTTPS standard port 443, as this
3703      is common on network printers */
3704   return ((ltype && p->service_name && p->domain &&
3705 	   g_str_equal(lhost, p->service_name) &&
3706 	   !strncmp(ldomain, p->domain, strlen(ldomain))) ||
3707 	  (!ltype && p->host && p->resource &&
3708 	   (g_str_equal(lscheme, "ipp") || g_str_equal(lscheme, "ipps")) &&
3709 	   !lusername[0] &&
3710 	   g_str_equal(lhost, p->host) &&
3711 	   ((!p->port && (lport == 631 || lport == 443)) ||
3712 	    lport == p->port ||
3713 	    (lport == 631 && p->port == 443) ||
3714 	    (lport == 443 && p->port == 631)) &&
3715 	   g_str_equal(lresource, p->resource)));
3716 }
3717 
3718 static gboolean
local_printer_has_uuid(gpointer key,gpointer value,gpointer user_data)3719 local_printer_has_uuid (gpointer key,
3720 			gpointer value,
3721 			gpointer user_data)
3722 {
3723   local_printer_t *printer = value;
3724   char    *uuid = user_data;
3725 
3726   debug_printf("local_printer_has_uuid() in THREAD %ld\n", pthread_self());
3727   return (printer != NULL && printer->uuid != NULL && uuid != NULL &&
3728 	  g_str_equal(printer->uuid, uuid));
3729 }
3730 
3731 static gboolean
local_printer_service_name_matches(gpointer key,gpointer value,gpointer user_data)3732 local_printer_service_name_matches (gpointer key,
3733 				    gpointer value,
3734 				    gpointer user_data)
3735 {
3736   char *queue_name = key;
3737   char *service_name = user_data;
3738   char *p;
3739   debug_printf("local_printer_service_name_matches() in THREAD %ld\n",
3740 	       pthread_self());
3741   p = remove_bad_chars(service_name, 2);
3742   if (p && strncasecmp(p, queue_name, 63) == 0) {
3743     free(p);
3744     return TRUE;
3745   }
3746   if (p) free(p);
3747   return FALSE;
3748 }
3749 
3750 static void
local_printers_create_subscription(http_t * conn)3751 local_printers_create_subscription (http_t *conn)
3752 {
3753   char temp[1024];
3754   if (!local_printers_context) {
3755     local_printers_context = g_malloc0 (sizeof (browsepoll_t));
3756     /* The httpGetAddr() function was introduced in CUPS 2.0.0 */
3757 #ifdef HAVE_CUPS_2_0
3758     local_printers_context->server =
3759       strdup(httpAddrString(httpGetAddress(conn),
3760 			    temp, sizeof(temp)));
3761     local_printers_context->port = httpAddrPort(httpGetAddress(conn));
3762 #else
3763     local_printers_context->server = cupsServer();
3764     local_printers_context->port = ippPort();
3765 #endif
3766     local_printers_context->can_subscribe = TRUE;
3767   }
3768 
3769   browse_poll_create_subscription (local_printers_context, conn);
3770 }
3771 
3772 int
add_dest_cb(dest_list_t * user_data,unsigned flags,cups_dest_t * dest)3773 add_dest_cb(dest_list_t *user_data, unsigned flags, cups_dest_t *dest)
3774 {
3775   if (flags & CUPS_DEST_FLAGS_REMOVED)
3776     /* Remove destination from array */
3777     user_data->num_dests =
3778       cupsRemoveDest(dest->name, dest->instance, user_data->num_dests,
3779 		     &(user_data->dests));
3780   else
3781     /* Add destination to array... */
3782     user_data->num_dests =
3783       cupsCopyDest(dest, user_data->num_dests,
3784 		   &(user_data->dests));
3785   return (1);
3786 }
3787 
3788 
3789 const char *
get_printer_uuid(http_t * http_printer,const char * raw_uri)3790 get_printer_uuid(http_t *http_printer,
3791 		 const char* raw_uri)
3792 {
3793   ipp_t *response = NULL;
3794   ipp_attribute_t *attr = NULL;
3795   const char * uuid = NULL;
3796 
3797   const char * const pattrs[] = {
3798     "printer-uuid",
3799   };
3800   const char * const req_attrs[] = {
3801     "printer-uuid",
3802   };
3803 
3804   if (http_printer == NULL)
3805     debug_printf ("HTTP connection for printer with URI %s not set!\n",
3806 		  raw_uri);
3807 
3808   if ((response =
3809        get_printer_attributes2(http_printer, raw_uri,
3810 			       pattrs, 1, req_attrs, 1, 0)) == NULL) {
3811     debug_printf ("Printer with URI %s has no \"printer-uuid\" IPP attribute!\n",
3812 		  raw_uri);
3813     return NULL;
3814   }
3815 
3816   attr = ippFindAttribute(response, "printer-uuid", IPP_TAG_URI);
3817 
3818 
3819   if (attr)
3820     uuid = strdup(ippGetString(attr, 0, NULL) + 9);
3821   else {
3822     debug_printf ("Printer with URI %s: Cannot read \"printer-uuid\" IPP attribute!\n",
3823 		  raw_uri);
3824   }
3825 
3826   ippDelete(response);
3827 
3828   return uuid;
3829 }
3830 
3831 static void
get_local_printers(void)3832 get_local_printers (void)
3833 {
3834   dest_list_t dest_list = {0, NULL};
3835   http_t *conn = NULL;
3836 
3837   conn = http_connect_local ();
3838 
3839   /* We only want to have a list of actually existing CUPS queues, not of
3840      DNS-SD-discovered printers for which CUPS can auto-setup a driverless
3841      print queue */
3842   if (OnlyUnsupportedByCUPS)
3843     cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, 0, 0,
3844 		  (cups_dest_cb_t)add_dest_cb, &dest_list);
3845   else
3846     cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, CUPS_PRINTER_LOCAL,
3847 		  CUPS_PRINTER_DISCOVERED, (cups_dest_cb_t)add_dest_cb,
3848 		  &dest_list);
3849   debug_printf ("cups-browsed (%s): cupsEnumDests\n", local_server_str);
3850   g_hash_table_remove_all (local_printers);
3851   if (OnlyUnsupportedByCUPS)
3852     g_hash_table_remove_all (cups_supported_remote_printers);
3853   int num_dests = dest_list.num_dests;
3854   cups_dest_t *dests = dest_list.dests;
3855   for (int i = 0; i < num_dests; i++) {
3856     const char *val;
3857     cups_dest_t *dest = &dests[i];
3858     local_printer_t *printer;
3859     gboolean cups_browsed_controlled;
3860     gboolean is_temporary;
3861     gboolean is_cups_supported_remote;
3862     char uri[HTTP_MAX_URI];
3863 
3864     const char *device_uri = cupsGetOption ("device-uri",
3865 					    dest->num_options,
3866 					    dest->options);
3867     if (device_uri == NULL)
3868       device_uri = "";
3869 
3870     /* Temporary CUPS queue? */
3871     val = cupsGetOption ("printer-is-temporary",
3872 			 dest->num_options,
3873 			 dest->options);
3874     is_temporary = (val && (!strcasecmp (val, "yes") ||
3875 			    !strcasecmp (val, "on") ||
3876 			    !strcasecmp (val, "true")));
3877 
3878     if (OnlyUnsupportedByCUPS) {
3879       /* Printer discovered by DNS-SD and supported by CUPS' temporary
3880 	 queues? */
3881       val = cupsGetOption ("printer-uri-supported",
3882 			   dest->num_options,
3883 			   dest->options);
3884       /* Printer has no local CUPS queue but CUPS would create a
3885 	 temporary queue on-demand */
3886       is_cups_supported_remote = (val == NULL || is_temporary);
3887     } else {
3888       is_cups_supported_remote = 0;
3889       if (is_temporary)
3890 	continue;
3891     }
3892 
3893     val = cupsGetOption (CUPS_BROWSED_MARK,
3894 			 dest->num_options,
3895 			 dest->options);
3896     cups_browsed_controlled = val && (!strcasecmp (val, "yes") ||
3897 				      !strcasecmp (val, "on") ||
3898 				      !strcasecmp (val, "true"));
3899     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3900 		     "localhost", 0, "/printers/%s", dest->name);
3901     printer = new_local_printer (device_uri, get_printer_uuid(conn, uri),
3902 				 cups_browsed_controlled);
3903     debug_printf ("Printer %s: %s, %s%s%s\n",
3904 		  dest->name, device_uri, printer->uuid,
3905 		  cups_browsed_controlled ? ", cups_browsed" : "",
3906 		  is_cups_supported_remote ? ", temporary" : "");
3907 
3908     if (is_cups_supported_remote)
3909       g_hash_table_insert (cups_supported_remote_printers,
3910 			   g_ascii_strdown (dest->name, -1),
3911 			   printer);
3912     else
3913       g_hash_table_insert (local_printers,
3914 			   g_ascii_strdown (dest->name, -1),
3915 			   printer);
3916   }
3917 
3918   cupsFreeDests (num_dests, dests);
3919 }
3920 
3921 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)3922 new_browse_data (int type, int state, const gchar *uri,
3923 		 const gchar *location, const gchar *info,
3924 		 const gchar *make_model, const gchar *browse_options)
3925 {
3926   browse_data_t *data = g_malloc (sizeof (browse_data_t));
3927   data->type = type;
3928   data->state = state;
3929   data->uri = g_strdup (uri);
3930   data->location = g_strdup (location);
3931   data->info = g_strdup (info);
3932   data->make_model = g_strdup (make_model);
3933   data->browse_options = g_strdup (browse_options);
3934   return data;
3935 }
3936 
3937 static void
browse_data_free(gpointer data)3938 browse_data_free (gpointer data)
3939 {
3940   browse_data_t *bdata = data;
3941   debug_printf("browse_data_free() in THREAD %ld\n", pthread_self());
3942   g_free (bdata->uri);
3943   g_free (bdata->location);
3944   g_free (bdata->info);
3945   g_free (bdata->make_model);
3946   g_free (bdata->browse_options);
3947   g_free (bdata);
3948 }
3949 
3950 static void
prepare_browse_data(void)3951 prepare_browse_data (void)
3952 {
3953   static const char * const rattrs[] = { "printer-type",
3954 					 "printer-state",
3955 					 "printer-uri-supported",
3956 					 "printer-info",
3957 					 "printer-location",
3958 					 "printer-make-and-model",
3959 					 "auth-info-required",
3960 					 "printer-uuid",
3961 					 "job-template" };
3962   ipp_t *request, *response = NULL;
3963   ipp_attribute_t *attr;
3964   http_t *conn = NULL;
3965 
3966   conn = http_connect_local ();
3967 
3968   if (conn == NULL) {
3969     debug_printf("Browse send failed to connect to localhost\n");
3970     goto fail;
3971   }
3972 
3973   request = ippNewRequest(CUPS_GET_PRINTERS);
3974   ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3975 		 "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]),
3976 		 NULL, rattrs);
3977   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
3978 		"requesting-user-name", NULL, cupsUser ());
3979 
3980   debug_printf("preparing browse data\n");
3981   response = cupsDoRequest (conn, request, "/");
3982   if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
3983     debug_printf("browse send failed for localhost: %s\n",
3984 		 cupsLastErrorString ());
3985     goto fail;
3986   }
3987 
3988   g_list_free_full (browse_data, browse_data_free);
3989   browse_data = NULL;
3990   for (attr = ippFirstAttribute(response); attr;
3991        attr = ippNextAttribute(response)) {
3992     int type = -1, state = -1;
3993     const char *uri = NULL;
3994     gchar *location = NULL;
3995     gchar *info = NULL;
3996     gchar *make_model = NULL;
3997     GString *browse_options = g_string_new ("");
3998 
3999     /* Skip any non-printer attributes */
4000     while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
4001       attr = ippNextAttribute(response);
4002 
4003     if (!attr)
4004       break;
4005 
4006     while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
4007       const char *attrname = ippGetName(attr);
4008       int value_tag = ippGetValueTag(attr);
4009 
4010       if (!strcasecmp(attrname, "printer-type") &&
4011 	  value_tag == IPP_TAG_ENUM) {
4012 	type = ippGetInteger(attr, 0);
4013 	if (type & CUPS_PRINTER_NOT_SHARED) {
4014 	  /* Skip CUPS queues not marked as shared */
4015 	  state = -1;
4016 	  type = -1;
4017 	  break;
4018 	}
4019       } else if (!strcasecmp(attrname, "printer-state") &&
4020 		 value_tag == IPP_TAG_ENUM)
4021 	state = ippGetInteger(attr, 0);
4022       else if (!strcasecmp(attrname, "printer-uri-supported") &&
4023 	       value_tag == IPP_TAG_URI)
4024 	uri = ippGetString(attr, 0, NULL);
4025       else if (!strcasecmp(attrname, "printer-location") &&
4026 	       value_tag == IPP_TAG_TEXT) {
4027 	/* Remove quotes */
4028 	gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
4029 	location = g_strjoinv ("", tokens);
4030 	g_strfreev (tokens);
4031       } else if (!strcasecmp(attrname, "printer-info") &&
4032 		 value_tag == IPP_TAG_TEXT) {
4033 	/* Remove quotes */
4034 	gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
4035 	info = g_strjoinv ("", tokens);
4036 	g_strfreev (tokens);
4037       } else if (!strcasecmp(attrname, "printer-make-and-model") &&
4038 		 value_tag == IPP_TAG_TEXT) {
4039 	/* Remove quotes */
4040 	gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
4041 	make_model = g_strjoinv ("", tokens);
4042 	g_strfreev (tokens);
4043       } else if (!strcasecmp(attrname, "auth-info-required") &&
4044 		 value_tag == IPP_TAG_KEYWORD) {
4045 	if (strcasecmp (ippGetString(attr, 0, NULL), "none"))
4046 	  g_string_append_printf (browse_options, "auth-info-required=%s ",
4047 				  ippGetString(attr, 0, NULL));
4048       } else if (!strcasecmp(attrname, "printer-uuid") &&
4049 		 value_tag == IPP_TAG_URI)
4050 	g_string_append_printf (browse_options, "uuid=%s ",
4051 				ippGetString(attr, 0, NULL));
4052       else if (!strcasecmp(attrname, "job-sheets-default") &&
4053 	       value_tag == IPP_TAG_NAME &&
4054 	       ippGetCount(attr) == 2)
4055 	g_string_append_printf (browse_options, "job-sheets=%s,%s ",
4056 				ippGetString(attr, 0, NULL),
4057 				ippGetString(attr, 1, NULL));
4058       else if (strstr(attrname, "-default")) {
4059 	gchar *name = g_strdup (attrname);
4060 	gchar *value = NULL;
4061 	*strstr (name, "-default") = '\0';
4062 
4063 	switch (value_tag) {
4064 	  gchar **tokens;
4065 
4066 	case IPP_TAG_KEYWORD:
4067 	case IPP_TAG_STRING:
4068 	case IPP_TAG_NAME:
4069 	  /* Escape value */
4070 	  tokens = g_strsplit_set (ippGetString(attr, 0, NULL),
4071 				   " \"\'\\", -1);
4072 	  value = g_strjoinv ("\\", tokens);
4073 	  g_strfreev (tokens);
4074 	  break;
4075 
4076 	default:
4077 	  /* other values aren't needed? */
4078 	  debug_printf("skipping %s (%d)\n", name, value_tag);
4079 	  break;
4080 	}
4081 
4082 	if (value) {
4083 	  g_string_append_printf (browse_options, "%s=%s ", name, value);
4084 	  g_free (value);
4085 	}
4086 
4087 	g_free (name);
4088       }
4089 
4090       attr = ippNextAttribute(response);
4091     }
4092 
4093     if (type != -1 && state != -1 && uri && location && info && make_model) {
4094       gchar *browse_options_str = g_string_free (browse_options, FALSE);
4095       browse_data_t *data;
4096       browse_options = NULL;
4097       g_strchomp (browse_options_str);
4098       data = new_browse_data (type, state, uri, location,
4099 			      info, make_model, browse_options_str);
4100       browse_data = g_list_insert (browse_data, data, 0);
4101       g_free (browse_options_str);
4102     }
4103 
4104     if (make_model)
4105       g_free (make_model);
4106 
4107     if (info)
4108       g_free (info);
4109 
4110     if (location)
4111       g_free (location);
4112 
4113     if (browse_options)
4114       g_string_free (browse_options, TRUE);
4115 
4116     if (!attr)
4117       break;
4118   }
4119 
4120  fail:
4121   if (response)
4122     ippDelete(response);
4123 }
4124 
4125 static void
update_local_printers(void)4126 update_local_printers (void)
4127 {
4128   gboolean get_printers = FALSE;
4129   http_t *conn;
4130 
4131   if (inhibit_local_printers_update)
4132     return;
4133 
4134   conn = http_connect_local ();
4135   if (conn &&
4136       (!local_printers_context || local_printers_context->can_subscribe)) {
4137     if (!local_printers_context ||
4138 	local_printers_context->subscription_id == -1) {
4139       /* No subscription yet. First, create the subscription. */
4140       local_printers_create_subscription (conn);
4141       get_printers = TRUE;
4142     } else
4143       /* We already have a subscription, so use it. */
4144 
4145       /* Note: for the moment, browse_poll_get_notifications() just
4146        * tells us whether we should re-fetch the printer list, so it
4147        * is safe to use here. */
4148       get_printers = browse_poll_get_notifications (local_printers_context,
4149 						    conn);
4150   } else
4151     get_printers = TRUE;
4152 
4153   if (get_printers) {
4154     get_local_printers ();
4155 
4156     if (BrowseLocalProtocols & BROWSE_CUPS)
4157       prepare_browse_data ();
4158   }
4159 }
4160 
4161 int
check_jobs()4162 check_jobs () {
4163   int num_jobs = 0;
4164   cups_job_t *jobs = NULL;
4165   remote_printer_t *p;
4166   http_t *conn = NULL;
4167 
4168   conn = http_connect_local ();
4169   if (conn == NULL) {
4170     debug_printf("Cannot connect to local CUPS to check whether there are still jobs.\n");
4171     return 0;
4172   }
4173 
4174   if (cupsArrayCount(remote_printers) > 0)
4175     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
4176 	 p;
4177 	 p = (remote_printer_t *)cupsArrayNext(remote_printers))
4178       if (!p->slave_of) {
4179 	num_jobs = cupsGetJobs2(conn, &jobs, p->queue_name, 0,
4180 				CUPS_WHICHJOBS_ACTIVE);
4181 	if (num_jobs > 0) {
4182 	  debug_printf("Queue %s still has jobs!\n", p->queue_name);
4183 	  cupsFreeJobs(num_jobs, jobs);
4184 	  return 1;
4185 	}
4186       }
4187 
4188   debug_printf("All our remote printers are without jobs.\n");
4189   return 0;
4190 }
4191 
4192 gboolean
autoshutdown_execute(gpointer data)4193 autoshutdown_execute (gpointer data)
4194 {
4195   debug_printf("autoshutdown_execute() in THREAD %ld\n", pthread_self());
4196   /* Are we still in auto shutdown mode and are we still without queues or
4197      jobs*/
4198   if (autoshutdown &&
4199       (cupsArrayCount(remote_printers) == 0 ||
4200        (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
4201     debug_printf("Automatic shutdown as there are no print queues maintained by us or no jobs on them for %d sec.\n",
4202 		 autoshutdown_timeout);
4203     g_main_loop_quit(gmainloop);
4204     g_main_context_wakeup(NULL);
4205   }
4206 
4207   /* Stop this timeout handler, we needed it only once */
4208   return FALSE;
4209 }
4210 
4211 int
color_space_score(const char * color_space)4212 color_space_score(const char *color_space)
4213 {
4214   int score = 0;
4215   const char *p = color_space;
4216 
4217   if (!p) return -1;
4218   if (!strncasecmp(p, "s", 1)) {
4219     p += 1;
4220     score += 2;
4221   } else if (!strncasecmp(p, "adobe", 5)) {
4222     p += 5;
4223     score += 1;
4224   } else
4225     score += 3;
4226   if (!strncasecmp(p, "black", 5)) {
4227     p += 5;
4228     score += 1000;
4229   } else if (!strncasecmp(p, "gray", 4)) {
4230     p += 4;
4231     score += 2000;
4232   } else if (!strncasecmp(p, "cmyk", 4)) {
4233     p += 4;
4234     score += 4000;
4235   } else if (!strncasecmp(p, "cmy", 3)) {
4236     p += 3;
4237     score += 3000;
4238   } else if (!strncasecmp(p, "rgb", 3)) {
4239     p += 3;
4240     score += 5000;
4241   }
4242   if (!strncasecmp(p, "-", 1) || !strncasecmp(p, "_", 1)) {
4243     p += 1;
4244   }
4245   score += strtol(p, (char **)&p, 10) * 10;
4246   debug_printf("Score for color space %s: %d\n", color_space, score);
4247   return score;
4248 }
4249 
4250 
4251 #ifdef HAVE_LDAP_REBIND_PROC
4252 #  if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
4253 /*
4254  * 'ldap_rebind_proc()' - Callback function for LDAP rebind
4255  */
4256 
4257 static int        /* O - Result code */
ldap_rebind_proc(LDAP * RebindLDAPHandle,LDAP_CONST char * refsp,ber_tag_t request,ber_int_t msgid,void * params)4258 ldap_rebind_proc(LDAP            *RebindLDAPHandle,  /* I - LDAP handle */
4259 		 LDAP_CONST char *refsp,             /* I - ??? */
4260 		 ber_tag_t       request,            /* I - ??? */
4261 		 ber_int_t       msgid,              /* I - ??? */
4262 		 void            *params)            /* I - ??? */
4263 {
4264   int   rc;     /* Result code */
4265 #    if LDAP_API_VERSION > 3000
4266   struct berval bval;     /* Bind value */
4267 #    endif /* LDAP_API_VERSION > 3000 */
4268   (void)request;
4269   (void)msgid;
4270   (void)params;
4271 
4272  /*
4273   * Bind to new LDAP server...
4274   */
4275 
4276   debug_printf("ldap_rebind_proc: Rebind to %s\n", refsp);
4277 
4278 #    if LDAP_API_VERSION > 3000
4279   bval.bv_val = BrowseLDAPPassword;
4280   bval.bv_len = (BrowseLDAPPassword == NULL) ? 0 : strlen(BrowseLDAPPassword);
4281 
4282   rc = ldap_sasl_bind_s(RebindLDAPHandle, BrowseLDAPBindDN, LDAP_SASL_SIMPLE,
4283                         &bval, NULL, NULL, NULL);
4284 #    else
4285   rc = ldap_bind_s(RebindLDAPHandle, BrowseLDAPBindDN, BrowseLDAPPassword,
4286                    LDAP_AUTH_SIMPLE);
4287 #    endif /* LDAP_API_VERSION > 3000 */
4288 
4289   return (rc);
4290 }
4291 
4292 
4293 #  else /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
4294 /*
4295  * 'ldap_rebind_proc()' - Callback function for LDAP rebind
4296  */
4297 
4298 static int        /* O - Result code */
ldap_rebind_proc(LDAP * RebindLDAPHandle,char ** dnp,char ** passwdp,int * authmethodp,int freeit,void * arg)4299 ldap_rebind_proc(LDAP *RebindLDAPHandle,   /* I - LDAP handle */
4300 		 char **dnp,               /* I - ??? */
4301 		 char **passwdp,           /* I - ??? */
4302 		 int  *authmethodp,        /* I - ??? */
4303 		 int  freeit,              /* I - ??? */
4304 		 void *arg)                /* I - ??? */
4305 {
4306   switch (freeit) {
4307   case 1:
4308     /*
4309      * Free current values...
4310      */
4311 
4312     debug_printf("ldap_rebind_proc: Free values...\n");
4313 
4314     if (dnp && *dnp)
4315       free(*dnp);
4316 
4317     if (passwdp && *passwdp)
4318       free(*passwdp);
4319     break;
4320 
4321   case 0:
4322     /*
4323      * Return credentials for LDAP referal...
4324      */
4325 
4326     debug_printf("ldap_rebind_proc: Return necessary values...\n");
4327 
4328     *dnp         = strdup(BrowseLDAPBindDN);
4329     *passwdp     = strdup(BrowseLDAPPassword);
4330     *authmethodp = LDAP_AUTH_SIMPLE;
4331     break;
4332 
4333   default:
4334     /*
4335      * Should never happen...
4336      */
4337 
4338     debug_printf("LDAP rebind has been called with wrong freeit value!\n");
4339     break;
4340   }
4341 
4342   return (LDAP_SUCCESS);
4343 }
4344 #  endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
4345 #endif /* HAVE_LDAP_REBIND_PROC */
4346 
4347 
4348 #ifdef HAVE_LDAP
4349 /*
4350  * 'ldap_new_connection()' - Start new LDAP connection
4351  */
4352 
4353 static LDAP *       /* O - LDAP handle */
ldap_new_connection(void)4354 ldap_new_connection(void)
4355 {
4356   int   rc;                       /* LDAP API status */
4357   int   version = 3;              /* LDAP version */
4358   struct berval bv = {0, ""};     /* SASL bind value */
4359   LDAP    *TempBrowseLDAPHandle=NULL;
4360                                   /* Temporary LDAP Handle */
4361 #  if defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP)
4362   int   ldap_ssl = 0;             /* LDAP SSL indicator */
4363   int   ssl_err = 0;              /* LDAP SSL error value */
4364 #  endif /* defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) */
4365 
4366 
4367 #  ifdef HAVE_OPENLDAP
4368 #    ifdef HAVE_LDAP_SSL
4369  /*
4370   * Set the certificate file to use for encrypted LDAP sessions...
4371   */
4372 
4373   if (BrowseLDAPCACertFile) {
4374     debug_printf("ldap_new_connection: Setting CA certificate file \"%s\"\n",
4375 		 BrowseLDAPCACertFile);
4376 
4377     if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
4378 			      (void *)BrowseLDAPCACertFile)) != LDAP_SUCCESS)
4379       debug_printf("Unable to set CA certificate file for LDAP "
4380 		   "connections: %d - %s\n", rc, ldap_err2string(rc));
4381   }
4382 #    endif /* HAVE_LDAP_SSL */
4383 
4384  /*
4385   * Initialize OPENLDAP connection...
4386   * LDAP stuff currently only supports ldapi EXTERNAL SASL binds...
4387   */
4388 
4389   if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost"))
4390     rc = ldap_initialize(&TempBrowseLDAPHandle, "ldapi:///");
4391   else
4392     rc = ldap_initialize(&TempBrowseLDAPHandle, BrowseLDAPServer);
4393 
4394 #  else /* HAVE_OPENLDAP */
4395 
4396   int   ldap_port = 0;          /* LDAP port */
4397   char    ldap_protocol[11],    /* LDAP protocol */
4398           ldap_host[255];       /* LDAP host */
4399 
4400  /*
4401   * Split LDAP URI into its components...
4402   */
4403 
4404   if (!BrowseLDAPServer) {
4405     debug_printf("BrowseLDAPServer not configured!\n");
4406     debug_printf("Disabling LDAP browsing!\n");
4407     /*BrowseLocalProtocols  &= ~BROWSE_LDAP;*/
4408     BrowseRemoteProtocols &= ~BROWSE_LDAP;
4409     return (NULL);
4410   }
4411 
4412   sscanf(BrowseLDAPServer, "%10[^:]://%254[^:/]:%d", ldap_protocol, ldap_host,
4413          &ldap_port);
4414 
4415   if (!strcmp(ldap_protocol, "ldap"))
4416     ldap_ssl = 0;
4417   else if (!strcmp(ldap_protocol, "ldaps"))
4418     ldap_ssl = 1;
4419   else {
4420     debug_printf("Unrecognized LDAP protocol (%s)!\n",
4421 		 ldap_protocol);
4422     debug_printf("Disabling LDAP browsing!\n");
4423     /*BrowseLocalProtocols &= ~BROWSE_LDAP;*/
4424     BrowseRemoteProtocols &= ~BROWSE_LDAP;
4425     return (NULL);
4426   }
4427 
4428   if (ldap_port == 0) {
4429     if (ldap_ssl)
4430       ldap_port = LDAPS_PORT;
4431     else
4432       ldap_port = LDAP_PORT;
4433   }
4434 
4435   debug_printf("ldap_new_connection: PROT:%s HOST:%s PORT:%d\n",
4436 	       ldap_protocol, ldap_host, ldap_port);
4437 
4438  /*
4439   * Initialize LDAP connection...
4440   */
4441 
4442   if (!ldap_ssl) {
4443     if ((TempBrowseLDAPHandle = ldap_init(ldap_host, ldap_port)) == NULL)
4444       rc = LDAP_OPERATIONS_ERROR;
4445     else
4446       rc = LDAP_SUCCESS;
4447 
4448 #    ifdef HAVE_LDAP_SSL
4449   } else {
4450    /*
4451     * Initialize SSL LDAP connection...
4452     */
4453 
4454     if (BrowseLDAPCACertFile) {
4455       rc = ldapssl_client_init(BrowseLDAPCACertFile, (void *)NULL);
4456       if (rc != LDAP_SUCCESS) {
4457         debug_printf("Failed to initialize LDAP SSL client!\n");
4458         rc = LDAP_OPERATIONS_ERROR;
4459       } else {
4460         if ((TempBrowseLDAPHandle = ldapssl_init(ldap_host, ldap_port,
4461                                                  1)) == NULL)
4462           rc = LDAP_OPERATIONS_ERROR;
4463         else
4464           rc = LDAP_SUCCESS;
4465       }
4466     } else {
4467       debug_printf("LDAP SSL certificate file/database not configured!\n");
4468       rc = LDAP_OPERATIONS_ERROR;
4469     }
4470 
4471 #    else /* HAVE_LDAP_SSL */
4472 
4473    /*
4474     * Return error, because client libraries doesn't support SSL
4475     */
4476 
4477     debug_printf("LDAP client libraries do not support SSL\n");
4478     rc = LDAP_OPERATIONS_ERROR;
4479 
4480 #    endif /* HAVE_LDAP_SSL */
4481   }
4482 #  endif /* HAVE_OPENLDAP */
4483 
4484  /*
4485   * Check return code from LDAP initialize...
4486   */
4487 
4488   if (rc != LDAP_SUCCESS) {
4489     debug_printf("Unable to initialize LDAP!\n");
4490 
4491     if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)
4492       debug_printf("Temporarily disabling LDAP browsing...\n");
4493     else {
4494       debug_printf("Disabling LDAP browsing!\n");
4495 
4496       /*BrowseLocalProtocols  &= ~BROWSE_LDAP;*/
4497       BrowseRemoteProtocols &= ~BROWSE_LDAP;
4498     }
4499 
4500     ldap_disconnect(TempBrowseLDAPHandle);
4501 
4502     return (NULL);
4503   }
4504 
4505  /*
4506   * Upgrade LDAP version...
4507   */
4508 
4509   if (ldap_set_option(TempBrowseLDAPHandle, LDAP_OPT_PROTOCOL_VERSION,
4510 		      (const void *)&version) != LDAP_SUCCESS) {
4511     debug_printf("Unable to set LDAP protocol version %d!\n",
4512 		 version);
4513     debug_printf("Disabling LDAP browsing!\n");
4514 
4515     /*BrowseLocalProtocols  &= ~BROWSE_LDAP;*/
4516     BrowseRemoteProtocols &= ~BROWSE_LDAP;
4517     ldap_disconnect(TempBrowseLDAPHandle);
4518 
4519     return (NULL);
4520   }
4521 
4522  /*
4523   * Register LDAP rebind procedure...
4524   */
4525 
4526 #  ifdef HAVE_LDAP_REBIND_PROC
4527 #    if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
4528 
4529   rc = ldap_set_rebind_proc(TempBrowseLDAPHandle, &ldap_rebind_proc,
4530                             (void *)NULL);
4531   if (rc != LDAP_SUCCESS)
4532     debug_printf("Setting LDAP rebind function failed with status %d: %s\n",
4533 		 rc, ldap_err2string(rc));
4534 
4535 #    else
4536 
4537   ldap_set_rebind_proc(TempBrowseLDAPHandle, &ldap_rebind_proc, (void *)NULL);
4538 
4539 #    endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
4540 #  endif /* HAVE_LDAP_REBIND_PROC */
4541 
4542  /*
4543   * Start LDAP bind...
4544   */
4545 
4546 #  if LDAP_API_VERSION > 3000
4547   struct berval bval;
4548   bval.bv_val = BrowseLDAPPassword;
4549   bval.bv_len = (BrowseLDAPPassword == NULL) ? 0 : strlen(BrowseLDAPPassword);
4550 
4551   if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost"))
4552     rc = ldap_sasl_bind_s(TempBrowseLDAPHandle, NULL, "EXTERNAL", &bv, NULL,
4553                           NULL, NULL);
4554   else
4555     rc = ldap_sasl_bind_s(TempBrowseLDAPHandle, BrowseLDAPBindDN,
4556 			  LDAP_SASL_SIMPLE, &bval, NULL, NULL, NULL);
4557 
4558 #  else
4559   rc = ldap_bind_s(TempBrowseLDAPHandle, BrowseLDAPBindDN,
4560 		   BrowseLDAPPassword, LDAP_AUTH_SIMPLE);
4561 #  endif /* LDAP_API_VERSION > 3000 */
4562 
4563   if (rc != LDAP_SUCCESS) {
4564     debug_printf("LDAP bind failed with error %d: %s\n",
4565 		 rc, ldap_err2string(rc));
4566 
4567 #  if defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP)
4568     if (ldap_ssl && (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)) {
4569       ssl_err = PORT_GetError();
4570       if (ssl_err != 0)
4571         debug_printf("LDAP SSL error %d: %s\n", ssl_err,
4572 		     ldapssl_err2string(ssl_err));
4573     }
4574 #  endif /* defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) */
4575 
4576     ldap_disconnect(TempBrowseLDAPHandle);
4577 
4578     return (NULL);
4579   }
4580 
4581   debug_printf("LDAP connection established\n");
4582 
4583   return (TempBrowseLDAPHandle);
4584 }
4585 
4586 
4587 /*
4588  * 'ldap_reconnect()' - Reconnect to LDAP Server
4589  */
4590 
4591 static LDAP *       /* O - New LDAP handle */
ldap_reconnect(void)4592 ldap_reconnect(void)
4593 {
4594   LDAP  *TempBrowseLDAPHandle = NULL; /* Temp Handle to LDAP server */
4595 
4596  /*
4597   * Get a new LDAP Handle and replace the global Handle
4598   * if the new connection was successful.
4599   */
4600 
4601   debug_printf("Try LDAP reconnect...\n");
4602 
4603   TempBrowseLDAPHandle = ldap_new_connection();
4604 
4605   if (TempBrowseLDAPHandle != NULL) {
4606     if (BrowseLDAPHandle != NULL)
4607       ldap_disconnect(BrowseLDAPHandle);
4608 
4609     BrowseLDAPHandle = TempBrowseLDAPHandle;
4610   }
4611 
4612   return (BrowseLDAPHandle);
4613 }
4614 
4615 
4616 /*
4617  * 'ldap_disconnect()' - Disconnect from LDAP Server
4618  */
4619 
4620 static void
ldap_disconnect(LDAP * ld)4621 ldap_disconnect(LDAP *ld)   /* I - LDAP handle */
4622 {
4623   int rc;       /* Return code */
4624 
4625  /*
4626   * Close LDAP handle...
4627   */
4628 
4629 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4630   rc = ldap_unbind_ext_s(ld, NULL, NULL);
4631 #  else
4632   rc = ldap_unbind_s(ld);
4633 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4634 
4635   if (rc != LDAP_SUCCESS)
4636     debug_printf("Unbind from LDAP server failed with status %d: %s\n",
4637 		 rc, ldap_err2string(rc));
4638 }
4639 
4640 /*
4641  * 'cupsdUpdateLDAPBrowse()' - Scan for new printers via LDAP...
4642  */
4643 
4644 void
cupsdUpdateLDAPBrowse(void)4645 cupsdUpdateLDAPBrowse(void)
4646 {
4647   char    uri[HTTP_MAX_URI],            /* Printer URI */
4648           host[HTTP_MAX_URI],           /* Hostname */
4649           resource[HTTP_MAX_URI],       /* Resource path */
4650           local_resource[HTTP_MAX_URI], /* Resource path */
4651           service_name[4096],
4652           location[1024],               /* Printer location */
4653           info[1024],                   /* Printer information */
4654           make_model[1024],             /* Printer make and model */
4655           type_num[30],                 /* Printer type number */
4656           scheme[32],                   /* URI's scheme */
4657           username[64];                 /* URI's username */
4658   int     port;                         /* URI's port number */
4659   char    *c;
4660   int     hl;
4661   int     rc;                           /* LDAP status */
4662   int     limit;                        /* Size limit */
4663   LDAPMessage *res,                     /* LDAP search results */
4664           *e;                           /* Current entry from search */
4665 
4666   debug_printf("UpdateLDAPBrowse\n");
4667 
4668  /*
4669   * Reconnect if LDAP Handle is invalid...
4670   */
4671 
4672   if (! BrowseLDAPHandle) {
4673     ldap_reconnect();
4674     return;
4675   }
4676 
4677  /*
4678   * Search for cups printers in LDAP directory...
4679   */
4680 
4681   rc = ldap_search_rec(BrowseLDAPHandle, BrowseLDAPDN, LDAP_SCOPE_SUBTREE,
4682                        BrowseLDAPFilter, (char **)ldap_attrs, 0, &res);
4683 
4684  /*
4685   * If ldap search was successfull then exit function
4686   * and temporary disable LDAP updates...
4687   */
4688 
4689   if (rc != LDAP_SUCCESS) {
4690     if (BrowseLDAPUpdate && ((rc == LDAP_SERVER_DOWN) ||
4691 			     (rc == LDAP_CONNECT_ERROR))) {
4692       BrowseLDAPUpdate = FALSE;
4693       debug_printf("LDAP update temporary disabled\n");
4694     }
4695     return;
4696   }
4697 
4698  /*
4699   * If LDAP updates were disabled, we will reenable them...
4700   */
4701 
4702   if (!BrowseLDAPUpdate) {
4703     BrowseLDAPUpdate = TRUE;
4704     debug_printf("LDAP update enabled\n");
4705   }
4706 
4707  /*
4708   * Count LDAP entries and return if no entry exist...
4709   */
4710 
4711   limit = ldap_count_entries(BrowseLDAPHandle, res);
4712   debug_printf("LDAP search returned %d entries\n", limit);
4713   if (limit < 1) {
4714     ldap_freeres(res);
4715     return;
4716   }
4717 
4718  /*
4719   * Loop through the available printers...
4720   */
4721 
4722   for (e = ldap_first_entry(BrowseLDAPHandle, res);
4723        e;
4724        e = ldap_next_entry(BrowseLDAPHandle, e)) {
4725    /*
4726     * Get the required values from this entry...
4727     */
4728 
4729     if (ldap_getval_firststring(BrowseLDAPHandle, e,
4730                                 "printerDescription", info, sizeof(info)) == -1)
4731       continue;
4732 
4733     if (ldap_getval_firststring(BrowseLDAPHandle, e,
4734                                 "printerLocation", location,
4735 				sizeof(location)) == -1)
4736       continue;
4737 
4738     if (ldap_getval_firststring(BrowseLDAPHandle, e,
4739                                 "printerMakeAndModel", make_model,
4740 				sizeof(make_model)) == -1)
4741       continue;
4742 
4743     if (ldap_getval_firststring(BrowseLDAPHandle, e,
4744                                 "printerType", type_num,
4745 				sizeof(type_num)) == -1)
4746       continue;
4747 
4748     if (ldap_getval_firststring(BrowseLDAPHandle, e,
4749                                 "printerURI", uri, sizeof(uri)) == -1)
4750       continue;
4751 
4752    /*
4753     * Process the entry...
4754     */
4755 
4756     memset(scheme, 0, sizeof(scheme));
4757     memset(username, 0, sizeof(username));
4758     memset(host, 0, sizeof(host));
4759     memset(resource, 0, sizeof(resource));
4760     memset(local_resource, 0, sizeof(local_resource));
4761 
4762     httpSeparateURI (HTTP_URI_CODING_ALL, uri,
4763 		     scheme, sizeof(scheme) - 1,
4764 		     username, sizeof(username) - 1,
4765 		     host, sizeof(host) - 1,
4766 		     &port,
4767 		     resource, sizeof(resource)- 1);
4768 
4769     if (strncasecmp (resource, "/printers/", 10) &&
4770 	strncasecmp (resource, "/classes/", 9)) {
4771       debug_printf("don't understand URI: %s\n", uri);
4772       return;
4773     }
4774 
4775     strncpy (local_resource, resource + 1, sizeof (local_resource) - 1);
4776     local_resource[sizeof (local_resource) - 1] = '\0';
4777     c = strchr (local_resource, '?');
4778     if (c)
4779       *c = '\0';
4780 
4781     /* Build the DNS-SD service name which CUPS would give to this printer
4782        when DNS-SD-broadcasting it */
4783     snprintf(service_name, sizeof (service_name), "%s @ %s",
4784 	     (strlen(info) > 0 ? info : strchr(local_resource, '/') + 1), host);
4785     /* Cut off trailing ".local" of host name */
4786     hl = strlen(service_name);
4787     if (hl > 6 && !strcasecmp(service_name + hl - 6, ".local"))
4788       service_name[hl - 6] = '\0';
4789     if (hl > 7 && !strcasecmp(service_name + hl - 7, ".local."))
4790       service_name[hl - 7] = '\0';
4791     /* DNS-SD service name has max. 63 characters */
4792     service_name[63] = '\0';
4793 
4794     debug_printf("LDAP: Remote host: %s; Port: %d; Remote queue name: %s; Service Name: %s\n",
4795 		 host, port, strchr(local_resource, '/') + 1, service_name);
4796 
4797     examine_discovered_printer_record(host, NULL, port, local_resource,
4798 				      service_name, location, info, "", "",
4799 				      "", 0, NULL);
4800 
4801   }
4802 
4803   ldap_freeres(res);
4804 }
4805 
4806 /*
4807  * 'ldap_search_rec()' - LDAP Search with reconnect
4808  */
4809 
4810 static int        /* O - Return code */
ldap_search_rec(LDAP * ld,char * base,int scope,char * filter,char * attrs[],int attrsonly,LDAPMessage ** res)4811 ldap_search_rec(LDAP        *ld,        /* I - LDAP handler */
4812                 char        *base,      /* I - Base dn */
4813                 int         scope,      /* I - LDAP search scope */
4814                 char        *filter,    /* I - Filter string */
4815                 char        *attrs[],   /* I - Requested attributes */
4816                 int         attrsonly,  /* I - Return only attributes? */
4817                 LDAPMessage **res)      /* I - LDAP handler */
4818 {
4819   int rc;       /* Return code */
4820   LDAP  *ldr;   /* LDAP handler after reconnect */
4821 
4822 
4823 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4824   rc = ldap_search_ext_s(ld, base, scope, filter, attrs, attrsonly, NULL, NULL,
4825                          NULL, LDAP_NO_LIMIT, res);
4826 #  else
4827   rc = ldap_search_s(ld, base, scope, filter, attrs, attrsonly, res);
4828 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4829 
4830  /*
4831   * If we have a connection problem try again...
4832   */
4833 
4834   if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR) {
4835     debug_printf("LDAP search failed with status %d: %s\n",
4836 		 rc, ldap_err2string(rc));
4837     debug_printf("We try the LDAP search once again after reconnecting to "
4838 		 "the server\n");
4839     ldap_freeres(*res);
4840     ldr = ldap_reconnect();
4841 
4842 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4843     rc = ldap_search_ext_s(ldr, base, scope, filter, attrs, attrsonly, NULL,
4844                            NULL, NULL, LDAP_NO_LIMIT, res);
4845 #  else
4846     rc = ldap_search_s(ldr, base, scope, filter, attrs, attrsonly, res);
4847 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4848   }
4849 
4850   if (rc == LDAP_NO_SUCH_OBJECT)
4851     debug_printf("ldap_search_rec: LDAP entry/object not found\n");
4852   else if (rc != LDAP_SUCCESS)
4853     debug_printf("ldap_search_rec: LDAP search failed with status %d: %s\n",
4854 		 rc, ldap_err2string(rc));
4855 
4856   if (rc != LDAP_SUCCESS)
4857     ldap_freeres(*res);
4858 
4859   return (rc);
4860 }
4861 
4862 
4863 /*
4864  * 'ldap_freeres()' - Free LDAPMessage
4865  */
4866 
4867 static void
ldap_freeres(LDAPMessage * entry)4868 ldap_freeres(LDAPMessage *entry)  /* I - LDAP handler */
4869 {
4870   int rc;       /* Return value */
4871 
4872   rc = ldap_msgfree(entry);
4873   if (rc == -1)
4874     debug_printf("Can't free LDAPMessage!\n");
4875   else if (rc == 0)
4876     debug_printf("Freeing LDAPMessage was unnecessary\n");
4877 }
4878 
4879 
4880 /*
4881  * 'ldap_getval_char()' - Get first LDAP value and convert to string
4882  */
4883 
4884 static int        /* O - Return code */
ldap_getval_firststring(LDAP * ld,LDAPMessage * entry,char * attr,char * retval,unsigned long maxsize)4885 ldap_getval_firststring(LDAP          *ld,     /* I - LDAP handler */
4886 			LDAPMessage   *entry,  /* I - LDAP message or search
4887 						      result */
4888 			char          *attr,   /* I - the wanted attribute  */
4889 			char          *retval, /* O - String to return */
4890 			unsigned long maxsize) /* I - Max string size */
4891 {
4892   char            *dn;    /* LDAP DN */
4893   int             rc = 0; /* Return code */
4894 #  if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4895   struct berval   **bval; /* LDAP value array */
4896   unsigned long   size;   /* String size */
4897 
4898 
4899  /*
4900   * Get value from LDAPMessage...
4901   */
4902 
4903   if ((bval = ldap_get_values_len(ld, entry, attr)) == NULL) {
4904     rc = -1;
4905     dn = ldap_get_dn(ld, entry);
4906     debug_printf("Failed to get LDAP value %s for %s!\n",
4907 		 attr, dn);
4908     ldap_memfree(dn);
4909   } else {
4910    /*
4911     * Check size and copy value into our string...
4912     */
4913 
4914     size = maxsize;
4915     if (size < (bval[0]->bv_len + 1)) {
4916       rc = -1;
4917       dn = ldap_get_dn(ld, entry);
4918       debug_printf("Attribute %s is too big! (dn: %s)\n",
4919 		   attr, dn);
4920       ldap_memfree(dn);
4921     } else
4922       size = bval[0]->bv_len + 1;
4923 
4924     strncpy(retval, bval[0]->bv_val, size);
4925     if (size > 0)
4926       retval[size - 1] = '\0';
4927     ldap_value_free_len(bval);
4928   }
4929 #  else
4930   char  **value;      /* LDAP value */
4931 
4932  /*
4933   * Get value from LDAPMessage...
4934   */
4935 
4936   if ((value = (char **)ldap_get_values(ld, entry, attr)) == NULL) {
4937     rc = -1;
4938     dn = ldap_get_dn(ld, entry);
4939     debug_printf("Failed to get LDAP value %s for %s!\n",
4940 		 attr, dn);
4941     ldap_memfree(dn);
4942   } else {
4943     strncpy(retval, *value, maxsize);
4944     if (maxsize > 0)
4945       retval[maxsize - 1] = '\0';
4946     ldap_value_free(value);
4947   }
4948 #  endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4949 
4950   return (rc);
4951 }
4952 
4953 #endif /* HAVE_LDAP */
4954 
4955 
4956 static int
create_subscription()4957 create_subscription ()
4958 {
4959   ipp_t *req;
4960   ipp_t *resp;
4961   ipp_attribute_t *attr;
4962   int id = 0;
4963   http_t *conn = NULL;
4964 
4965   conn = http_connect_local ();
4966   if (conn == NULL) {
4967     debug_printf("Cannot connect to local CUPS to subscribe to notifications.\n");
4968     return 0;
4969   }
4970 
4971   req = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION);
4972   ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
4973 		"printer-uri", NULL, "/");
4974   ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
4975 		"notify-events", NULL, "all");
4976   ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
4977 		"notify-recipient-uri", NULL, "dbus://");
4978   ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
4979 		 "notify-lease-duration", notify_lease_duration);
4980 
4981   resp = cupsDoRequest (conn, req, "/");
4982   if (!resp || cupsLastError() != IPP_STATUS_OK) {
4983     debug_printf ("Error subscribing to CUPS notifications: %s\n",
4984 		  cupsLastErrorString ());
4985     return 0;
4986   }
4987 
4988   attr = ippFindAttribute (resp, "notify-subscription-id", IPP_TAG_INTEGER);
4989   if (attr)
4990     id = ippGetInteger (attr, 0);
4991   else
4992     debug_printf (""
4993 		  "ipp-create-printer-subscription response doesn't contain "
4994 		  "subscription id.\n");
4995 
4996   ippDelete (resp);
4997   return id;
4998 }
4999 
5000 
5001 static gboolean
renew_subscription(int id)5002 renew_subscription (int id)
5003 {
5004   ipp_t *req;
5005   ipp_t *resp;
5006   http_t *conn = NULL;
5007 
5008   conn = http_connect_local ();
5009   if (conn == NULL) {
5010     debug_printf("Cannot connect to local CUPS to renew subscriptions.\n");
5011     return FALSE;
5012   }
5013 
5014   req = ippNewRequest (IPP_RENEW_SUBSCRIPTION);
5015   ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
5016 		 "notify-subscription-id", id);
5017   ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
5018 		"printer-uri", NULL, "/");
5019   ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5020 		"notify-recipient-uri", NULL, "dbus://");
5021   ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5022 		 "notify-lease-duration", notify_lease_duration);
5023 
5024   resp = cupsDoRequest (conn, req, "/");
5025   if (!resp || cupsLastError() != IPP_STATUS_OK) {
5026     debug_printf ("Error renewing CUPS subscription %d: %s\n",
5027 		  id, cupsLastErrorString ());
5028     return FALSE;
5029   }
5030 
5031   ippDelete (resp);
5032   return TRUE;
5033 }
5034 
5035 
5036 static gboolean
renew_subscription_timeout(gpointer userdata)5037 renew_subscription_timeout (gpointer userdata)
5038 {
5039   int *subscription_id = userdata;
5040 
5041   debug_printf("renew_subscription_timeout() in THREAD %ld\n", pthread_self());
5042 
5043   if (*subscription_id <= 0 || !renew_subscription (*subscription_id))
5044     *subscription_id = create_subscription ();
5045 
5046   return TRUE;
5047 }
5048 
5049 
5050 void
cancel_subscription(int id)5051 cancel_subscription (int id)
5052 {
5053   ipp_t *req;
5054   ipp_t *resp;
5055   http_t *conn = NULL;
5056 
5057   conn = http_connect_local ();
5058   if (conn == NULL) {
5059     debug_printf("Cannot connect to local CUPS to cancel subscriptions.\n");
5060     return;
5061   }
5062 
5063   if (id <= 0)
5064     return;
5065 
5066   req = ippNewRequest (IPP_CANCEL_SUBSCRIPTION);
5067   ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
5068 		"printer-uri", NULL, "/");
5069   ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
5070 		 "notify-subscription-id", id);
5071 
5072   resp = cupsDoRequest (conn, req, "/");
5073   if (!resp || cupsLastError() != IPP_STATUS_OK) {
5074     debug_printf ("Error subscribing to CUPS notifications: %s\n",
5075 		  cupsLastErrorString ());
5076     return;
5077   }
5078 
5079   ippDelete (resp);
5080 }
5081 
5082 int
is_created_by_cups_browsed(const char * printer)5083 is_created_by_cups_browsed (const char *printer) {
5084   remote_printer_t *p;
5085 
5086   if (printer == NULL)
5087     return 0;
5088   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
5089        p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
5090     if (!p->slave_of && !strcasecmp(printer, p->queue_name))
5091       return 1;
5092 
5093   return 0;
5094 }
5095 
5096 remote_printer_t *
printer_record(const char * printer)5097 printer_record (const char *printer) {
5098   remote_printer_t *p;
5099 
5100   if (printer == NULL)
5101     return NULL;
5102   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
5103        p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
5104     if (!p->slave_of && !strcasecmp(printer, p->queue_name))
5105       return p;
5106 
5107   return NULL;
5108 }
5109 
5110 void
log_cluster(remote_printer_t * p)5111 log_cluster(remote_printer_t *p) {
5112   remote_printer_t *q, *r;
5113   int i;
5114   if (p == NULL || (!debug_stderr && !debug_logfile))
5115     return;
5116   if (p->slave_of)
5117     q = p->slave_of;
5118   else
5119     q = p;
5120   if (q->queue_name == NULL)
5121     return;
5122   debug_printf("Remote CUPS printers clustered as queue %s:\n", q->queue_name);
5123   for (r = (remote_printer_t *)cupsArrayFirst(remote_printers), i = 0;
5124        r; r = (remote_printer_t *)cupsArrayNext(remote_printers), i ++)
5125     if (r->status != STATUS_DISAPPEARED && r->status != STATUS_UNCONFIRMED &&
5126 	r->status != STATUS_TO_BE_RELEASED &&
5127 	(r == q || r->slave_of == q))
5128       debug_printf("  %s%s%s\n", r->uri,
5129 		   (r == q ? "*" : ""),
5130 		   (i == q->last_printer ? " (last job printed)" : ""));
5131 }
5132 
5133 void
log_all_printers()5134 log_all_printers() {
5135   remote_printer_t *p, *q;
5136   if (!debug_stderr && !debug_logfile)
5137     return;
5138   debug_printf("=== Remote printer overview ===\n");
5139   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
5140        p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
5141     debug_printf("Printer %s (%s, %s): Local queue %s, %s, Slave of %s%s\n",
5142 		 p->uri,
5143 		 p->host, (p->ip ? p->ip : "IP not determined"), p->queue_name,
5144 		 (p->netprinter ? "IPP Printer" : "Remote CUPS Printer"),
5145 		 ((q = p->slave_of) != NULL ?
5146 		  (q->uri ? q->uri : "Deleted Printer") : "None"),
5147 		 (p->status == STATUS_UNCONFIRMED ? " (Unconfirmed)" :
5148 		  (p->status == STATUS_DISAPPEARED ? " (Disappeared)" :
5149 		   (p->status == STATUS_TO_BE_RELEASED ?
5150 		    " (To be released from cups-browsed)" :
5151 		    (p->status == STATUS_TO_BE_CREATED ?
5152 		     " (To be created/updated)" : "")))));
5153   debug_printf("===============================\n");
5154 }
5155 
5156 char*
is_disabled(const char * printer,const char * reason)5157 is_disabled(const char *printer, const char *reason) {
5158   ipp_t *request, *response;
5159   ipp_attribute_t *attr;
5160   const char *pname = NULL;
5161   ipp_pstate_t pstate = IPP_PRINTER_IDLE;
5162   const char *p;
5163   char *pstatemsg = NULL;
5164   static const char *pattrs[] =
5165                 {
5166                   "printer-name",
5167                   "printer-state",
5168 		  "printer-state-message"
5169                 };
5170   http_t *conn = NULL;
5171 
5172   conn = http_connect_local ();
5173   if (conn == NULL) {
5174     debug_printf("Cannot connect to local CUPS to check whether the printer %s is disabled.\n",
5175 		 printer);
5176     return NULL;
5177   }
5178 
5179   request = ippNewRequest(CUPS_GET_PRINTERS);
5180   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
5181 		"requested-attributes",
5182 		sizeof(pattrs) / sizeof(pattrs[0]),
5183 		NULL, pattrs);
5184   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
5185 	       "requesting-user-name",
5186 	       NULL, cupsUser());
5187   if ((response = cupsDoRequest(conn, request, "/")) != NULL) {
5188     for (attr = ippFirstAttribute(response); attr != NULL;
5189 	 attr = ippNextAttribute(response)) {
5190       while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
5191 	attr = ippNextAttribute(response);
5192       if (attr == NULL)
5193 	break;
5194       pname = NULL;
5195       pstate = IPP_PRINTER_IDLE;
5196       if (pstatemsg) {
5197 	free(pstatemsg);
5198 	pstatemsg = NULL;
5199       }
5200       while (attr != NULL && ippGetGroupTag(attr) ==
5201 	     IPP_TAG_PRINTER) {
5202 	if (!strcmp(ippGetName(attr), "printer-name") &&
5203 	    ippGetValueTag(attr) == IPP_TAG_NAME)
5204 	  pname = ippGetString(attr, 0, NULL);
5205 	else if (!strcmp(ippGetName(attr), "printer-state") &&
5206 		 ippGetValueTag(attr) == IPP_TAG_ENUM)
5207 	  pstate = (ipp_pstate_t)ippGetInteger(attr, 0);
5208 	else if (!strcmp(ippGetName(attr), "printer-state-message") &&
5209 		 ippGetValueTag(attr) == IPP_TAG_TEXT) {
5210 	  if (pstatemsg != NULL) {
5211 	    free(pstatemsg);
5212 	    pstatemsg = NULL;
5213 	  }
5214 	  p = ippGetString(attr, 0, NULL);
5215 	  if (p != NULL) pstatemsg = strdup(p);
5216 	}
5217 	attr = ippNextAttribute(response);
5218       }
5219       if (pname == NULL) {
5220 	if (attr == NULL)
5221 	  break;
5222 	else
5223 	  continue;
5224       }
5225       if (!strcasecmp(pname, printer)) {
5226 	switch (pstate) {
5227 	case IPP_PRINTER_IDLE:
5228 	case IPP_PRINTER_PROCESSING:
5229 	  ippDelete(response);
5230 	  if (pstatemsg != NULL) {
5231 	    free(pstatemsg);
5232 	    pstatemsg = NULL;
5233 	  }
5234 	  return NULL;
5235 	case IPP_PRINTER_STOPPED:
5236 	  ippDelete(response);
5237 	  if (reason == NULL)
5238 	    return pstatemsg;
5239 	  else if (pstatemsg != NULL && (strcasestr(pstatemsg, reason) != NULL))
5240 	    return pstatemsg;
5241 	  else {
5242             if (pstatemsg != NULL) {
5243 	      free(pstatemsg);
5244 	      pstatemsg = NULL;
5245             }
5246 	    return NULL;
5247 	  }
5248 	}
5249       }
5250     }
5251     debug_printf("No information regarding enabled/disabled found about the requested printer '%s'\n",
5252 		 printer);
5253     ippDelete(response);
5254     if (pstatemsg != NULL) {
5255       free(pstatemsg);
5256       pstatemsg = NULL;
5257     }
5258     return NULL;
5259   }
5260   debug_printf("ERROR: Request for printer info failed: %s\n",
5261 	       cupsLastErrorString());
5262   if (pstatemsg != NULL) {
5263     free(pstatemsg);
5264     pstatemsg = NULL;
5265   }
5266   return NULL;
5267 }
5268 
5269 int
enable_printer(const char * printer)5270 enable_printer (const char *printer) {
5271   ipp_t *request;
5272   char uri[HTTP_MAX_URI];
5273   http_t *conn = NULL;
5274 
5275   conn = http_connect_local ();
5276   if (conn == NULL) {
5277     debug_printf("Cannot connect to local CUPS to enable printer %s.\n",
5278 		 printer);
5279     return -1;
5280   }
5281 
5282   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
5283 		   "localhost", 0, "/printers/%s", printer);
5284   request = ippNewRequest (IPP_RESUME_PRINTER);
5285   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
5286 		"printer-uri", NULL, uri);
5287   ippDelete(cupsDoRequest (conn, request, "/admin/"));
5288   if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
5289     debug_printf("ERROR: Failed enabling printer '%s': %s\n",
5290 		 printer, cupsLastErrorString());
5291     return -1;
5292   }
5293   debug_printf("Enabled printer '%s'\n", printer);
5294   return 0;
5295 }
5296 
5297 int
disable_printer(const char * printer,const char * reason)5298 disable_printer (const char *printer, const char *reason) {
5299   ipp_t *request;
5300   char uri[HTTP_MAX_URI];
5301   http_t *conn = NULL;
5302 
5303   conn = http_connect_local ();
5304   if (conn == NULL) {
5305     debug_printf("Cannot connect to local CUPS to disable printer %s.\n",
5306 		 printer);
5307     return -1;
5308   }
5309 
5310   if (reason == NULL)
5311     reason = "Disabled by cups-browsed";
5312   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
5313 		   "localhost", 0, "/printers/%s", printer);
5314   request = ippNewRequest (IPP_PAUSE_PRINTER);
5315   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
5316 		"printer-uri", NULL, uri);
5317   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
5318 		"printer-state-message", NULL, reason);
5319   ippDelete(cupsDoRequest (conn, request, "/admin/"));
5320   if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
5321     debug_printf("ERROR: Failed disabling printer '%s': %s\n",
5322 		 printer, cupsLastErrorString());
5323     return -1;
5324   }
5325   debug_printf("Disabled printer '%s'\n", printer);
5326   return 0;
5327 }
5328 
5329 int
set_cups_default_printer(const char * printer)5330 set_cups_default_printer(const char *printer) {
5331   ipp_t *request;
5332   char uri[HTTP_MAX_URI];
5333   http_t *conn = NULL;
5334 
5335   conn = http_connect_local ();
5336   if (conn == NULL) {
5337     debug_printf("Cannot connect to local CUPS to subscribe to set printer %s as default printer.\n",
5338 		 printer);
5339     return -1;
5340   }
5341 
5342   if (printer == NULL)
5343     return 0;
5344   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
5345                    "localhost", 0, "/printers/%s", printer);
5346   request = ippNewRequest(IPP_OP_CUPS_SET_DEFAULT);
5347   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
5348                "printer-uri", NULL, uri);
5349   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
5350                NULL, cupsUser());
5351   ippDelete(cupsDoRequest(conn, request, "/admin/"));
5352   if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
5353     debug_printf("ERROR: Failed setting CUPS default printer to '%s': %s\n",
5354 		 printer, cupsLastErrorString());
5355     return -1;
5356   }
5357   debug_printf("Successfully set CUPS default printer to '%s'\n",
5358 	       printer);
5359   return 0;
5360 }
5361 
5362 char*
get_cups_default_printer()5363 get_cups_default_printer() {
5364   ipp_t *request, *response;
5365   ipp_attribute_t *attr;
5366   const char *default_printer_name = NULL;
5367   char *name_string;
5368   http_t *conn = NULL;
5369 
5370   conn = http_connect_local ();
5371   if (conn == NULL) {
5372     debug_printf("Cannot connect to local CUPS to find out which is the default printer.\n");
5373     return NULL;
5374   }
5375 
5376   request = ippNewRequest(CUPS_GET_DEFAULT);
5377   /* Default user */
5378   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
5379 	       "requesting-user-name", NULL, cupsUser());
5380   /* Do it */
5381   response = cupsDoRequest(conn, request, "/");
5382   if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE || !response) {
5383     debug_printf("Could not determine system default printer!\n");
5384   } else {
5385     for (attr = ippFirstAttribute(response); attr != NULL;
5386 	 attr = ippNextAttribute(response)) {
5387       while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
5388 	attr = ippNextAttribute(response);
5389       if (attr) {
5390 	for (; attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER;
5391 	     attr = ippNextAttribute(response)) {
5392 	  if (!strcasecmp(ippGetName(attr), "printer-name") &&
5393 	      ippGetValueTag(attr) == IPP_TAG_NAME) {
5394 	    default_printer_name = ippGetString(attr, 0, NULL);
5395 	    break;
5396 	  }
5397 	}
5398       }
5399       if (default_printer_name)
5400 	break;
5401     }
5402   }
5403 
5404   if (default_printer_name != NULL) {
5405     name_string = strdup(default_printer_name);
5406   } else {
5407     name_string = NULL;
5408   }
5409 
5410   ippDelete(response);
5411 
5412   return name_string;
5413 }
5414 
5415 int
is_cups_default_printer(const char * printer)5416 is_cups_default_printer(const char *printer) {
5417   if (printer == NULL)
5418     return 0;
5419   char *cups_default = get_cups_default_printer();
5420   if (cups_default == NULL)
5421     return 0;
5422   if (!strcasecmp(printer, cups_default)) {
5423     free(cups_default);
5424     return 1;
5425   }
5426   free(cups_default);
5427   return 0;
5428 }
5429 
5430 int
invalidate_default_printer(int local)5431 invalidate_default_printer(int local) {
5432   const char *filename = local ? local_default_printer_file :
5433     remote_default_printer_file;
5434   unlink(filename);
5435   return 0;
5436 }
5437 
5438 int
record_default_printer(const char * printer,int local)5439 record_default_printer(const char *printer, int local) {
5440   FILE *fp = NULL;
5441   const char *filename = local ? local_default_printer_file :
5442     remote_default_printer_file;
5443 
5444   if (printer == NULL || strlen(printer) == 0)
5445     return invalidate_default_printer(local);
5446 
5447   fp = fopen(filename, "w+");
5448   if (fp == NULL) {
5449     debug_printf("ERROR: Failed creating file %s\n",
5450 		 filename);
5451     invalidate_default_printer(local);
5452     return -1;
5453   }
5454   fprintf(fp, "%s", printer);
5455   fclose(fp);
5456 
5457   return 0;
5458 }
5459 
5460 char*
retrieve_default_printer(int local)5461 retrieve_default_printer(int local) {
5462   FILE *fp = NULL;
5463   const char *filename = local ? local_default_printer_file :
5464     remote_default_printer_file;
5465   const char *printer = NULL;
5466   char *p, buf[1024];
5467   int n;
5468 
5469   fp = fopen(filename, "r");
5470   if (fp == NULL) {
5471     debug_printf("Failed reading file %s\n",
5472 		 filename);
5473     return NULL;
5474   }
5475   p = buf;
5476   n = fscanf(fp, "%s", p);
5477   if (n == 1) {
5478     if (strlen(p) > 0)
5479       printer = p;
5480   }
5481   fclose(fp);
5482 
5483   return (printer ? strdup(printer) : NULL);
5484 }
5485 
5486 int
invalidate_printer_options(const char * printer)5487 invalidate_printer_options(const char *printer) {
5488   char filename[1024];
5489 
5490   snprintf(filename, sizeof(filename), save_options_file,
5491 	   printer);
5492   unlink(filename);
5493   return 0;
5494 }
5495 
5496 char*
loadPPD(http_t * http,const char * name)5497 loadPPD(http_t *http,
5498 	const char *name)
5499 {
5500   /* This function replaces cupsGetPPD2(), but is much simplified
5501      (does not support classes) and works with non-standard (!= 631)
5502      ports */
5503 
5504   char uri[HTTP_MAX_URI];
5505   char *resource;
5506   int fd, status;
5507   char tempfile[1024] = "";
5508 
5509   /* Download URI and resource for the PPD file */
5510   httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "http", NULL,
5511 		   "localhost", 0, "/printers/%s.ppd", name);
5512   resource = strstr(uri, "/printers/");
5513 
5514   /* Download the file */
5515   fd = cupsTempFd(tempfile, sizeof(tempfile));
5516   status = cupsGetFd(http, resource, fd);
5517   close(fd);
5518 
5519   /* Check for errors */
5520   if (status == HTTP_STATUS_OK)
5521   {
5522     if (tempfile[0])
5523       return(strdup(tempfile));
5524   }
5525   else if (tempfile[0])
5526     unlink(tempfile);
5527   return NULL;
5528 }
5529 
5530 int
record_printer_options(const char * printer)5531 record_printer_options(const char *printer) {
5532   remote_printer_t *p;
5533   char filename[1024];
5534   FILE *fp = NULL;
5535   char uri[HTTP_MAX_URI], *resource;
5536   ipp_t *request, *response;
5537   ipp_attribute_t *attr;
5538   const char *key;
5539   char buf[65536], *c;
5540   char *ppdname = NULL;
5541   ppd_file_t *ppd;
5542   ppd_option_t *ppd_opt;
5543   cups_option_t *option;
5544   int i;
5545   /* List of IPP attributes to get recorded */
5546   static const char *attrs_to_record[] =
5547     {
5548      "*-default",
5549      "auth-info-required",
5550      /*"device-uri",*/
5551      "job-quota-period",
5552      "job-k-limit",
5553      "job-page-limit",
5554      /*"port-monitor",*/
5555      "printer-error-policy",
5556      "printer-info",
5557      "printer-is-accepting-jobs",
5558      "printer-is-shared",
5559      "printer-geo-location",
5560      "printer-location",
5561      "printer-op-policy",
5562      "printer-organization",
5563      "printer-organizational-unit",
5564      /*"printer-state",
5565        "printer-state-message",
5566        "printer-state-reasons",*/
5567      "requesting-user-name-allowed",
5568      "requesting-user-name-denied",
5569      NULL
5570     };
5571   const char **ptr;
5572   http_t *conn = NULL;
5573 
5574   if (printer == NULL || strlen(printer) == 0)
5575     return 0;
5576 
5577   /* Get our data about this printer */
5578   p = printer_record(printer);
5579 
5580   if (p == NULL) {
5581     debug_printf("Not recording printer options for %s: Unknown printer!\n",
5582 		 printer);
5583     return 0;
5584   }
5585 
5586   if (p->status == STATUS_TO_BE_RELEASED) {
5587     debug_printf("Not recording printer options for externally modified printer %s.\n",
5588 		 printer);
5589     return 0;
5590   }
5591 
5592   snprintf(filename, sizeof(filename), save_options_file,
5593 	   printer);
5594 
5595   debug_printf("Recording printer options for %s to %s\n",
5596 	       printer, filename);
5597 
5598   conn = http_connect_local ();
5599   if (conn) {
5600     /* If there is a PPD file for this printer, we save the local
5601        settings for the PPD options. */
5602     if (cups_notifier != NULL || (p && p->netprinter)) {
5603       if ((ppdname = loadPPD(conn, printer)) == NULL) {
5604 	debug_printf("Unable to get PPD file for %s: %s\n",
5605 		     printer, cupsLastErrorString());
5606       } else if ((ppd = ppdOpenFile(ppdname)) == NULL) {
5607 	unlink(ppdname);
5608 	debug_printf("Unable to open PPD file for %s.\n",
5609 		     printer);
5610       } else {
5611 	debug_printf("Recording option settings of the PPD file for %s (%s):\n",
5612 		     printer, ppd->nickname);
5613 	ppdMarkDefaults(ppd);
5614 	for (ppd_opt = ppdFirstOption(ppd); ppd_opt;
5615 	     ppd_opt = ppdNextOption(ppd))
5616 	  if (strcasecmp(ppd_opt->keyword, "PageRegion") != 0) {
5617 	    debug_printf("   %s=%s\n",
5618 			 ppd_opt->keyword, ppd_opt->defchoice);
5619 	    strncpy(buf, ppd_opt->keyword, sizeof(buf));
5620 	    p->num_options = cupsAddOption(buf, ppd_opt->defchoice,
5621 					   p->num_options, &(p->options));
5622 	  }
5623 	ppdClose(ppd);
5624 	unlink(ppdname);
5625       }
5626     }
5627 
5628     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
5629 		     "localhost", 0, "/printers/%s", printer);
5630     resource = uri + (strlen(uri) - strlen(printer) - 10);
5631     request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
5632     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
5633 		 uri);
5634     response = cupsDoRequest(conn, request, resource);
5635 
5636     /* Write all supported printer attributes */
5637     if (response) {
5638       debug_printf("Recording option settings from the IPP attributes for %s:\n",
5639 		   printer);
5640       attr = ippFirstAttribute(response);
5641       while (attr) {
5642 	key = ippGetName(attr);
5643 	for (ptr = attrs_to_record; *ptr; ptr++)
5644 	  if (strcasecmp(key, *ptr) == 0 ||
5645 	      (*ptr[0] == '*' &&
5646 	       strcasecmp(key + strlen(key) - strlen(*ptr) + 1, *ptr + 1) == 0))
5647 	    break;
5648 	if (*ptr != NULL) {
5649 	  if (strcasecmp(key, CUPS_BROWSED_DEST_PRINTER "-default") != 0) {
5650 	    ippAttributeString(attr, buf, sizeof(buf));
5651 	    buf[sizeof(buf) - 1] = '\0';
5652 	    c = buf;
5653 	    while (*c) {
5654 	      if (*c == '\\')
5655 		memmove(c, c + 1, strlen(c));
5656 	      if (*c) c ++;
5657 	    }
5658 	    debug_printf("   %s=%s\n", key, buf);
5659 	    p->num_options = cupsAddOption(key, buf, p->num_options,
5660 					   &(p->options));
5661 	  }
5662 	}
5663 	attr = ippNextAttribute(response);
5664       }
5665       ippDelete(response);
5666     }
5667   } else {
5668     debug_printf("Cannot connect to local CUPS to read out the IPP and PPD attributes for printer %s.\n",
5669 		 printer);
5670   }
5671 
5672   if (ppdname)
5673     free(ppdname);
5674 
5675   if (p->num_options > 0) {
5676     fp = fopen(filename, "w+");
5677     if (fp == NULL) {
5678       debug_printf("ERROR: Failed creating file %s: %s\n",
5679 		   filename, strerror(errno));
5680       return -1;
5681     }
5682 
5683     for (i = p->num_options, option = p->options; i > 0; i --, option ++)
5684       if (fprintf (fp, "%s=%s\n", option->name, option->value) < 0) {
5685 	debug_printf("ERROR: Failed to write into file %s: %s\n",
5686 		     filename, strerror(errno));
5687 	fclose(fp);
5688 	return -1;
5689       }
5690 
5691     fclose(fp);
5692 
5693     return 0;
5694   } else
5695     return -1;
5696 }
5697 
5698 int
load_printer_options(const char * printer,int num_options,cups_option_t ** options)5699 load_printer_options(const char *printer, int num_options,
5700 		     cups_option_t **options) {
5701   char filename[1024];
5702   FILE *fp = NULL;
5703   char *opt = NULL, *val;
5704   size_t optlen = 0;
5705 
5706   if (printer == NULL || strlen(printer) == 0 || options == NULL)
5707     return 0;
5708 
5709   /* Prepare reading file with saved option settings */
5710   snprintf(filename, sizeof(filename), save_options_file,
5711 	   printer);
5712 
5713   debug_printf("Loading saved printer options for %s from %s\n",
5714 	       printer, filename);
5715 
5716   /* Open the file with the saved option settings for this print queue */
5717   fp = fopen(filename, "r");
5718   if (fp == NULL) {
5719     debug_printf("Failed reading file %s, probably no options recorded yet\n",
5720 		 filename);
5721   } else {
5722     /* Now read the lines of the file and add each setting to our request */
5723     errno = 0;
5724     debug_printf("Loading following option settings for printer %s:\n",
5725 		 printer);
5726     while (getline(&opt, &optlen, fp) != -1) {
5727       if (strlen(opt) > 1 && (val = strchr(opt, '=')) != NULL) {
5728 	*val = '\0';
5729 	val ++;
5730 	val[strlen(val)-1] = '\0';
5731 	debug_printf("   %s=%s\n", opt, val);
5732 	num_options = cupsAddOption(opt, val, num_options, options);
5733       }
5734     }
5735     debug_printf("\n");
5736     if (errno != 0)
5737       debug_printf("Failed reading saved options file %s: %s\n",
5738 		   filename, strerror(errno));
5739     free(opt);
5740     fclose(fp);
5741   }
5742   return (num_options);
5743 }
5744 
5745 int
queue_creation_handle_default(const char * printer)5746 queue_creation_handle_default(const char *printer) {
5747   /* No default printer management if we cannot get D-Bus notifications
5748      from CUPS */
5749   if (cups_notifier == NULL)
5750     return 0;
5751   /* If this queue is recorded as the former default queue (and the current
5752      default is local), set it as default (the CUPS notification handler
5753      will record the local default printer then) */
5754   char *recorded_default = retrieve_default_printer(0);
5755   if (recorded_default == NULL || strcasecmp(recorded_default, printer)) {
5756     if (recorded_default) free(recorded_default);
5757     return 0;
5758   }
5759   free(recorded_default);
5760   char *current_default = get_cups_default_printer();
5761   if (current_default == NULL || !is_created_by_cups_browsed(current_default)) {
5762     if (set_cups_default_printer(printer) < 0) {
5763       debug_printf("ERROR: Could not set former default printer %s as default again.\n",
5764 		   printer);
5765       free(current_default);
5766       return -1;
5767     } else {
5768       debug_printf("Former default printer %s re-appeared, set as default again.\n",
5769 		   printer);
5770       invalidate_default_printer(0);
5771     }
5772   }
5773   free(current_default);
5774   return 0;
5775 }
5776 
5777 int
queue_removal_handle_default(const char * printer)5778 queue_removal_handle_default(const char *printer) {
5779   /* No default printer management if we cannot get D-Bus notifications
5780      from CUPS */
5781   if (cups_notifier == NULL)
5782     return 0;
5783   /* If the queue is the default printer, get back
5784      to the recorded local default printer, record this queue for getting the
5785      default set to this queue again if it re-appears. */
5786   /* We call this also if a queue is only conserved because on cups-browsed
5787      shutdown it still has jobs */
5788   if (!is_cups_default_printer(printer))
5789     return 0;
5790   /* Record the fact that this printer was default */
5791   if (record_default_printer(default_printer, 0) < 0) {
5792     /* Delete record file if recording failed */
5793     debug_printf("ERROR: Failed recording remote default printer (%s). Removing the file with possible old recording.\n",
5794 		 printer);
5795     invalidate_default_printer(0);
5796   } else
5797     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",
5798 		 printer);
5799   /* Switch back to a recorded local printer, if available */
5800   char *local_default = retrieve_default_printer(1);
5801   if (local_default != NULL) {
5802     if (set_cups_default_printer(local_default) >= 0) {
5803       debug_printf("Switching back to %s as default printer.\n",
5804 		   local_default);
5805       free(local_default);
5806     } else {
5807       debug_printf("ERROR: Unable to switch back to %s as default printer.\n",
5808 		   local_default);
5809       free(local_default);
5810       return -1;
5811     }
5812   }
5813   invalidate_default_printer(1);
5814   return 0;
5815 }
5816 
5817 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)5818 get_local_queue_name(const char *service_name,
5819 		     const char *make_model,
5820 		     const char *resource,
5821 		     const char *remote_host,
5822 		     int *is_cups_queue,
5823 		     const char *exclude) {
5824   char *queue_name = NULL, *backup_queue_name = NULL,
5825     *local_queue_name = NULL, *local_queue_name_lower = NULL;
5826   local_printer_t *local_printer = NULL;
5827   cluster_t *cluster = NULL;
5828   char *member = NULL, *str = NULL;
5829 
5830   if (*is_cups_queue) {
5831     /* This is a remote CUPS printer */
5832     /* Determine the queue name */
5833     if (LocalQueueNamingRemoteCUPS == LOCAL_QUEUE_NAMING_MAKE_MODEL &&
5834 	make_model)
5835       /* Works only with DNS-SD-discovered queues as otherwise we have no
5836 	 make/model info */
5837       queue_name = remove_bad_chars(make_model, 0);
5838     else if (LocalQueueNamingRemoteCUPS == LOCAL_QUEUE_NAMING_REMOTE_NAME)
5839       /* Not directly used in script generation input later, but taken from
5840 	 packet, so better safe than sorry. (consider second loop with
5841 	 backup_queue_name) */
5842       queue_name = remove_bad_chars(strrchr(resource, '/') + 1, 0);
5843     else
5844       /* Convert DNS-SD service name into a CUPS queue name exactly
5845 	 as CUPS would do it, to override CUPS' own temporary queue
5846 	 generation mechanism */
5847       queue_name = remove_bad_chars(service_name, 2);
5848   } else {
5849     /* This is an IPP-based network printer */
5850     /* Determine the queue name */
5851     if (LocalQueueNamingIPPPrinter == LOCAL_QUEUE_NAMING_MAKE_MODEL &&
5852 	make_model)
5853       /* Works only if we actually have make/model info in the DNS-SD record*/
5854       queue_name = remove_bad_chars(make_model, 0);
5855     else
5856       /* Convert DNS-SD service name into a CUPS queue name exactly
5857 	 as CUPS would do it, to override CUPS' own temporary queue
5858 	 generation mechanism */
5859       queue_name = remove_bad_chars(service_name, 2);
5860   }
5861   /* Check if there exists already a CUPS queue with the
5862      requested name Try name@host in such a case and if
5863      this is also taken, ignore the printer */
5864 
5865   /* Get available CUPS queues */
5866   update_local_printers ();
5867 
5868   /* We skip trying to use the queue name purely derived from the
5869      remote CUPS queue name or make and model for remote CUPS queues
5870      when automatic clustering of remote CUPS queues is turned off,
5871      to directly create queues with names containing the server name
5872      to avoid name clashes and with this remote queues skipped by
5873      cups-browsed. */
5874   if ((!*is_cups_queue ||
5875        AutoClustering == 1 ||
5876        LocalQueueNamingRemoteCUPS == LOCAL_QUEUE_NAMING_DNSSD) &&
5877       (!exclude || strcasecmp(queue_name, exclude))) {
5878     /* Is there a local queue with the name of the remote queue? */
5879     local_queue_name_lower = g_ascii_strdown(queue_name, -1);
5880     local_printer = g_hash_table_lookup (local_printers,
5881 					 local_queue_name_lower);
5882     free(local_queue_name_lower);
5883     /* To decide on whether the queue name is already taken, only
5884        consider CUPS queues not created by us. */
5885     if (local_printer && !local_printer->cups_browsed_controlled) {
5886       debug_printf("Queue name %s already taken.\n",
5887 		   queue_name);
5888       local_queue_name = NULL;
5889     } else
5890       local_queue_name = strdup(queue_name);
5891   }
5892   /* Use the originally chosen queue name plus the server name if the
5893      original name is already taken or if we had skipped using it. Do
5894      this only if we do not use DNS-SD-service-name-based naming. */
5895   if (!local_queue_name &&
5896       (!*is_cups_queue ||
5897        LocalQueueNamingRemoteCUPS != LOCAL_QUEUE_NAMING_DNSSD) &&
5898       (is_cups_queue ||
5899        LocalQueueNamingIPPPrinter != LOCAL_QUEUE_NAMING_DNSSD)) {
5900     if ((backup_queue_name = malloc((strlen(queue_name) +
5901 				     strlen(remote_host) + 2) *
5902 				    sizeof(char))) == NULL) {
5903       debug_printf("ERROR: Unable to allocate memory.\n");
5904       exit(1);
5905     }
5906     sprintf(backup_queue_name, "%s@%s", queue_name, remote_host);
5907     local_queue_name = backup_queue_name;
5908     debug_printf("Using fallback queue name: %s\n",
5909 		 local_queue_name);
5910     /* Is there a local queue with the name <queue>@<host>? */
5911     local_queue_name_lower = g_ascii_strdown(local_queue_name, -1);
5912     local_printer = g_hash_table_lookup (local_printers,
5913 					 local_queue_name_lower);
5914     free(local_queue_name_lower);
5915     if ((local_printer && !local_printer->cups_browsed_controlled) ||
5916 	(exclude && !strcasecmp(local_queue_name, exclude))) {
5917       /* Found also a local queue with name <queue>@<host> (or
5918 	 this name is explicitly excluded), so ignore this remote
5919 	 printer */
5920       debug_printf("%s also taken, printer ignored.\n",
5921 		   local_queue_name);
5922       free(backup_queue_name);
5923       local_queue_name = NULL;
5924     }
5925   }
5926   free(queue_name);
5927   if (!local_queue_name) {
5928     debug_printf("No suitable local queue name found, printer ignored.\n");
5929     return NULL;
5930   }
5931 
5932   /* Check whether our new printer matches one of the user-defined
5933      printer clusters */
5934   for (cluster = cupsArrayFirst(clusters);
5935        cluster;
5936        cluster = cupsArrayNext(clusters)) {
5937     if (exclude && !strcasecmp(cluster->local_queue_name, exclude))
5938       continue;
5939     local_queue_name_lower = g_ascii_strdown(cluster->local_queue_name, -1);
5940     local_printer = g_hash_table_lookup (local_printers,
5941 					 local_queue_name_lower);
5942     free(local_queue_name_lower);
5943     if (local_printer && !local_printer->cups_browsed_controlled)
5944       continue;
5945     for (member = cupsArrayFirst(cluster->members);
5946 	 member;
5947 	 member = cupsArrayNext(cluster->members)) {
5948       /* Match remote CUPS queue name */
5949       if ((str = strrchr(resource, '/')) != NULL && strlen(str) > 1) {
5950 	str = remove_bad_chars(str + 1, 2);
5951 	if (strcasecmp(member, str) == 0) /* Match */
5952 	  break;
5953 	free(str);
5954       }
5955       /* Match make and model */
5956       if (make_model) {
5957 	str = remove_bad_chars(make_model, 2);
5958 	if (strcasecmp(member, str) == 0) /* Match */
5959 	  break;
5960 	free(str);
5961       }
5962       /* Match DNS-SD service name */
5963       if (service_name) {
5964 	str = remove_bad_chars(service_name, 2);
5965 	if (strcasecmp(member, str) == 0) /* Match */
5966 	  break;
5967 	free(str);
5968       }
5969     }
5970     if (member)
5971       break;
5972   }
5973   if (cluster) {
5974     local_queue_name = strdup(cluster->local_queue_name);
5975     *is_cups_queue = 2;
5976     free(str);
5977   } else if (AutoClustering) {
5978     /* If we do automatic clustering by matching queue names, do not
5979        add a queue to a manually defined cluster because it matches
5980        the cluster's local queue name. Manually defined clusters can
5981        only be joined by printers which match one of the cluster's
5982        member names */
5983     for (cluster = cupsArrayFirst(clusters);
5984 	 cluster;
5985 	 cluster = cupsArrayNext(clusters)) {
5986       if (strcasecmp(local_queue_name, cluster->local_queue_name) == 0) {
5987 	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",
5988 		     local_queue_name);
5989 	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");
5990 	free(local_queue_name);
5991 	return NULL;
5992       }
5993     }
5994   }
5995   return local_queue_name;
5996 }
5997 
5998 int
join_cluster_if_needed(remote_printer_t * p,int is_cups_queue)5999 join_cluster_if_needed(remote_printer_t *p,
6000 		       int is_cups_queue) {
6001 
6002   /* is_cups_queue: -1: Unknown, 0: IPP printer, 1: Remote CUPS queue,
6003      2: Remote CUPS queue in user-defined cluster      */
6004 
6005   remote_printer_t *q;
6006 
6007   for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
6008        q;
6009        q = (remote_printer_t *)cupsArrayNext(remote_printers))
6010     if (q != p &&
6011 	!strcasecmp(q->queue_name, p->queue_name) && /* Queue with same name
6012 							on server */
6013 	!q->slave_of) /* Find the master of the queues with this name,
6014 			 to avoid "daisy chaining" */
6015       break;
6016   if (q && AutoClustering == 0 && (is_cups_queue == 1 ||is_cups_queue == 0) ) {
6017     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);
6018     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");
6019     return -1;
6020   }
6021 
6022   p->slave_of = (q && q->status != STATUS_DISAPPEARED &&
6023 		 q->status != STATUS_UNCONFIRMED &&
6024 		 q->status != STATUS_TO_BE_RELEASED) ? q : NULL;
6025   if (p->slave_of) {
6026     debug_printf("Printer %s already available through host %s, port %d.\n",
6027 		 p->queue_name, q->host, q->port);
6028     /* Update q */
6029     q->status = STATUS_TO_BE_CREATED;
6030     q->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
6031     log_cluster(p);
6032   } else if (q) {
6033     q->slave_of = p;
6034     debug_printf("Unconfirmed/disappeared printer %s already available through host %s, port %d, marking that printer a slave of the newly found one.\n",
6035 		 p->queue_name, q->host, q->port);
6036     log_cluster(p);
6037   }
6038   return (q ? 1 : 0);
6039 }
6040 
6041 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)6042 on_printer_state_changed (CupsNotifier *object,
6043                           const gchar *text,
6044                           const gchar *printer_uri,
6045                           const gchar *printer,
6046                           guint printer_state,
6047                           const gchar *printer_state_reasons,
6048                           gboolean printer_is_accepting_jobs,
6049                           gpointer user_data)
6050 {
6051   char *ptr, buf[2048];
6052 
6053   debug_printf("on_printer_state_changed() in THREAD %ld\n", pthread_self());
6054 
6055   debug_printf("[CUPS Notification] Printer state change on printer %s: %s\n",
6056 	       printer, text);
6057   debug_printf("[CUPS Notification] Printer state reasons: %s\n",
6058 	       printer_state_reasons);
6059 
6060   if (terminating) {
6061     debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n");
6062     return;
6063   }
6064 
6065   if (autoshutdown && autoshutdown_on == NO_JOBS) {
6066     if (check_jobs() == 0) {
6067       /* If auto shutdown is active for triggering on no jobs being left, we
6068 	 schedule the shutdown in autoshutdown_timeout seconds */
6069       if (!autoshutdown_exec_id) {
6070 	debug_printf ("No jobs there any more on printers made available by us, shutting down in %d sec...\n",
6071 		      autoshutdown_timeout);
6072 	autoshutdown_exec_id =
6073 	  g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
6074 				 NULL);
6075       }
6076     } else {
6077       /* If auto shutdown is active for triggering on no jobs being left, we
6078 	 cancel a shutdown in autoshutdown_timeout seconds as there are jobs
6079          again. */
6080       if (autoshutdown_exec_id) {
6081 	debug_printf ("New jobs there on the printers made available by us, killing auto shutdown timer.\n");
6082 	g_source_remove(autoshutdown_exec_id);
6083 	autoshutdown_exec_id = 0;
6084       }
6085     }
6086   }
6087 
6088   if ((ptr = strstr(text, " is now the default printer")) != NULL) {
6089     /* Default printer has changed, we are triggered by the new default
6090        printer */
6091     strncpy(buf, text, ptr - text);
6092     buf[ptr - text] = '\0';
6093     debug_printf("[CUPS Notification] Default printer changed from %s to %s.\n",
6094 		 default_printer, buf);
6095     if (is_created_by_cups_browsed(default_printer)) {
6096       /* Previous default printer created by cups-browsed */
6097       if (!is_created_by_cups_browsed(buf)) {
6098 	/* New default printer local */
6099 	/* Removed backed-up local default printer as we do not have a
6100 	   remote printer as default any more */
6101 	invalidate_default_printer(1);
6102 	debug_printf("Manually switched default printer from a cups-browsed-generated one to a local printer.\n");
6103       }
6104     } else {
6105       /* Previous default printer local */
6106       if (is_created_by_cups_browsed(buf)) {
6107 	/* New default printer created by cups-browsed */
6108 	/* Back up the local default printer to be able to return to it
6109 	   if the remote printer disappears */
6110 	if (record_default_printer(default_printer, 1) < 0) {
6111 	  /* Delete record file if recording failed */
6112 	  debug_printf("ERROR: Failed recording local default printer. Removing the file with possible old recording.\n");
6113 	  invalidate_default_printer(1);
6114 	} else
6115 	  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");
6116 	/* Remove a recorded remote printer as after manually selecting
6117 	   another one as default this one is not relevant any more */
6118 	invalidate_default_printer(0);
6119       }
6120     }
6121     if (default_printer != NULL)
6122       free((void *)default_printer);
6123     default_printer = strdup(buf);
6124   } else if ((ptr = strstr(text, " is no longer the default printer"))
6125 	     != NULL) {
6126     /* Default printer has changed, we are triggered by the former default
6127        printer */
6128     strncpy(buf, text, ptr - text);
6129     buf[ptr - text] = '\0';
6130     debug_printf("[CUPS Notification] %s not default printer any more.\n", buf);
6131   }
6132 }
6133 
6134 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)6135 on_job_state (CupsNotifier *object,
6136 	      const gchar *text,
6137 	      const gchar *printer_uri,
6138 	      const gchar *printer,
6139 	      guint printer_state,
6140 	      const gchar *printer_state_reasons,
6141 	      gboolean printer_is_accepting_jobs,
6142 	      guint job_id,
6143 	      guint job_state,
6144 	      const gchar *job_state_reasons,
6145 	      const gchar *job_name,
6146 	      guint job_impressions_completed,
6147 	      gpointer user_data)
6148 {
6149   int i, count;
6150   char buf[2048];
6151   remote_printer_t *p, *q, *r, *s=NULL;
6152   http_t *http = NULL;
6153   ipp_t *request, *response, *printer_attributes = NULL;
6154   ipp_attribute_t *attr;
6155   const char *pname = NULL;
6156   ipp_pstate_t pstate = IPP_PRINTER_IDLE;
6157   int paccept = 0;
6158   int num_jobs, min_jobs = 99999999;
6159   char destination_uri[1024];
6160   const char *dest_host = NULL;
6161   int dest_index = 0;
6162   int valid_dest_found = 0;
6163   char uri[HTTP_MAX_URI];
6164   int num_options;
6165   cups_option_t *options;
6166   int num_of_printers;
6167   char* document_format;
6168   int  print_quality = 0;
6169   const char *pdl = NULL;
6170   cups_array_t *pdl_list;
6171   char         resolution[32];
6172   res_t        *max_res = NULL, *min_res = NULL, *res = NULL;
6173   int          xres, yres;
6174   int          got_printer_info;
6175   static const char *pattrs[] =
6176     {
6177      "printer-name",
6178      "printer-state",
6179      "printer-is-accepting-jobs"
6180     };
6181   http_t *conn = NULL;
6182 
6183   debug_printf("on_job_state() in THREAD %ld\n", pthread_self());
6184 
6185   debug_printf("[CUPS Notification] Job state changed on printer %s: %s\n",
6186 	       printer, text);
6187   debug_printf("[CUPS Notification] Printer state reasons: %s\n",
6188 	       printer_state_reasons);
6189   debug_printf("[CUPS Notification] Job ID: %d\n",
6190 	       job_id);
6191   debug_printf("[CUPS Notification] Job State: %s\n",
6192 	       job_state_reasons);
6193   debug_printf("[CUPS Notification] Job is processing: %s\n",
6194 	       job_state == IPP_JOB_PROCESSING ? "Yes" : "No");
6195 
6196   if (terminating) {
6197     debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n");
6198     return;
6199   }
6200 
6201   if (autoshutdown && autoshutdown_on == NO_JOBS) {
6202     if (check_jobs() == 0) {
6203       /* If auto shutdown is active for triggering on no jobs being left, we
6204 	 schedule the shutdown in autoshutdown_timeout seconds */
6205       if (!autoshutdown_exec_id) {
6206 	debug_printf ("No jobs there any more on printers made available by us, shutting down in %d sec...\n", autoshutdown_timeout);
6207 	autoshutdown_exec_id =
6208 	  g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
6209 				 NULL);
6210       }
6211     } else {
6212       /* If auto shutdown is active for triggering on no jobs being left, we
6213 	 cancel a shutdown in autoshutdown_timeout seconds as there are jobs
6214 	 again. */
6215       if (autoshutdown_exec_id) {
6216 	debug_printf ("New jobs there on the printers made available by us, killing auto shutdown timer.\n");
6217 	g_source_remove(autoshutdown_exec_id);
6218 	autoshutdown_exec_id = 0;
6219       }
6220     }
6221   }
6222 
6223   if (job_id != 0 && job_state == IPP_JOB_PROCESSING) {
6224     /* Printer started processing a job, check if it uses the implicitclass
6225        backend and if so, we select the remote queue to which to send the job
6226        in a way so that we get load balancing between all remote queues
6227        associated with this queue.
6228 
6229        There are two methods to do that (configurable in cups-browsed.conf):
6230 
6231        Queuing of jobs on the client (LoadBalancingType = QUEUE_ON_CLIENT):
6232 
6233        Here we check all remote printers assigned to this printer and to its
6234        slaves which is currently accepting jobs and idle. If all are busy,
6235        we send a failure message and the backend will close with an error code
6236        after some seconds of delay, to make the job getting retried making us
6237        checking again here. If we find a destination, we tell the backend
6238        which remote queue this destination is, making the backend printing the
6239        job there immediately.
6240 
6241        With this all waiting jobs get queued up on the client, on the servers
6242        there will only be the jobs which are actually printing, as we do not
6243        send jobs to a server which is already printing. This is also the
6244        method which CUPS uses for classes. Advantage is a more even
6245        distribution of the job workload on the servers, and if a server fails,
6246        there are not several jobs stuck or lost. Disadvantage is that if one
6247        takes the client (laptop, mobile phone, ...) out of the local network,
6248        printing stops with the jobs waiting in the local queue.
6249 
6250        Queuing of jobs on the servers (LoadBalancingType = QUEUE_ON_SERVERS):
6251 
6252        Here we check all remote printers assigned to this printer and to its
6253        slaves which is currently accepting jobs and find the one with the
6254        lowest amount of jobs waiting and send the job to there. So on the
6255        local queue we have never jobs waiting if at least one remote printer
6256        accepts jobs.
6257 
6258        Not having jobs waiting locally has the advantage that we can take the
6259        local machine from the network and all jobs get printed. Disadvantage
6260        is that if a server with a full queue of jobs goes away, the jobs go
6261        away, too.
6262 
6263        Default is queuing the jobs on the client as this is what CUPS does
6264        with classes. */
6265 
6266     debug_printf("[CUPS Notification] %s starts processing a job.\n", printer);
6267     conn = http_connect_local ();
6268     if (conn == NULL) {
6269       debug_printf("Cannot connect to local CUPS to set destination for job in the load-balanced cluster %s.\n",
6270 		   printer);
6271       return;
6272     }
6273     q = printer_record(printer);
6274     /* If we hit a slave and not the master, switch to the master */
6275     if (q && q->slave_of)
6276       q = q->slave_of;
6277     if (q && q->queue_name) {
6278       /* We have remote CUPS queue(s) and so are using the implicitclass
6279 	 backend */
6280       debug_printf("[CUPS Notification] %s is using the \"implicitclass\" CUPS backend, so let us search for a destination for this job.\n", printer);
6281 
6282       /* We keep track of the printer which we used last time and start
6283 	 checking with the next printer this time, to get a "round robin"
6284 	 type of printer usage instead of having most jobs going to the first
6285 	 printer in the list. Method taken from the cupsdFindAvailablePrinter()
6286 	 function of the scheduler/classes.c file of CUPS. */
6287 
6288       if (q->last_printer < 0 ||
6289 	  q->last_printer >= cupsArrayCount(remote_printers))
6290 	q->last_printer = 0;
6291       log_cluster(q);
6292       for (i = q->last_printer + 1; ; i++) {
6293 	if (i >= cupsArrayCount(remote_printers))
6294 	  i = 0;
6295 	p = (remote_printer_t *)cupsArrayIndex(remote_printers, i);
6296 	if (!strcasecmp(p->queue_name, printer) &&
6297 	    p->status == STATUS_CONFIRMED) {
6298 	  num_of_printers = 0;
6299 	  for (r = (remote_printer_t *)cupsArrayFirst(remote_printers);
6300 	       r; r = (remote_printer_t *)cupsArrayNext(remote_printers)) {
6301 	    if (!strcmp(r->queue_name, q->queue_name)) {
6302 	      if(r->status == STATUS_DISAPPEARED ||
6303 		 r->status == STATUS_UNCONFIRMED ||
6304 		 r->status == STATUS_TO_BE_RELEASED )
6305 		continue;
6306 	      num_of_printers ++;
6307 	    }
6308 	  }
6309 
6310 	  /* If we are in a cluster, see whether the printer supports the
6311 	     requested job attributes*/
6312 	  if (num_of_printers > 1) {
6313 	    if (!supports_job_attributes_requested(printer, i, job_id,
6314 						   &print_quality)) {
6315 	      debug_printf("Printer with uri %s in cluster %s doesn't support the requested job attributes\n",
6316 			   p->uri, p->queue_name);
6317 	      if (i == q->last_printer)
6318 		break;
6319 	      else
6320 		continue;
6321 	    }
6322 	  }
6323 	  debug_printf("Checking state of remote printer %s on host %s, IP %s, port %d.\n",
6324 		       p->uri, p->host, p->ip, p->port);
6325 
6326 	  /* Check whether the printer is idle, processing, or disabled */
6327 	  debug_printf("HTTP connection to %s:%d established.\n", p->host,
6328 		       p->port);
6329 	  response = get_printer_attributes(p->uri, pattrs,
6330 					    sizeof(pattrs) / sizeof(pattrs[0]),
6331 					    NULL, 0, 0);
6332 	  debug_log_out(get_printer_attributes_log);
6333 	  if (response != NULL) {
6334 	    debug_printf("IPP request to %s:%d successful.\n", p->host,
6335 			 p->port);
6336 	    pname = NULL;
6337 	    pstate = IPP_PRINTER_IDLE;
6338 	    paccept = 0;
6339 	    for (attr = ippFirstAttribute(response); attr != NULL;
6340 		 attr = ippNextAttribute(response)) {
6341 	      while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
6342 		attr = ippNextAttribute(response);
6343 	      if (attr == NULL)
6344 		break;
6345 	      pname = NULL;
6346 	      pstate = IPP_PRINTER_IDLE;
6347 	      paccept = 0;
6348 	      got_printer_info = 0;
6349 	      while (attr != NULL && ippGetGroupTag(attr) ==
6350 		     IPP_TAG_PRINTER) {
6351 		if (!strcmp(ippGetName(attr), "printer-name") &&
6352 		    ippGetValueTag(attr) == IPP_TAG_NAME) {
6353 		  pname = ippGetString(attr, 0, NULL);
6354 		} else if (!strcmp(ippGetName(attr), "printer-state") &&
6355 			   ippGetValueTag(attr) == IPP_TAG_ENUM)
6356 		  pstate = (ipp_pstate_t)ippGetInteger(attr, 0);
6357 		else if (!strcmp(ippGetName(attr),
6358 				 "printer-is-accepting-jobs") &&
6359 			 ippGetValueTag(attr) == IPP_TAG_BOOLEAN) {
6360 		  paccept = ippGetBoolean(attr, 0);
6361 		  got_printer_info = 1;
6362 		}
6363 		attr = ippNextAttribute(response);
6364 	      }
6365 	      if (got_printer_info == 0) {
6366 		if (attr == NULL)
6367 		  break;
6368 		else
6369 		  continue;
6370 	      }
6371 	      debug_printf("IPP Response contains attributes values printer-name %s, accepting-job %d\n",
6372 			   (pname ? pname : "(Not reported)"), paccept);
6373 	      if (paccept) {
6374 		debug_printf("Printer %s on host %s, port %d is accepting jobs.\n",
6375 			     p->uri, p->host, p->port);
6376 		switch (pstate) {
6377 		case IPP_PRINTER_IDLE:
6378 		  valid_dest_found = 1;
6379 		  dest_host = p->ip ? p->ip : p->host;
6380 		  strncpy(destination_uri, p->uri, sizeof(destination_uri) - 1);
6381 		  printer_attributes = p->prattrs;
6382 		  pdl = p->pdl;
6383 		  s = p;
6384 		  dest_index = i;
6385 		  debug_printf("Printer %s on host %s, port %d is idle, take this as destination and stop searching.\n",
6386 			       p->uri, p->host, p->port);
6387 		  break;
6388 		case IPP_PRINTER_PROCESSING:
6389 		  valid_dest_found = 1;
6390 		  if (LoadBalancingType == QUEUE_ON_SERVERS) {
6391 		    num_jobs = 0;
6392 		    http =
6393 		      httpConnectEncryptShortTimeout (p->ip ? p->ip : p->host,
6394 						      p->port,
6395 						     HTTP_ENCRYPT_IF_REQUESTED);
6396 		    if (http) {
6397 		      num_jobs = get_number_of_jobs(http, p->uri, 0,
6398 						    CUPS_WHICHJOBS_ACTIVE);
6399 		      if (num_jobs >= 0 && num_jobs < min_jobs) {
6400 			min_jobs = num_jobs;
6401 			dest_host = p->ip ? p->ip : p->host;
6402 			strncpy(destination_uri, p->uri,
6403 				sizeof(destination_uri) - 1);
6404 			printer_attributes = p->prattrs;
6405 			pdl = p->pdl;
6406 			s = p;
6407 			dest_index = i;
6408 		      }
6409 		      debug_printf("Printer %s on host %s, port %d is printing and it has %d jobs.\n",
6410 				   p->uri, p->host, p->port,
6411 				   num_jobs);
6412 		      httpClose(http);
6413 		      http = NULL;
6414 		    }
6415 		  } else
6416 		    debug_printf("Printer %s on host %s, port %d is printing.\n",
6417 				 p->uri, p->host, p->port);
6418 		  break;
6419 		case IPP_PRINTER_STOPPED:
6420 		  debug_printf("Printer %s on host %s, port %d is disabled, skip it.\n",
6421 			       p->uri, p->host, p->port);
6422 		  break;
6423 		}
6424 	      } else {
6425 		debug_printf("Printer %s on host %s, port %d is not accepting jobs, skip it.\n",
6426 			     p->uri, p->host, p->port);
6427 	      }
6428 	      break;
6429 	    }
6430 
6431 	    ippDelete(response);
6432 	    response = NULL;
6433 
6434 	    if (pstate == IPP_PRINTER_IDLE && paccept) {
6435 	      q->last_printer = i;
6436 	      break;
6437 	    }
6438 	  } else
6439 	    debug_printf("IPP request to %s:%d failed.\n", p->host,
6440 			 p->port);
6441 	}
6442 	if (i == q->last_printer)
6443 	  break;
6444       }
6445 
6446       /* Write the selected destination host into an option of our implicit
6447 	 class queue (cups-browsed-dest-printer="<dest>") so that the
6448 	 implicitclass backend will pick it up */
6449 
6450       if ((pdl_list = cupsArrayNew3((cups_array_func_t)strcasecmp,
6451 				    NULL, NULL, 0,
6452 				    (cups_acopy_func_t)strdup,
6453 				    (cups_afree_func_t)free)) == NULL){
6454 	debug_printf("Could Not allocate memory for cups Array \n");
6455 	return;
6456       }
6457 
6458       /* Finding the best pdl supported by the printer, we need to send the
6459 	 document format to the implictclass backend */
6460       if (((attr = ippFindAttribute(printer_attributes,
6461 				    "document-format-supported",
6462 				    IPP_TAG_MIMETYPE)) != NULL) ||
6463 	  (pdl && pdl[0] != '\0')) {
6464 	const char *format = pdl;
6465 	i = 0;
6466 	count = ippGetCount(attr);
6467 	while ((attr && i < count) || /* Go through formats in attribute */
6468 	       (!attr && pdl && pdl[0] != '\0' && format[0] != '\0')) {
6469 	  /* Go through formats in pdl string (from DNS-SD record) */
6470 	  /* Pick next format from attribute */
6471           if (attr) format = ippGetString(attr, i, NULL);
6472           /* Add format to list of supported PDLs, skip duplicates */
6473           if (!cupsArrayFind(pdl_list, (void *)format))
6474             cupsArrayAdd(pdl_list, (void *)format);
6475           if (attr)
6476 	    /* Next format in attribute */
6477 	    i ++;
6478 	  else {
6479 	    /* Find the next format in the string pdl, if there is none left,
6480 	       go to the terminating zero */
6481             while (!isspace(*format) && *format != ',' && *format != '\0')
6482               format ++;
6483             while ((isspace(*format) || *format == ',') && *format != '\0')
6484               format ++;
6485 	  }
6486         }
6487       }
6488 
6489       /* The priority order for the PDLs is the same as in the
6490 	 PPD generator in cupsfilters/ppdgenerator.c */
6491       document_format = (char *)malloc(sizeof(char) * 32);
6492       if (cupsArrayFind(pdl_list, "application/vnd.cups-pdf") ||
6493 	  cupsArrayFind(pdl_list, "application/pdf"))
6494 	strcpy(document_format, "pdf");
6495 #ifdef CUPS_RASTER_HAVE_APPLERASTER
6496       else if (cupsArrayFind(pdl_list, "image/urf"))
6497 	strcpy(document_format, "apple-raster");
6498 #endif
6499       else if (cupsArrayFind(pdl_list, "image/pwg-raster"))
6500 	strcpy(document_format, "raster");
6501 #ifdef QPDF_HAVE_PCLM
6502       else if (cupsArrayFind(pdl_list, "application/PCLm"))
6503 	strcpy(document_format, "pclm");
6504 #endif
6505       else if (cupsArrayFind(pdl_list, "application/vnd.hp-pclxl"))
6506 	strcpy(document_format, "pclxl");
6507       else if (cupsArrayFind(pdl_list, "application/vnd.cups-postscript") ||
6508 	       cupsArrayFind(pdl_list, "application/postscript"))
6509 	strcpy(document_format, "postscript");
6510       else if (cupsArrayFind(pdl_list, "application/vnd.hp-pcl") ||
6511 	       cupsArrayFind(pdl_list, "application/pcl") ||
6512 	       cupsArrayFind(pdl_list, "application/x-pcl"))
6513 	strcpy(document_format, "pcl");
6514 
6515       if (pdl_list)
6516         cupsArrayDelete(pdl_list);
6517 
6518       /* Deciding the resolution to be sent with the job */
6519       /* Finding the minimum and maximum resolution supported by the printer */
6520 
6521       max_res = resolutionNew(0, 0);
6522       min_res = resolutionNew(0, 0);
6523 
6524       if (s &&
6525 	  ((attr = ippFindAttribute(s->prattrs, "printer-resolution-supported",
6526 				    IPP_TAG_RESOLUTION)) != NULL)) {
6527 	for (i = 0, count = ippGetCount(attr); i < count; i ++) {
6528 	  if ((res = ippResolutionToRes(attr, i)) != NULL) {
6529 	    debug_printf("%d %d\n",res->x,res->y);
6530 	    if (i == 0) {
6531 	      max_res->x = res->x;
6532 	      max_res->y = res->y;
6533 	      min_res->x = res->x;
6534 	      min_res->y = res->y;
6535 	    } else {
6536 	      if(compare_resolutions((void *)res,(void *)max_res,NULL) > 0) {
6537 		max_res->x = res->x;
6538 		max_res->y = res->y;
6539 	      }
6540 	      if(compare_resolutions((void *)res,(void *)min_res,NULL) < 0) {
6541 		min_res->x = res->x;
6542 		min_res->y = res->y;
6543 	      }
6544 	    }
6545 	    free_resolution(res, NULL);
6546 	    res = NULL;
6547 	  }
6548 	}
6549       }
6550 
6551       /* If we are requesting normal print quality then send default
6552 	 resolution, for draft send minimum resolution and for high,
6553 	 send the maximum resolution */
6554       /* If none of the below dpi is selected then default dpi will be
6555 	 sent as 600 */
6556       snprintf(resolution,sizeof(resolution), "600dpi");
6557       if (s && print_quality == 3) {
6558 	if (min_res != NULL){
6559 	  if (min_res->x == min_res->y)
6560 	    snprintf(resolution,sizeof(resolution), "%ddpi", min_res->x);
6561 	  else
6562 	    snprintf(resolution,sizeof(resolution), "%dx%ddpi", min_res->x,
6563 		     min_res->y);
6564 	}
6565       } else if (s && print_quality == 5) {
6566 	if (max_res != NULL) {
6567 	  if (max_res->x == max_res->y)
6568 	    snprintf(resolution, sizeof(resolution), "%ddpi", max_res->x);
6569 	  else
6570 	    snprintf(resolution, sizeof(resolution), "%dx%ddpi", max_res->x,
6571 		     max_res->y);
6572 	}
6573       } else if (s) {
6574 	if ((attr = ippFindAttribute(s->prattrs, "printer-resolution-default",
6575 				     IPP_TAG_ZERO)) != NULL) {
6576 	  if ((res = ippResolutionToRes(attr, 0)) != NULL) {
6577 	    xres = res->x;
6578 	    yres = res->y;
6579 	    if (xres == yres)
6580 	      snprintf(resolution, sizeof(resolution), "%ddpi", xres);
6581 	    else
6582 	      snprintf(resolution, sizeof(resolution), "%dx%ddpi", xres, yres);
6583 	    free_resolution(res, NULL);
6584 	  }
6585 	}
6586       }
6587 
6588       free_resolution(max_res, NULL);
6589       free_resolution(min_res, NULL);
6590 
6591       request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
6592       httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
6593 		       "localhost", 0, "/printers/%s", printer);
6594       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
6595 		   "printer-uri", NULL, uri);
6596       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
6597 		   "requesting-user-name", NULL, cupsUser());
6598       if (dest_host) {
6599 	q->last_printer = dest_index;
6600 	snprintf(buf, sizeof(buf), "\"%d %s %s %s\"", job_id, destination_uri,
6601 		 document_format, resolution);
6602 	debug_printf("Destination for job %d to %s: %s\n",
6603 		     job_id, printer, destination_uri);
6604       } else if (valid_dest_found == 1) {
6605 	snprintf(buf, sizeof(buf), "\"%d ALL_DESTS_BUSY\"", job_id);
6606 	debug_printf("All destinations busy for job %d to %s\n",
6607 		     job_id, printer);
6608       } else {
6609 	snprintf(buf, sizeof(buf), "\"%d NO_DEST_FOUND\"", job_id);
6610 	debug_printf("No destination found for job %d to %s\n",
6611 		     job_id, printer);
6612       }
6613       num_options = 0;
6614       options = NULL;
6615       num_options = cupsAddOption(CUPS_BROWSED_DEST_PRINTER "-default", buf,
6616 				  num_options, &options);
6617       cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
6618       cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
6619       ippDelete(cupsDoRequest(conn, request, "/admin/"));
6620 
6621       cupsFreeOptions(num_options, options);
6622       free(document_format);
6623 
6624       if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
6625 	debug_printf("ERROR: Unable to set \"" CUPS_BROWSED_DEST_PRINTER
6626 		     "-default\" option to communicate the destination server for this job (%s)!\n",
6627 		     cupsLastErrorString());
6628 	return;
6629       }
6630     }
6631   }
6632 }
6633 
6634 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)6635 on_printer_deleted (CupsNotifier *object,
6636 		    const gchar *text,
6637 		    const gchar *printer_uri,
6638 		    const gchar *printer,
6639 		    guint printer_state,
6640 		    const gchar *printer_state_reasons,
6641 		    gboolean printer_is_accepting_jobs,
6642 		    gpointer user_data)
6643 {
6644   remote_printer_t *p;
6645   char *r;
6646   char *local_queue_name_lower = NULL;
6647   local_printer_t *local_printer = NULL;
6648 
6649   debug_printf("on_printer_deleted() in THREAD %ld\n", pthread_self());
6650 
6651   debug_printf("[CUPS Notification] Printer deleted: %s\n",
6652 	       text);
6653 
6654   if (terminating) {
6655     debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n");
6656     return;
6657   }
6658 
6659   if (is_created_by_cups_browsed(printer)) {
6660     /* Get available CUPS queues to check whether the queue did not
6661        already get re-created */
6662     update_local_printers ();
6663     /* Look up print queue in the list */
6664     local_queue_name_lower = g_ascii_strdown(printer, -1);
6665     local_printer = g_hash_table_lookup (local_printers,
6666 					 local_queue_name_lower);
6667     free(local_queue_name_lower);
6668     /* If the queue is there again, do not re-create it */
6669     if (local_printer) {
6670       debug_printf("Printer %s already re-created.\n",
6671 		   printer);
6672       return;
6673     }
6674 
6675     /* a cups-browsed-generated printer got deleted, re-create it */
6676     debug_printf("Printer %s got deleted, re-creating it.\n",
6677 		 printer);
6678     /* If the deleted printer was the default printer, make sure it gets the
6679        default printer again */
6680     if (default_printer && !strcasecmp(printer, default_printer)) {
6681       if (record_default_printer(printer, 0) < 0) {
6682 	/* Delete record file if recording failed */
6683 	debug_printf("ERROR: Failed recording remote default printer. Removing the file with possible old recording.\n");
6684 	invalidate_default_printer(0);
6685       } else
6686 	debug_printf("Recorded %s as remote default printer so that it gets set as default after re-creating.\n",
6687 		     printer);
6688       /* Make sure that a recorded local default printer does not get lost
6689 	 during the recovery operation */
6690       if ((r = retrieve_default_printer(1)) != NULL) {
6691 	if (default_printer != NULL)
6692 	  free((void *)default_printer);
6693 	default_printer = r;
6694       }
6695     }
6696     /* Schedule for immediate creation of the CUPS queue */
6697     p = printer_record(printer);
6698     if (p && p->status != STATUS_DISAPPEARED &&
6699 	p->status != STATUS_UNCONFIRMED &&
6700 	p->status != STATUS_TO_BE_RELEASED) {
6701       p->status = STATUS_TO_BE_CREATED;
6702       p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
6703       if (in_shutdown == 0)
6704 	recheck_timer();
6705     }
6706   }
6707 }
6708 
6709 static int
queue_overwritten(remote_printer_t * p)6710 queue_overwritten (remote_printer_t *p)
6711 {
6712   http_t        *conn = NULL;
6713   ipp_t         *response = NULL;       /* IPP Response */
6714   ipp_attribute_t *attr;                /* Current attribute */
6715   const char    *printername,           /* Print queue name */
6716                 *uri,                   /* Printer URI */
6717                 *device,                /* Printer device URI */
6718                 *makemodel;             /* Printer make and model
6719                                            (equals PPD NickName) */
6720   char          local_queue_uri[1024];
6721   static const char *pattrs[] =        /* Attributes we need for printers... */
6722                 {
6723                   "printer-name",
6724                   "printer-uri-supported",
6725                   "device-uri",
6726                   "printer-make-and-model"
6727                 };
6728   int           overwritten = 0;
6729 
6730   if (p->overwritten)
6731     /* We already have discovered that this queue got overwritten
6732        so we do not repeat the tests and exit positively */
6733     return 1;
6734 
6735   if (p->uri[0] == '\0')
6736     /* Also skip unconfirmed printer entries from queues of the
6737        previous session, they do not have a PPD file registered, so we
6738        cannot compare */
6739     return 0;
6740 
6741   /* Get the device URI which our CUPS queue actually has now, a
6742      change of the URI means a modification or replacement of the
6743      print queue by something user-defined. So we schedule this queue
6744      for release from handling by cups-browsed.
6745 
6746      In a second step get the NickName of the PPD which our CUPS queue
6747      actually uses now, a change of the NickName means a replacement
6748      of the PPD of the print queue by a user-selected one. So we
6749      schedule this queue for release from handling by cups-browsed
6750      also in this case.
6751 
6752      We only need the NickName from the PPD and due to the fact that
6753      the cupsGetPPD2() function does not work when CUPS is on a
6754      non-standard port (!= 631, Bug!) and the NickName is also in the
6755      get-printer-attributes IPP response as "printer-make-and-model",
6756      we go the IPP way here and do not download the printer's PPD. */
6757 
6758   conn = http_connect_local ();
6759   if (conn == NULL) {
6760     debug_printf("Cannot connect to local CUPS to see whether queue %s got overwritten.\n",
6761 		 p->queue_name);
6762     return 0;
6763   }
6764 
6765   /* URI of the local CUPS queue (not the device URI */
6766   httpAssembleURIf(HTTP_URI_CODING_ALL, local_queue_uri,
6767 		   sizeof(local_queue_uri),
6768 		   "ipp", NULL, "localhost", 0,
6769 		   "/printers/%s", p->queue_name);
6770   response = get_printer_attributes2(conn, local_queue_uri,
6771 				     pattrs, sizeof(pattrs) / sizeof(pattrs[0]),
6772 				     pattrs, sizeof(pattrs) / sizeof(pattrs[0]),
6773 				     1);
6774   debug_log_out(get_printer_attributes_log);
6775   if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING) {
6776     debug_printf("lpstat: %s\n", cupsLastErrorString());
6777   } else {
6778     printername = NULL;
6779     device      = NULL;
6780     uri         = NULL;
6781     makemodel   = NULL;
6782     for (attr = ippFirstAttribute(response); attr != NULL;
6783 	 attr = ippNextAttribute(response)) {
6784       if (!strcmp(ippGetName(attr), "printer-name") &&
6785 	  ippGetValueTag(attr) == IPP_TAG_NAME)
6786 	printername = ippGetString(attr, 0, NULL);
6787       if (!strcmp(ippGetName(attr), "printer-uri-supported") &&
6788 	  ippGetValueTag(attr) == IPP_TAG_URI)
6789 	uri = ippGetString(attr, 0, NULL);
6790       if (!strcmp(ippGetName(attr), "device-uri") &&
6791 	  ippGetValueTag(attr) == IPP_TAG_URI)
6792 	device = ippGetString(attr, 0, NULL);
6793       if (!strcmp(ippGetName(attr), "printer-make-and-model") &&
6794 	  ippGetValueTag(attr) == IPP_TAG_TEXT)
6795 	makemodel = ippGetString(attr, 0, NULL);
6796     }
6797     if (printername != NULL &&
6798 	strcasecmp(p->queue_name, printername) == 0) {
6799       if (device == NULL)
6800 	device = uri;
6801       /* Check device URI */
6802       if (device != NULL &&
6803 	  (p->uri == NULL ||
6804 	   (strlen(device) < 16 ||
6805 	    strncmp(device, "implicitclass://", 16)))) {
6806 	/* The printer's device URI is different to what we have
6807 	   assigned, so we got notified because the queue was
6808 	   externally modified and so we will release this printer
6809 	   from the control of cups-browsed */
6810 	debug_printf("Printer %s got modified externally, discovered by a change of its device URI from %s to %s.\n",
6811 		     p->queue_name,
6812 		     (p->uri ? (p->netprinter ? p->uri :
6813 				"implicitclass://...") :
6814 		      "(not yet determined)"),
6815 		     device);
6816 	overwritten = 1;
6817       }
6818       /* Check NickName */
6819       if (p->nickname == NULL || makemodel == NULL ||
6820 	  strcasecmp(p->nickname, makemodel)) {
6821 	/* The PPD file of the queue got replaced which we
6822 	   discovered by comparing the NickName of the PPD with the
6823 	   NickName which the PPD we have used has. So we were
6824 	   notified because the queue was externally modified and so
6825 	   we will release this printer from the control of
6826 	   cups-browsed */
6827 	debug_printf("Printer %s got modified externally, discovered by the NickName of its PPD file having changed from \"%s\" to \"%s\".\n",
6828 		     p->queue_name, (p->nickname ? p->nickname : "(no PPD)"),
6829 		     (makemodel ? makemodel :
6830 		      "(NickName not readable)"));
6831 	overwritten = 1;
6832       }
6833     }
6834   }
6835   if (response) ippDelete(response);
6836 
6837   return overwritten;
6838 }
6839 
6840 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)6841 on_printer_modified (CupsNotifier *object,
6842 		     const gchar *text,
6843 		     const gchar *printer_uri,
6844 		     const gchar *printer,
6845 		     guint printer_state,
6846 		     const gchar *printer_state_reasons,
6847 		     gboolean printer_is_accepting_jobs,
6848 		     gpointer user_data)
6849 {
6850   remote_printer_t *p;
6851   http_t        *conn = NULL;
6852   ipp_t         *request;               /* IPP Request */
6853   int           re_create, is_cups_queue;
6854   char          *new_queue_name;
6855   cups_array_t  *to_be_renamed;
6856   char          local_queue_uri[1024];
6857   char          *resolved_uri = NULL;
6858 
6859   debug_printf("on_printer_modified() in THREAD %ld\n", pthread_self());
6860 
6861   debug_printf("[CUPS Notification] Printer modified: %s\n",
6862 	       text);
6863 
6864   if (is_created_by_cups_browsed(printer)) {
6865     p = printer_record(printer);
6866     if (p->overwritten)
6867       /* We already have discovered that this queue got overwritten
6868          and are treating the process appropriately, so return now to
6869          avoid an infinite recursion */
6870       return;
6871 
6872     if (queue_overwritten(p)) {
6873       /* Our generated local queue pointing to a remote printer got
6874 	 overwritten by an externally created queue with the same
6875 	 name.
6876 	 We will release control from this queue now and try to
6877 	 re-create our queue under a different name, usually
6878 	 <old_name>@<remote_host>.
6879 	 If we have slaves, we have to do this for them, too. */
6880 
6881       p->overwritten = 1;
6882 
6883       /* First, remove the "cups-browsed=true" from the queue's
6884 	 options, so that cups-browsed considers this queue as created
6885 	 manually */
6886       debug_printf("Removing \"cups-browsed=true\" from CUPS queue %s (%s).\n",
6887 		   p->queue_name, p->uri);
6888       conn = http_connect_local ();
6889       if (conn == NULL)
6890 	debug_printf("Browse send failed to connect to localhost\n");
6891       else {
6892 	request = ippNewRequest(IPP_OP_CUPS_ADD_MODIFY_PRINTER);
6893 	/* Printer URI: ipp://localhost/printers/<queue name> */
6894 	httpAssembleURIf(HTTP_URI_CODING_ALL, local_queue_uri,
6895 			 sizeof(local_queue_uri),
6896 			 "ipp", NULL, "localhost", 0,
6897 			 "/printers/%s", p->queue_name);
6898 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
6899 		     "printer-uri", NULL, local_queue_uri);
6900 	/* Default user */
6901 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
6902 		     "requesting-user-name", NULL, cupsUser());
6903 	/* Option to be removed */
6904 	ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_DELETEATTR,
6905 		      CUPS_BROWSED_MARK "-default", 0);
6906 	/* Do it */
6907 	ippDelete(cupsDoRequest(conn, request, "/admin/"));
6908 	if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
6909 	  debug_printf("Unable to remove \"cups-browsed=true\" from CUPS queue!\n");
6910       }
6911 
6912       /* Now try to rename all our printer entries with this
6913 	 queue name. Drop entries where renaming fails */
6914       to_be_renamed = cupsArrayNew(NULL, NULL);
6915       /* Put the printer entries which need attention into
6916 	 a separate array, as we cannot run two nested loops
6917 	 on one CUPS array, as our printer entry array */
6918       for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
6919 	   p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
6920 	if (strcasecmp(p->queue_name, printer) == 0) {
6921 	  p->overwritten = 1;
6922 	  cupsArrayAdd(to_be_renamed, p);
6923 	}
6924       for (p = (remote_printer_t *)cupsArrayFirst(to_be_renamed);
6925 	   p; p = (remote_printer_t *)cupsArrayNext(to_be_renamed)) {
6926 	is_cups_queue = (p->netprinter == 0 ? 1 : 0);
6927 	re_create = 1;
6928 	/* Is there a local queue with the same URI as the remote queue? */
6929 	if (g_hash_table_find (local_printers,
6930 			       local_printer_is_same_device, p)) {
6931 	  /* Found a local queue with the same URI as our discovered printer
6932 	     would get, so ignore this remote printer */
6933 	  debug_printf("Printer with URI %s (or IPP/IPPS equivalent) already exists, no replacement queue to be created.\n",
6934 		       p->uri);
6935 	  re_create = 0;
6936 	} else if ((new_queue_name = /* Try to find a new queue name */
6937 		    get_local_queue_name(p->service_name, p->make_model,
6938 					 p->resource, p->host,
6939 					 &is_cups_queue,
6940 					 p->queue_name)) == NULL) {
6941 	  /* Not able to find a new name for the queue */
6942 	  debug_printf("No new name for printer found, no replacement queue to be created.\n");
6943 	  re_create = 0;
6944 	} else {
6945 	  free(p->queue_name);
6946 	  p->queue_name = new_queue_name;
6947 	  /* Check whether the queue under its new name will be stand-alone or
6948 	     part of a cluster */
6949 	  if (join_cluster_if_needed(p, is_cups_queue) < 0) {
6950 	    /* There are other cups-browsed-generated queues with the new
6951 	       name, not able to cluster this queue with them */
6952 	    debug_printf("Not able to cluster this queue with equally-named ones.\n");
6953 	    re_create = 0;
6954 	  }
6955 	}
6956 	if (resolved_uri) free(resolved_uri);
6957 	if (re_create) {
6958 	  p->overwritten = 0;
6959 	  p->status = STATUS_TO_BE_CREATED;
6960 	  p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
6961 	  debug_printf("Released CUPS queue %s from the control of cups-browsed. Printer with URI %s renamed to %s.\n",
6962 		       printer, p->uri, p->queue_name);
6963 	} else {
6964 	  /* To remove this entry independent of any other entry we
6965 	     set the slave_of to NULL. This does not lead to an
6966 	     attempt to remove a CUPS queue as we have the status
6967 	     STATUS_TO_BE_RELEASED */
6968 	  p->slave_of = NULL;
6969 	  p->status = STATUS_TO_BE_RELEASED;
6970 	  p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
6971 	  debug_printf("Released CUPS queue %s from the control of cups-browsed. No local queue any more for printer with URI %s.\n",
6972 		       printer, p->uri);
6973 	}
6974       }
6975       cupsArrayDelete(to_be_renamed);
6976       if (in_shutdown == 0)
6977 	recheck_timer();
6978     } else {
6979       if (terminating) {
6980 	debug_printf("[CUPS Notification]: Not saving external option changes because cups-browsed is terminating.\n");
6981 	return;
6982       }
6983       /* The user has changed settings of a printer which we have generated,
6984 	 backup the changes for the case of a crash or unclean shutdown of
6985 	 cups-browsed. */
6986       if (!p->no_autosave) {
6987 	debug_printf("Settings of printer %s got modified, doing backup.\n",
6988 		     p->queue_name);
6989 	p->no_autosave = 1; /* Avoid infinite recursion */
6990 	record_printer_options(p->queue_name);
6991 	p->no_autosave = 0;
6992       }
6993     }
6994   }
6995 }
6996 
6997 
6998 
6999 /* This compare function makes the "lo" (looback) interface always
7000    sorted to the beginning of the array, this way one only needs to
7001    check the first element of the error to find out whether a remote
7002    printer is already available through the loopback interface (preferred
7003    interface) or not.
7004    All other interfaces are sorted alphabetically, the types let IPPS
7005    appear before IPP, and the families numerically (makes IPv4 appear
7006    before IPv6). */
7007 
7008 int
ipp_discovery_cmp(void * va,void * vb,void * data)7009 ipp_discovery_cmp(void *va, void *vb, void *data) {
7010   ipp_discovery_t *a = (ipp_discovery_t *)va;
7011   ipp_discovery_t *b = (ipp_discovery_t *)vb;
7012   int cmp;
7013 
7014   if (!a && !b)
7015     return 0;
7016   if (a && !b)
7017     return -1;
7018   if (!a && b)
7019     return  1;
7020 
7021   if (!strcasecmp(a->interface, "lo") && strcasecmp(b->interface, "lo"))
7022     return -1;
7023   if (strcasecmp(a->interface, "lo") && !strcasecmp(b->interface, "lo"))
7024     return  1;
7025 
7026   cmp = strcasecmp(a->interface, b->interface);
7027   if (cmp)
7028     return cmp;
7029 
7030   if (strcasestr(a->type, "ipps") && !strcasestr(b->type, "ipps"))
7031     return -1;
7032   if (!strcasestr(a->type, "ipps") && strcasestr(b->type, "ipps"))
7033     return  1;
7034 
7035   cmp = strcasecmp(a->type, b->type);
7036   if (cmp)
7037     return cmp;
7038 
7039   if (a->family < b->family)
7040     return -1;
7041   else if (a->family > b->family)
7042     return  1;
7043   else
7044     return 0;
7045 }
7046 
7047 void
ipp_discovery_free(void * ve,void * data)7048 ipp_discovery_free(void *ve, void *data) {
7049   ipp_discovery_t *e = (ipp_discovery_t *)ve;
7050 
7051   if (e) {
7052     if (e->interface)
7053       free(e->interface);
7054     if (e->type)
7055       free(e->type);
7056     free(e);
7057   }
7058 }
7059 
7060 void
ipp_discoveries_list(cups_array_t * a)7061 ipp_discoveries_list(cups_array_t *a) {
7062   ipp_discovery_t *e;
7063 
7064   debug_printf("Printer discovered %d times:\n", cupsArrayCount(a));
7065   for (e = cupsArrayFirst(a); e; e = cupsArrayNext(a))
7066     debug_printf("    %s, %s, %s\n", e->interface, e->type,
7067 		 (e->family == AF_INET ? "IPv4" :
7068 		  (e->family == AF_INET6 ? "IPv6" : "???")));
7069 }
7070 
7071 int
ipp_discoveries_add(cups_array_t * a,const char * interface,const char * type,int family)7072 ipp_discoveries_add(cups_array_t *a,
7073 		    const char *interface,
7074 		    const char *type,
7075 		    int family) {
7076   ipp_discovery_t *e;
7077 
7078   if (!interface || !type)
7079     return 0;
7080   if ((e = (ipp_discovery_t *)calloc(1, sizeof(ipp_discovery_t))) ==
7081       NULL) {
7082     debug_printf("ERROR: Unable to allocate memory.\n");
7083     return 0;
7084   }
7085   e->interface = strdup(interface);
7086   e->type = strdup(type);
7087   e->family = family;
7088   cupsArrayAdd(a, e);
7089   ipp_discoveries_list(a);
7090   return 1;
7091 }
7092 
7093 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)7094 create_remote_printer_entry (const char *queue_name,
7095 			     const char *location,
7096 			     const char *info,
7097 			     const char *uri,
7098 			     const char *host,
7099 			     const char *ip,
7100 			     int port,
7101 			     const char *resource,
7102 			     const char *service_name,
7103 			     const char *type,
7104 			     const char *domain,
7105 			     const char *interface,
7106 			     int family,
7107 			     const char *pdl,
7108 			     int color,
7109 			     int duplex,
7110 			     const char *make_model,
7111 			     int is_cups_queue)
7112 {
7113   remote_printer_t *p;
7114   remote_printer_t *q;
7115   http_t *http_printer = NULL;
7116 #ifdef HAVE_CUPS_1_6
7117   int i;
7118   ipp_attribute_t *attr;
7119   char valuebuffer[65536];
7120   int is_pwgraster = 0;
7121   int is_appleraster = 0;
7122   int is_pclm = 0;
7123   int is_pdf = 0;
7124 #endif /* HAVE_CUPS_1_6 */
7125 
7126   if (!queue_name || !location || !info || !uri || !host || !resource ||
7127       !service_name || !type || !domain) {
7128     debug_printf("ERROR: create_remote_printer_entry(): Input value missing!\n");
7129     return NULL;
7130   }
7131 
7132   /* Mark this as a queue to be created locally pointing to the printer */
7133   if ((p = (remote_printer_t *)calloc(1, sizeof(remote_printer_t))) == NULL) {
7134     debug_printf("ERROR: Unable to allocate memory.\n");
7135     return NULL;
7136   }
7137 
7138   /* Assure that, if we have forgotten to set a field in the printer
7139      record, that it is set to zero */
7140   memset(p, 0, sizeof(remote_printer_t));
7141 
7142   /* Queue name */
7143   p->queue_name = strdup(queue_name);
7144   if (!p->queue_name)
7145     goto fail;
7146 
7147   p->location = strdup(location);
7148   if (!p->location)
7149     goto fail;
7150 
7151   p->info = strdup(info);
7152   if (!p->info)
7153     goto fail;
7154 
7155   if (make_model)
7156     p->make_model = strdup(make_model);
7157   else
7158     p->make_model = NULL;
7159 
7160   if (pdl)
7161     p->pdl = strdup(pdl);
7162   else
7163     p->pdl = NULL;
7164 
7165   p->color = color;
7166 
7167   p->duplex = duplex;
7168 
7169   p->uri = strdup(uri);
7170   if (!p->uri)
7171     goto fail;
7172 
7173   p->slave_of = NULL;
7174   p->last_printer = -1;
7175 
7176   p->num_options = 0;
7177   p->options = NULL;
7178 
7179   p->host = strdup (host);
7180   if (!p->host)
7181     goto fail;
7182 
7183   p->ip = (ip != NULL ? strdup (ip) : NULL);
7184 
7185   p->port = (port != 0 ? port : 631);
7186 
7187   p->resource = strdup (resource);
7188   if (!p->resource)
7189     goto fail;
7190 
7191   p->service_name = strdup (service_name);
7192   if (!p->service_name)
7193     goto fail;
7194 
7195   /* Record DNS-SD service parameters to identify print queue
7196      entry for removal when service disappears */
7197   p->type = strdup (type);
7198   if (!p->type)
7199     goto fail;
7200 
7201   p->domain = strdup (domain);
7202   if (!p->domain)
7203     goto fail;
7204 
7205   p->ipp_discoveries =
7206     cupsArrayNew3(ipp_discovery_cmp, NULL, NULL, 0, NULL, ipp_discovery_free);
7207   if (p->ipp_discoveries == NULL) {
7208     debug_printf("ERROR: Unable to allocate memory.\n");
7209     return NULL;
7210   }
7211   if (domain != NULL && domain[0] != '\0' &&
7212       type != NULL && type[0] != '\0')
7213     ipp_discoveries_add(p->ipp_discoveries, interface, type, family);
7214 
7215   /* Schedule for immediate creation of the CUPS queue */
7216   p->status = STATUS_TO_BE_CREATED;
7217   p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
7218 
7219   /* Flag which can be set to inhibit automatic saving of option settings
7220      by the on_printer_modified() notification handler function */
7221   p->no_autosave = 0;
7222 
7223   /* Flag to be set when a local queue generated by us was overwritten
7224      by an external process. It serves to avoid that the process to
7225      treat this case is not repeated in an infinite recursion */
7226   p->overwritten = 0;
7227 
7228   /* Flag to mark whether this printer was discovered through a legacy
7229      CUPS broadcast (1) or through DNS-SD (0) */
7230   p->is_legacy = 0;
7231 
7232   /* Initialize field for how many timeouts cups-browsed experienced
7233      in a row during creation of this printer's queue */
7234   p->timeouted = 0;
7235 
7236   /* Initialize nickname array for *Nickname directive from PPD
7237    * - either from CUPS server or from our PPD generator */
7238   p->nickname = NULL;
7239 
7240   /* Remote CUPS printer or local queue remaining from previous cups-browsed
7241      session */
7242   /* is_cups_queue: -1: Unknown, 0: IPP printer, 1: Remote CUPS queue,
7243                      2: Remote CUPS queue in user-defined cluster      */
7244   if (is_cups_queue != 0) {
7245     if (is_cups_queue > 0 && CreateRemoteCUPSPrinterQueues == 0) {
7246       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",
7247 		   p->queue_name, p->uri);
7248       goto fail;
7249     }
7250     /* For a remote CUPS printer our local queue will be raw or get a
7251        PPD file from the remote CUPS server, so that the driver on the
7252        remote CUPS server gets used. So we will not generate a PPD file
7253        or interface script at this point. */
7254     p->netprinter = 0;
7255     if (p->uri[0] != '\0') {
7256       p->prattrs = get_printer_attributes(p->uri, NULL, 0, NULL, 0, 1);
7257       debug_log_out(get_printer_attributes_log);
7258       if (p->prattrs == NULL)
7259 	debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n",
7260 		     p->queue_name, p->uri);
7261     }
7262   } else {
7263 #ifndef HAVE_CUPS_1_6
7264     /* The following code uses a lot of CUPS >= 1.6 specific stuff.
7265        For older CUPS <= 1.5.4 the following functionality is skipped
7266        which means for CUPS <= 1.5.4 only for CUPS printer broadcasts
7267        there are local queues created which should be sufficient
7268        on systems where traditional CUPS <= 1.5.4 is used. */
7269     goto fail;
7270 #else /* HAVE_CUPS_1_6 */
7271     /* Non-CUPS printer broadcasts are most probably from printers
7272        directly connected to the network and using the IPP protocol.
7273        We check whether we can set them up without a device-specific
7274        driver, only using page description languages which the
7275        operating system provides: PCL 5c/5e/6/XL, PostScript, PDF, PWG
7276        Raster, Apple Raster, PCLm. Especially printers designed for
7277        driverless printing (DNS-SD + IPP 2.x + at least one of PWG
7278        Raster, Apple Raster, PCLm, PDF) will work this way. Making
7279        only driverless queues we can get an easy, configuration-less
7280        way to print from mobile devices, even if there is no CUPS
7281        server with shared printers around. */
7282 
7283     if (CreateIPPPrinterQueues == IPP_PRINTERS_NO) {
7284       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",
7285 		   p->queue_name, p->uri);
7286       goto fail;
7287     }
7288 
7289     if (!pdl || pdl[0] == '\0' ||
7290 	(!strcasestr(pdl, "application/postscript") &&
7291 	 !strcasestr(pdl, "application/pdf") &&
7292 	 !strcasestr(pdl, "image/pwg-raster") &&
7293 #ifdef CUPS_RASTER_HAVE_APPLERASTER
7294 	 !strcasestr(pdl, "image/urf") &&
7295 #endif
7296 #ifdef QPDF_HAVE_PCLM
7297 	 !strcasestr(pdl, "application/PCLm") &&
7298 #endif
7299 	 ((!strcasestr(pdl, "application/vnd.hp-PCL") &&
7300 	   !strcasestr(pdl, "application/PCL") &&
7301 	   !strcasestr(pdl, "application/x-pcl")) ||
7302 	  ((!strncasecmp(make_model, "HP", 2) || /* HP inkjets not supported */
7303 	    !strncasecmp(make_model, "Hewlett Packard", 15) ||
7304 	    !strncasecmp(make_model, "Hewlett-Packard", 15)) &&
7305 	   !strcasestr(make_model, "LaserJet") &&
7306 	   !strcasestr(make_model, "Mopier"))) &&
7307 	 !strcasestr(pdl, "application/vnd.hp-PCLXL"))) {
7308       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",
7309 		   p->queue_name, p->uri, make_model, pdl);
7310       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",
7311 #ifdef CUPS_RASTER_HAVE_APPLERASTER
7312 		   "Apple Raster, ",
7313 #else
7314 		   "",
7315 #endif
7316 #ifdef QPDF_HAVE_PCLM
7317 		   "PCLm, "
7318 #else
7319 		   ""
7320 #endif
7321 		   );
7322       goto fail;
7323     }
7324 
7325     /* Check whether we have an equally named queue already */
7326     for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
7327 	 q;
7328 	 q = (remote_printer_t *)cupsArrayNext(remote_printers))
7329       if (!strcasecmp(q->queue_name, p->queue_name)) {/* Queue with same name */
7330 	debug_printf("We have already created a queue with the name %s for another printer. Skipping this printer.\n", p->queue_name);
7331 	debug_printf("Try setting \"LocalQueueNamingIPPPrinter DNS-SD\" in cups-browsed.conf.\n");
7332 	goto fail;
7333       }
7334 
7335     p->slave_of = NULL;
7336     p->netprinter = 1;
7337     p->prattrs = get_printer_attributes(p->uri, NULL, 0, NULL, 0, 1);
7338     debug_log_out(get_printer_attributes_log);
7339     if (p->prattrs == NULL) {
7340       debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n",
7341 		   p->queue_name, p->uri);
7342       goto fail;
7343     }
7344 
7345     /* If we have opted for only printers designed for driverless use (PWG
7346        Raster + Apple Raster + PCLm + PDF) being set up automatically, we check
7347        first, whether our printer supports IPP 2.0 or newer. If not, we
7348        skip this printer */
7349     if (CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7350       valuebuffer[0] = '\0';
7351       debug_printf("Checking whether printer %s supports IPP 2.x or newer:\n",
7352 		   p->queue_name);
7353       if ((attr = ippFindAttribute(p->prattrs,
7354 				   "ipp-versions-supported",
7355 				   IPP_TAG_KEYWORD)) != NULL) {
7356 	debug_printf("  Attr: %s\n", ippGetName(attr));
7357 	for (i = 0; i < ippGetCount(attr); i ++) {
7358 	  strncpy(valuebuffer, ippGetString(attr, i, NULL),
7359 		  sizeof(valuebuffer) - 1);
7360 	  if (strlen(ippGetString(attr, i, NULL)) > 65535)
7361 	    valuebuffer[65535] = '\0';
7362 	  debug_printf("  Keyword: %s\n", valuebuffer);
7363 	  if (valuebuffer[0] > '1')
7364 	    break;
7365 	}
7366       }
7367       if (!attr || valuebuffer[0] == '\0' || valuebuffer[0] <= '1') {
7368 	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");
7369 	goto fail;
7370       } else
7371 	debug_printf("  --> Printer supports IPP 2.x or newer.\n");
7372     }
7373 
7374     /* If we have opted for only PWG Raster printers or for only printers
7375        designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF)
7376        being set up automatically, we check whether the printer has a non-empty
7377        string in its "pwg-raster-document-resolution-supported" IPP attribute
7378        to see whether we have a PWG Raster printer. */
7379     if (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER ||
7380 	CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7381       valuebuffer[0] = '\0';
7382       debug_printf("Checking whether printer %s understands PWG Raster:\n",
7383 		   p->queue_name);
7384       if ((attr = ippFindAttribute(p->prattrs,
7385 				   "pwg-raster-document-resolution-supported",
7386 				   IPP_TAG_RESOLUTION)) != NULL) {
7387 	debug_printf("  Attr: %s\n", ippGetName(attr));
7388 	ippAttributeString(attr, valuebuffer, sizeof(valuebuffer));
7389 	debug_printf("  Value: %s\n", valuebuffer);
7390 	if (valuebuffer[0] == '\0') {
7391 	  for (i = 0; i < ippGetCount(attr); i ++) {
7392 	    strncpy(valuebuffer, ippGetString(attr, i, NULL),
7393 		    sizeof(valuebuffer) - 1);
7394 	    if (strlen(ippGetString(attr, i, NULL)) > 65535)
7395 	      valuebuffer[65535] = '\0';
7396 	    debug_printf("  Keyword: %s\n", valuebuffer);
7397 	    if (valuebuffer[0] != '\0')
7398 	      break;
7399 	  }
7400 	}
7401       }
7402       if (attr && valuebuffer[0] != '\0')
7403         is_pwgraster = 1;
7404       debug_printf("  --> Printer %s PWG Raster.\n",
7405 		   is_pwgraster ? "supports" : "does not support");
7406     }
7407 
7408 #ifdef CUPS_RASTER_HAVE_APPLERASTER
7409     /* If we have opted for only Apple Raster printers or for only printers
7410        designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF)
7411        being set up automatically, we check whether the printer has a non-empty
7412        string in its "urf-supported" IPP attribute to see whether we have an
7413        Apple Raster printer. */
7414     if (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER ||
7415 	CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7416       valuebuffer[0] = '\0';
7417       debug_printf("Checking whether printer %s understands Apple Raster:\n",
7418 		   p->queue_name);
7419       if ((attr = ippFindAttribute(p->prattrs, "urf-supported", IPP_TAG_KEYWORD)) != NULL) {
7420 	debug_printf("  Attr: %s\n", ippGetName(attr));
7421 	ippAttributeString(attr, valuebuffer, sizeof(valuebuffer));
7422 	debug_printf("  Value: %s\n", valuebuffer);
7423 	if (valuebuffer[0] == '\0') {
7424 	  for (i = 0; i < ippGetCount(attr); i ++) {
7425 	    strncpy(valuebuffer, ippGetString(attr, i, NULL),
7426 		    sizeof(valuebuffer) - 1);
7427 	    if (strlen(ippGetString(attr, i, NULL)) > 65535)
7428 	      valuebuffer[65535] = '\0';
7429 	    debug_printf("  Keyword: %s\n", valuebuffer);
7430 	    if (valuebuffer[0] != '\0')
7431 	      break;
7432 	  }
7433 	}
7434       }
7435       if (attr && valuebuffer[0] != '\0')
7436         is_appleraster = 1;
7437       debug_printf("  --> Printer %s Apple Raster.\n",
7438 		   is_appleraster ? "supports" : "does not support");
7439     }
7440 #endif
7441 
7442 #ifdef QPDF_HAVE_PCLM
7443     /* If we have opted for only PCLm printers or for only printers
7444        designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF)
7445        being set up automatically, we check whether the printer has a non-empty
7446        string in its "pclm-compression-method-preferred" IPP attribute to see
7447        whether we have a PCLm printer. */
7448     if (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM ||
7449 	CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7450       valuebuffer[0] = '\0';
7451       debug_printf("Checking whether printer %s understands PCLm:\n",
7452 		   p->queue_name);
7453       if ((attr = ippFindAttribute(p->prattrs,
7454 				   "pclm-compression-method-preferred",
7455 				   IPP_TAG_KEYWORD)) != NULL) {
7456 	debug_printf("  Attr: %s\n", ippGetName(attr));
7457 	ippAttributeString(attr, valuebuffer, sizeof(valuebuffer));
7458 	debug_printf("  Value: %s\n", p->queue_name, valuebuffer);
7459 	if (valuebuffer[0] == '\0') {
7460 	  for (i = 0; i < ippGetCount(attr); i ++) {
7461 	    strncpy(valuebuffer, ippGetString(attr, i, NULL),
7462 		    sizeof(valuebuffer) - 1);
7463 	    if (strlen(ippGetString(attr, i, NULL)) > 65535)
7464 	      valuebuffer[65535] = '\0';
7465 	    debug_printf("  Keyword: %s\n", valuebuffer);
7466 	    if (valuebuffer[0] != '\0')
7467 	      break;
7468 	  }
7469 	}
7470       }
7471       if (attr && valuebuffer[0] != '\0')
7472         is_pclm = 1;
7473       debug_printf("  --> Printer %s PCLm.\n",
7474 		   is_pclm ? "supports" : "does not support");
7475     }
7476 #endif
7477 
7478     /* If we have opted for only PDF printers or for only printers
7479        designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF)
7480        being set up automatically, we check whether the printer has
7481        "application/pdf" under its PDLs. */
7482     if (CreateIPPPrinterQueues == IPP_PRINTERS_PDF ||
7483 	CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7484       debug_printf("Checking whether printer %s understands PDF: PDLs: %s\n",
7485 		   p->queue_name, pdl);
7486       if(strcasestr(pdl, "application/pdf"))
7487         is_pdf = 1;
7488       debug_printf("  --> Printer %s PDF.\n",
7489 		   is_pdf ? "supports" : "does not support");
7490     }
7491 
7492     /* If the printer is not the driverless printer we opted for, we skip
7493        this printer. */
7494     if ((CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS &&
7495 	 is_pwgraster == 0 && is_appleraster == 0 && is_pclm == 0 &&
7496 	 is_pdf == 0) ||
7497 	(CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER &&
7498 	 is_pwgraster == 0) ||
7499 	(CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER &&
7500 	 is_appleraster == 0) ||
7501 	(CreateIPPPrinterQueues == IPP_PRINTERS_PCLM &&
7502 	 is_pclm == 0) ||
7503 	(CreateIPPPrinterQueues == IPP_PRINTERS_PDF &&
7504 	 is_pdf == 0)) {
7505       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",
7506 		   p->queue_name, p->uri,
7507 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER ||
7508 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7509 		    ", " : ""),
7510 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER ||
7511 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7512 		    (is_pwgraster ? "" : "not ") : ""),
7513 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER ||
7514 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7515 		    "PWG Raster" : ""),
7516 		   (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER ||
7517 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7518 		    ", " : ""),
7519 		   (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER ||
7520 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7521 		    (is_appleraster ? "" : "not ") : ""),
7522 		   (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER ||
7523 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7524 		    "Apple Raster" : ""),
7525 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM ||
7526 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7527 		    ", " : ""),
7528 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM ||
7529 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7530 		    (is_pclm ? "" : "not ") : ""),
7531 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM ||
7532 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7533 		    "PCLm" : ""),
7534 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PDF ||
7535 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7536 		    ", " : ""),
7537 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PDF ||
7538 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7539 		    (is_pdf ? "" : "not ") : ""),
7540 		   (CreateIPPPrinterQueues == IPP_PRINTERS_PDF ||
7541 		    CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7542 		    "PDF" : ""));
7543       goto fail;
7544     }
7545 
7546 #endif /* HAVE_CUPS_1_6 */
7547   }
7548   /* Check whether we have an equally named queue already from another
7549      server and join a cluster if needed */
7550   if (join_cluster_if_needed(p, is_cups_queue) < 0)
7551     goto fail;
7552   /* Add the new remote printer entry */
7553   log_all_printers();
7554   cupsArrayAdd(remote_printers, p);
7555   log_all_printers();
7556 
7557   /* If auto shutdown is active we have perhaps scheduled a timer to shut down
7558      due to not having queues any more to maintain, kill the timer now */
7559   if (autoshutdown && autoshutdown_exec_id &&
7560       autoshutdown_on == NO_QUEUES &&
7561       cupsArrayCount(remote_printers) > 0) {
7562     debug_printf ("New printers there to make available, killing auto shutdown timer.\n");
7563     g_source_remove(autoshutdown_exec_id);
7564     autoshutdown_exec_id = 0;
7565   }
7566 
7567   if (http_printer)
7568     httpClose(http_printer);
7569   return p;
7570 
7571  fail:
7572   debug_printf("ERROR: Unable to create print queue, ignoring printer.\n");
7573   if (p->prattrs) ippDelete(p->prattrs);
7574   if (http_printer)
7575     httpClose(http_printer);
7576   if (p->type) free (p->type);
7577   if (p->service_name) free (p->service_name);
7578   if (p->host) free (p->host);
7579   if (p->resource) free (p->resource);
7580   if (p->domain) free (p->domain);
7581   cupsArrayDelete(p->ipp_discoveries);
7582   if (p->ip) free (p->ip);
7583   cupsFreeOptions(p->num_options, p->options);
7584   if (p->uri) free (p->uri);
7585   if (p->pdl) free (p->pdl);
7586   if (p->make_model) free (p->make_model);
7587   if (p->location) free (p->location);
7588   if (p->info) free (p->info);
7589   if (p->queue_name) free (p->queue_name);
7590   if (p->nickname) free (p->nickname);
7591   free (p);
7592   return NULL;
7593 }
7594 
7595 void
remove_printer_entry(remote_printer_t * p)7596 remove_printer_entry(remote_printer_t *p) {
7597   remote_printer_t *q = NULL, *r;
7598 
7599   if (p == NULL) {
7600     debug_printf ("ERROR: remove_printer_entry(): Supplied printer entry is NULL");
7601     return;
7602   }
7603 
7604   if (!p->slave_of) {
7605     /* Check whether this queue has a slave from another server and
7606        find it */
7607     for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
7608 	 q;
7609 	 q = (remote_printer_t *)cupsArrayNext(remote_printers))
7610       if (q != p && q->slave_of == p &&
7611 	  q->status != STATUS_DISAPPEARED && q->status != STATUS_UNCONFIRMED &&
7612 	  q->status != STATUS_TO_BE_RELEASED)
7613 	break;
7614   }
7615   if (q) {
7616     /* Make q the master of the cluster and p a slave of q. This way
7617        removal of p does not delete the cluster's CUPS queue and update
7618        of q makes sure the cluster's queue gets back into working state */
7619     for (r = (remote_printer_t *)cupsArrayFirst(remote_printers);
7620 	 r;
7621 	 r = (remote_printer_t *)cupsArrayNext(remote_printers))
7622       if (r != q && r->slave_of == p &&
7623 	  r->status != STATUS_DISAPPEARED && r->status != STATUS_UNCONFIRMED &&
7624 	  r->status != STATUS_TO_BE_RELEASED)
7625 	r->slave_of = q;
7626     q->slave_of = NULL;
7627     p->slave_of = q;
7628     q->num_options = p->num_options;
7629     q->options = p->options;
7630     p->num_options = 0;
7631     p->options = NULL;
7632     /* Schedule this printer for updating the CUPS queue */
7633     q->status = STATUS_TO_BE_CREATED;
7634     q->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
7635     debug_printf("Printer %s (%s) diasappeared, replacing by backup on host %s, port %d with URI %s.\n",
7636 		 p->queue_name, p->uri, q->host, q->port, q->uri);
7637   } else
7638     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",
7639 		 p->queue_name, p->host, p->port, p->uri);
7640 
7641   /* Schedule entry and its CUPS queue for removal */
7642   if (p->status != STATUS_TO_BE_RELEASED)
7643     p->status = STATUS_DISAPPEARED;
7644   p->timeout = time(NULL) + TIMEOUT_REMOVE;
7645 }
7646 
update_cups_queues(gpointer unused)7647 gboolean update_cups_queues(gpointer unused) {
7648   remote_printer_t *p, *q, *r, *s, *master;
7649   http_t        *http;
7650   char          uri[HTTP_MAX_URI], device_uri[HTTP_MAX_URI], buf[1024],
7651                 line[1024];
7652   int           num_options;
7653   cups_option_t *options;
7654   int           num_jobs;
7655   cups_job_t    *jobs;
7656   ipp_t         *request;
7657   time_t        current_time;
7658   int           i, ap_remote_queue_id_line_inserted,
7659                 want_raw, num_cluster_printers = 0;
7660   char          *disabled_str;
7661   char          *ppdfile, *ifscript;
7662   int           fd = 0;  /* Script file descriptor */
7663   char          tempfile[1024];  /* Temporary file */
7664   char          buffer[8192];  /* Buffer for creating script */
7665   int           bytes;
7666   const char    *cups_serverbin;  /* CUPS_SERVERBIN environment variable */
7667 #ifdef HAVE_CUPS_1_6
7668   ipp_attribute_t *attr;
7669   int           count, left, right, bottom, top;
7670   const char    *default_page_size = NULL, *best_color_space = NULL,
7671                 *color_space;
7672 #endif
7673   const char    *loadedppd = NULL;
7674   ppd_file_t    *ppd = NULL;
7675   ppd_choice_t  *choice;
7676   cups_file_t   *in, *out;
7677   char          keyword[1024], *keyptr;
7678   const char    *customval;
7679   const char    *val = NULL;
7680   cups_dest_t   *dest = NULL;
7681   int           is_shared;
7682   cups_array_t  *conflicts = NULL;
7683   ipp_t         *printer_attributes = NULL;
7684   cups_array_t  *sizes=NULL;
7685   ipp_t         *printer_ipp_response;
7686   char          *make_model = NULL;
7687   const char    *pdl=NULL;
7688   int           color;
7689   int           duplex;
7690   char          *default_pagesize = NULL;
7691   const char    *default_color = NULL;
7692   int           cups_queues_updated = 0;
7693 
7694   /* Create dummy entry to point slaves at when their master is about to
7695      get removed now (if we point them to NULL, we would try to remove
7696      the already removed CUPS queue again when it comes to the removal
7697      of the slave. */
7698   if (deleted_master == NULL) {
7699     if ((deleted_master =
7700 	 (remote_printer_t *)calloc(1, sizeof(remote_printer_t))) == NULL) {
7701       debug_printf("ERROR: Unable to allocate memory.\n");
7702       if (in_shutdown == 0)
7703 	recheck_timer ();
7704       return FALSE;
7705     }
7706     memset(deleted_master, 0, sizeof(remote_printer_t));
7707     deleted_master->uri = "<DELETED>";
7708   }
7709 
7710   /* Now redirect the slave_of pointers of the masters which get deleted now
7711      to this dummy entry */
7712   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
7713        p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
7714     if ((p->status == STATUS_DISAPPEARED ||
7715 	 p->status == STATUS_TO_BE_RELEASED) &&
7716 	(q = p->slave_of) != NULL && q->queue_name &&
7717 	(q->status == STATUS_DISAPPEARED || q->status == STATUS_TO_BE_RELEASED))
7718       p->slave_of = deleted_master;
7719 
7720   debug_printf("Processing printer list ...\n");
7721   log_all_printers();
7722   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
7723        p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
7724 
7725     /* We need to get the current time as precise as possible for retries
7726        and reset the timeout flag */
7727     current_time = time(NULL);
7728     timeout_reached = 0;
7729 
7730     /* terminating means we have received a signal and should shut down.
7731        in_shutdown means we have exited the main loop.
7732        update_cups_queues() is called after having exited the main loop
7733        in order to remove any queues we have set up */
7734     if (terminating && !in_shutdown) {
7735       debug_printf("Stopping processing printer list because cups-browsed is terminating.\n");
7736       break;
7737     }
7738 
7739     /* We do not necessarily update all local CUPS queues which are
7740        scheduled for creation, update, or removal in a single call of
7741        the update_cups_queues() function, as then we could be stuck in
7742        this function for a long time and other tasks of cups-browsed,
7743        especially directing print jobs to destination printers before
7744        the implicitclass backend times out, will not get done in time.
7745        We schedule a new call of update_cups_queues() after a short
7746        delay to continue with the next local CUPS queues. */
7747     if (!in_shutdown && update_cups_queues_max_per_call > 0 &&
7748 	cups_queues_updated >= update_cups_queues_max_per_call) {
7749       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",
7750 		   update_cups_queues_max_per_call);
7751       break;
7752     }
7753 
7754     switch (p->status) {
7755 
7756       /* Print queue generated by us in a previous session */
7757     case STATUS_UNCONFIRMED:
7758 
7759       /* Only act if the timeout has passed */
7760       if (p->timeout > current_time)
7761 	break;
7762 
7763       /* Queue not reported again by DNS-SD, remove it */
7764       debug_printf("No remote printer named %s available, removing entry from previous session.\n",
7765 		   p->queue_name);
7766       remove_printer_entry(p);
7767 
7768     /* DNS-SD has reported this printer as disappeared or we have replaced
7769        this printer by another one */
7770     case STATUS_DISAPPEARED:
7771     case STATUS_TO_BE_RELEASED:
7772 
7773       /* Only act if the timeout has passed */
7774       if (p->timeout > current_time)
7775 	break;
7776 
7777       debug_printf("Removing entry %s (%s)%s.\n", p->queue_name, p->uri,
7778 		   (p->slave_of ||
7779 		    p->status == STATUS_TO_BE_RELEASED ? "" :
7780 		    " and its CUPS queue"));
7781 
7782       /* Slaves do not have a CUPS queue */
7783       if ((q = p->slave_of) == NULL) {
7784 	if ((http = http_connect_local ()) == NULL) {
7785 	  debug_printf("Unable to connect to CUPS!\n");
7786 	  if (in_shutdown == 0) {
7787             current_time = time(NULL);
7788 	    p->timeout = current_time + TIMEOUT_RETRY;
7789 	  }
7790 	  break;
7791 	}
7792 
7793 	/* Do not auto-save option settings due to the print queue removal
7794 	   process or release process */
7795 	p->no_autosave = 1;
7796 
7797 	/* Record the option settings to retrieve them when the remote
7798 	   queue re-appears later or when cups-browsed gets started again */
7799 	record_printer_options(p->queue_name);
7800 
7801 	if (p->status != STATUS_TO_BE_RELEASED &&
7802 	    !queue_overwritten(p)) {
7803 	  /* Remove the CUPS queue */
7804 
7805 	  /* Check whether there are still jobs and do not remove the queue
7806 	     then */
7807 	  num_jobs = 0;
7808 	  jobs = NULL;
7809 	  num_jobs = cupsGetJobs2(http, &jobs, p->queue_name, 0,
7810 				  CUPS_WHICHJOBS_ACTIVE);
7811 	  if (num_jobs > 0) { /* There are still jobs */
7812 	    debug_printf("Queue has still jobs or CUPS error!\n");
7813 	    cupsFreeJobs(num_jobs, jobs);
7814 	    /* Disable the queue */
7815 #ifdef HAVE_AVAHI
7816 	    if (avahi_present || p->domain == NULL || p->domain[0] == '\0')
7817 	      /* If avahi has got shut down, do not disable queues which are,
7818 		 created based on DNS-SD broadcasts as the server has most
7819 		 probably not gone away */
7820 #endif /* HAVE_AVAHI */
7821 	      disable_printer(p->queue_name,
7822 			      "Printer disappeared or cups-browsed shutdown");
7823 	    /* Schedule the removal of the queue for later */
7824 	    if (in_shutdown == 0) {
7825               current_time = time(NULL);
7826 	      p->timeout = current_time + TIMEOUT_RETRY;
7827 	      p->no_autosave = 0;
7828 	      break;
7829 	    } else
7830 	      /* Make sure queue's list entry gets freed */
7831 	      goto keep_queue;
7832 	  }
7833 
7834 	  /* If this queue was the default printer, note that fact so that
7835 	     it gets the default printer again when it re-appears, also switch
7836 	     back to the last local default printer */
7837 	  queue_removal_handle_default(p->queue_name);
7838 
7839 	  /* If we do not have a subscription to CUPS' D-Bus notifications and
7840 	     so no default printer management, we simply do not remove this
7841 	     CUPS queue if it is the default printer, to not cause a change
7842 	     of the default printer or the loss of the information that this
7843 	     printer is the default printer. */
7844 	  if (cups_notifier == NULL && is_cups_default_printer(p->queue_name)) {
7845 	    /* Schedule the removal of the queue for later */
7846 	    if (in_shutdown == 0) {
7847               current_time = time(NULL);
7848 	      p->timeout = current_time + TIMEOUT_RETRY;
7849 	      p->no_autosave = 0;
7850 	      break;
7851 	    } else
7852 	      /* Make sure queue's list entry gets freed */
7853 	      goto keep_queue;
7854 	  }
7855 
7856 	  /* No jobs, remove the CUPS queue */
7857 	  debug_printf("Removing local CUPS queue %s (%s).\n", p->queue_name,
7858 		       p->uri);
7859 	  request = ippNewRequest(CUPS_DELETE_PRINTER);
7860 	  /* Printer URI: ipp://localhost/printers/<queue name> */
7861 	  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
7862 			   "localhost", 0, "/printers/%s",
7863 			   p->queue_name);
7864 	  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
7865 		       "printer-uri", NULL, uri);
7866 	  /* Default user */
7867 	  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
7868 		       "requesting-user-name", NULL, cupsUser());
7869 	  /* Do it */
7870 	  ippDelete(cupsDoRequest(http, request, "/admin/"));
7871 
7872 	  cups_queues_updated ++;
7873 
7874 	  if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE &&
7875 	      cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND) {
7876 	    debug_printf("Unable to remove CUPS queue! (%s)\n",
7877 			 cupsLastErrorString());
7878 	    if (in_shutdown == 0) {
7879               current_time = time(NULL);
7880 	      p->timeout = current_time + TIMEOUT_RETRY;
7881 	      p->no_autosave = 0;
7882 	      break;
7883 	    }
7884 	  }
7885 	}
7886       }
7887 
7888     keep_queue:
7889 
7890       /* CUPS queue removed or released from cups-browsed, remove the list
7891 	 entry */
7892       /* Note that we do not need to break out of the loop passing through
7893 	 all elements of a CUPS array when we remove an element via the
7894 	 cupsArrayRemove() function, as the function decreases the array-
7895 	 internal index by one and so the cupsArrayNext() call gives us
7896 	 the element right after the deleted element. So no skipping
7897          of an element and especially no reading beyond the end of the
7898          array. */
7899       cupsArrayRemove(remote_printers, p);
7900       if (p->queue_name) free (p->queue_name);
7901       if (p->location) free (p->location);
7902       if (p->info) free (p->info);
7903       if (p->make_model) free (p->make_model);
7904       if (p->pdl) free (p->pdl);
7905       if (p->uri) free (p->uri);
7906       cupsFreeOptions(p->num_options, p->options);
7907       if (p->host) free (p->host);
7908       if (p->ip) free (p->ip);
7909       if (p->resource) free (p->resource);
7910       if (p->service_name) free (p->service_name);
7911       if (p->type) free (p->type);
7912       if (p->domain) free (p->domain);
7913       cupsArrayDelete(p->ipp_discoveries);
7914       if (p->prattrs) ippDelete (p->prattrs);
7915       if (p->nickname) free (p->nickname);
7916       free(p);
7917       p = NULL;
7918 
7919       /* If auto shutdown is active and all printers we have set up got removed
7920 	 again, schedule the shutdown in autoshutdown_timeout seconds
7921          Note that in this case we also do not have jobs any more so if we
7922          auto shutdown on running out of jobs, trigger it here, too. */
7923       if (in_shutdown == 0 && autoshutdown && !autoshutdown_exec_id &&
7924 	  (cupsArrayCount(remote_printers) == 0 ||
7925 	   (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
7926 	debug_printf ("No printers there any more to make available or no jobs, shutting down in %d sec...\n", autoshutdown_timeout);
7927 	autoshutdown_exec_id =
7928 	  g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
7929 				 NULL);
7930       }
7931 
7932       break;
7933 
7934     /* DNS-SD has reported a new remote printer, create a CUPS queue for it,
7935        or upgrade an existing queue, or update a queue to use a backup host
7936        when it has disappeared on the currently used host */
7937     /* (...or, we've just received a CUPS Browsing packet for this queue) */
7938     case STATUS_TO_BE_CREATED:
7939 
7940       /* Do not create a queue for slaves */
7941       if (p->slave_of) {
7942 	master = p->slave_of;
7943 	if (master->queue_name) {
7944 	  p->status = STATUS_CONFIRMED;
7945 	  master->status = STATUS_TO_BE_CREATED;
7946 	  master->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
7947 	  if (p->is_legacy) {
7948 	    p->timeout = time(NULL) + BrowseTimeout;
7949 	    debug_printf("starting BrowseTimeout timer for %s (%ds)\n",
7950 			 p->queue_name, BrowseTimeout);
7951 	  } else
7952 	    p->timeout = (time_t) -1;
7953 	} else {
7954 	  debug_printf("Master for slave %s is invalid (deleted?)\n",
7955 		       p->queue_name);
7956 	  p->status = STATUS_DISAPPEARED;
7957 	  p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
7958 	}
7959 	break;
7960       }
7961 
7962       /* Only act if the timeout has passed */
7963       if (p->timeout > current_time)
7964 	break;
7965 
7966       /* cups-browsed tried to add this print queue unsuccessfully for too
7967 	 many times due to timeouts - Skip print queue creation for this one */
7968       if (p->timeouted >= HttpMaxRetries) {
7969 	debug_printf("Max number of retries (%d) for creating print queue %s reached, skipping it.\n",
7970 		HttpMaxRetries, p->queue_name);
7971 	continue;
7972       }
7973 
7974       debug_printf("Creating/Updating CUPS queue %s\n",
7975 		   p->queue_name);
7976 
7977       /* Make sure to have a connection to the local CUPS daemon */
7978       if ((http = http_connect_local ()) == NULL) {
7979 	debug_printf("Unable to connect to CUPS!\n");
7980         current_time = time(NULL);
7981 	p->timeout = current_time + TIMEOUT_RETRY;
7982 	break;
7983       }
7984       httpSetTimeout(http, HttpLocalTimeout, http_timeout_cb, NULL);
7985 
7986       /* Do not auto-save option settings due to the print queue creation
7987 	 process */
7988       p->no_autosave = 1;
7989 
7990       /* Printer URI: ipp://localhost/printers/<queue name> */
7991       httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
7992 		       "localhost", 0, "/printers/%s", p->queue_name);
7993 
7994       ifscript = NULL;
7995       ppdfile = NULL;
7996 
7997 #ifdef HAVE_CUPS_1_6
7998       /* Check whether there is a temporary CUPS queue which we would
7999          overwrite */
8000       dest = NULL;
8001       if (OnlyUnsupportedByCUPS == 0)
8002         dest = cupsGetNamedDest(http, p->queue_name, NULL);
8003       if (dest) {
8004 	/* CUPS has found a queue with this name.
8005 	   Either CUPS generates a temporary queue here or we have already
8006 	   made this queue permanent. In any case, load the PPD from this
8007 	   queue to conserve the PPD which CUPS has originally generated. */
8008 	if (p->netprinter == 1 && IPPPrinterQueueType == PPD_YES &&
8009 	    UseCUPSGeneratedPPDs) {
8010 	  if (LocalQueueNamingIPPPrinter != LOCAL_QUEUE_NAMING_DNSSD) {
8011 	    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",
8012 			 p->queue_name);
8013 	    debug_printf("Not loading PPD from temporary CUPS queue for this printer.\n");
8014 	    debug_printf("Try setting \"LocalQueueNamingIPPPrinter DNS-SD\" in cups-browsed.conf.\n");
8015 	  } else {
8016 	    /* This call makes CUPS actually create the queue so that we can
8017 	       grab the PPD. We discard the result of the call. */
8018 	    debug_printf("Establishing dummy connection to make CUPS create the temporary queue.\n");
8019 	    cups_dinfo_t *dinfo = cupsCopyDestInfo(http, dest);
8020 	    if (dinfo == NULL)
8021 	      debug_printf("Unable to connect to destination.\n");
8022 	    else {
8023 	      debug_printf("Temporary queue created, grabbing the PPD.\n");
8024 	      cupsFreeDestInfo(dinfo);
8025 	      loadedppd = NULL;
8026 	      if ((loadedppd = loadPPD(http, p->queue_name)) == NULL)
8027 		debug_printf("Unable to load PPD from local temporary queue %s!\n",
8028 			     p->queue_name);
8029 	      else {
8030 		ppdfile = strdup(loadedppd);
8031 		debug_printf("Loaded PPD file %s from local temporary queue %s.\n",
8032 			     ppdfile, p->queue_name);
8033 	      }
8034 	    }
8035 	  }
8036 	}
8037 	/* If we have already a temporary CUPS queue our local queue we
8038 	   are creating would overwrite the temporary queue, and so the
8039 	   resulting queue will still be considered temporary by CUPS and
8040 	   removed after one minute of inactivity. To avoid this we need
8041 	   to convert the queue into a permanent one and CUPS does this
8042 	   only by sharing the queue (setting its boolean printer-is-shared
8043 	   option. We unset the bit right after that to not actually share
8044 	   the queue (if we want to share the queue we take care about this
8045 	   later).
8046 	   Note that we cannot reliably determine whether we have a
8047 	   temporary queue via the printer-is-temporary attribute,
8048 	   therefore we consider only shared queues as for sure
8049 	   permanent and not shared queues as possibly temporary. To
8050 	   assure we have a permanent queue in the end we set and
8051 	   remove the shared bit on any queue which is not shared.
8052 	   If the temporary queue is pointing to a remote CUPS printer
8053 	   we cannot modify its printer-is-shared option as CUPS prevents
8054 	   this. In this case we remove the temporary queue so that we
8055 	   create a fresh one which will always be permanent.
8056 	   If the temporary queue has still jobs we will not remove it to
8057 	   not loose the jobs and wait with creating our new queue until
8058 	   the jobs are done. */
8059 	val = cupsGetOption ("printer-is-shared",
8060 			     dest->num_options,
8061 			     dest->options);
8062 	is_shared = val && (!strcasecmp (val, "yes") ||
8063 			    !strcasecmp (val, "on") ||
8064 			    !strcasecmp (val, "true"));
8065 	cupsFreeDests(1, dest);
8066 	if (!is_shared) {
8067 	  debug_printf("Our new queue overwrites the possibly temporary CUPS queue %s, so we need to assure the queue gets permanent.\n",
8068 		       p->queue_name);
8069 	  /* We need to modify the printer-is-shared bit twice if we need to
8070 	     make a temporary queue permanent but not share this queue */
8071 	  for (i = 0; i <= 1; i ++) {
8072 	    if (i == 0)
8073 	      debug_printf("Setting printer-is-shared bit to make this queue permanent.\n");
8074 	    else
8075 	      debug_printf("Unsetting printer-is-shared bit.\n");
8076 	    request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
8077 	    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8078 			 "printer-uri", NULL, uri);
8079 	    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8080 			 "requesting-user-name", NULL, cupsUser());
8081 	    num_options = 0;
8082 	    options = NULL;
8083 	    num_options = cupsAddOption("printer-is-shared",
8084 					(i == 0 ? "true" : "false"),
8085 					num_options, &options);
8086 	    num_options = cupsAddOption(CUPS_BROWSED_MARK "-default", "true", num_options, &options);
8087 	    cupsEncodeOptions2(request, num_options, options,
8088 			       IPP_TAG_OPERATION);
8089 	    cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
8090 	    /*
8091 	     * Do IPP request for printer-is-shared option only when we have
8092 	     * network printer or if we have remote CUPS queue, do IPP request
8093 	     * only if we have CUPS older than 2.2.
8094 	     * When you have remote queue, clean up and break from the loop.
8095 	     */
8096 	    if (p->netprinter != 0 || !HAVE_CUPS_2_2 || AllowResharingRemoteCUPSPrinters)
8097 	      ippDelete(cupsDoRequest(http, request, "/admin/"));
8098 	    else {
8099 	      ippDelete(request);
8100 	      cupsFreeOptions(num_options, options);
8101 	      break;
8102 	    }
8103 	    cupsFreeOptions(num_options, options);
8104 	    if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
8105 	      debug_printf("Unable change printer-is-shared bit to %s (%s)!\n",
8106 			   (i == 0 ? "true" : "false"),
8107 			   cupsLastErrorString());
8108 	      break;
8109 	    }
8110 	  }
8111 	  /* Error on modifying printer-is-shared bit, removing possibly
8112 	     temporary queue */
8113 	  if (i <= 1) {
8114 	    debug_printf("Removing the possibly temporary CUPS queue.\n");
8115 	    /* Check whether there are still jobs and do not remove the queue
8116 	       then */
8117 	    num_jobs = 0;
8118 	    jobs = NULL;
8119 	    num_jobs = cupsGetJobs2(http, &jobs, p->queue_name, 0,
8120 				    CUPS_WHICHJOBS_ACTIVE);
8121 	    if (num_jobs > 0) { /* there are still jobs */
8122 	      debug_printf("Temporary queue has still jobs or CUPS error, retrying later.\n");
8123 	      cupsFreeJobs(num_jobs, jobs);
8124 	      /* Schedule the removal of the queue for later */
8125 	      if (in_shutdown == 0) {
8126                 current_time = time(NULL);
8127 		p->timeout = current_time + TIMEOUT_RETRY;
8128 		p->no_autosave = 0;
8129 	      }
8130 	      break;
8131 	    }
8132 	    /* No jobs, remove the CUPS queue */
8133 	    request = ippNewRequest(CUPS_DELETE_PRINTER);
8134 	    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8135 			 "printer-uri", NULL, uri);
8136 	    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8137 			 "requesting-user-name", NULL, cupsUser());
8138 	    ippDelete(cupsDoRequest(http, request, "/admin/"));
8139 	    if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE &&
8140 		cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND) {
8141 	      debug_printf("Unable to remove temporary CUPS queue (%s), retrying later\n",
8142 			   cupsLastErrorString());
8143 	      if (in_shutdown == 0) {
8144                 current_time = time(NULL);
8145 		p->timeout = current_time + TIMEOUT_RETRY;
8146 		p->no_autosave = 0;
8147 		break;
8148 	      }
8149 	    }
8150 	  }
8151 	} else
8152 	  debug_printf("Creating/Updating permanent CUPS queue %s.\n",
8153 		       p ->queue_name);
8154       } else
8155 	debug_printf("Creating permanent CUPS queue %s.\n",
8156 		     p->queue_name);
8157 
8158       /* If we did not already obtain a PPD file from the temporary CUPS queue
8159          or if we want to use a System V interface script for our IPP network
8160 	 printer, we proceed here */
8161       if (p->netprinter == 1) {
8162 	if (p->prattrs == NULL) {
8163 	  p->prattrs = get_printer_attributes(p->uri, NULL, 0, NULL, 0, 1);
8164 	  debug_log_out(get_printer_attributes_log);
8165 	}
8166 	if (p->prattrs == NULL) {
8167 	  debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n",
8168 		       p->queue_name, p->uri);
8169 	  p->status = STATUS_DISAPPEARED;
8170           current_time = time(NULL);
8171 	  p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8172 	  goto cannot_create;
8173 	}
8174 	if (IPPPrinterQueueType == PPD_YES) {
8175 	  num_cluster_printers = 0;
8176 	  for (s = (remote_printer_t *)cupsArrayFirst(remote_printers);
8177 	       s; s = (remote_printer_t *)cupsArrayNext(remote_printers)) {
8178 	    if (!strcmp(s->queue_name, p->queue_name)) {
8179 	      if (s->status == STATUS_DISAPPEARED ||
8180 		  s->status == STATUS_UNCONFIRMED ||
8181 		  s->status == STATUS_TO_BE_RELEASED )
8182 		continue;
8183 	      num_cluster_printers ++;
8184 	    }
8185 	  }
8186 
8187 	  if (num_cluster_printers == 1) {
8188 	    printer_attributes = p->prattrs;
8189 	    conflicts = NULL;
8190 	    default_pagesize = NULL;
8191 	    default_color = NULL;
8192 	    make_model = p->make_model;
8193 	    pdl = p->pdl;
8194 	    color = p->color;
8195 	    duplex = p->duplex;
8196 	    sizes = NULL;
8197 	  } else {
8198 	    make_model = (char*)malloc(sizeof(char) * 256);
8199 	    printer_attributes = get_cluster_attributes(p->queue_name);
8200 	    if ((attr = ippFindAttribute(printer_attributes,
8201 					 "printer-make-and-model",
8202 					 IPP_TAG_TEXT)) != NULL)
8203 	      strncpy(make_model, ippGetString(attr, 0, NULL),
8204 		      sizeof(make_model) - 1);
8205 	    color = 0;
8206 	    duplex = 0;
8207 	    for (r = (remote_printer_t *)cupsArrayFirst(remote_printers);
8208 		 r; r = (remote_printer_t *)cupsArrayNext(remote_printers)) {
8209 	      if (!strcmp(p->queue_name, r->queue_name)) {
8210 		if (r->color == 1)
8211 		  color = 1;
8212 		if (r->duplex == 1)
8213 		  duplex = 1;
8214 	      }
8215 	    }
8216 	    default_pagesize = (char *)malloc(sizeof(char)*32);
8217 	    debug_printf("Generated Merged Attributes for local queue %s\n",
8218 			 p->queue_name);
8219 	    conflicts = generate_cluster_conflicts(p->queue_name,
8220 						   printer_attributes);
8221 	    debug_printf("Generated Constraints for queue %s\n",
8222 			 p->queue_name);
8223 	    sizes = get_cluster_sizes(p->queue_name);
8224 	    get_cluster_default_attributes(&printer_attributes,
8225 					   p->queue_name, default_pagesize,
8226 					   &default_color);
8227 	    debug_printf("Generated Default Attributes for local queue %s\n",
8228 			 p->queue_name);
8229 	  }
8230 	  if (ppdfile == NULL) {
8231 	    /* If we do not want CUPS-generated PPDs or we cannot obtain a
8232 	       CUPS-generated PPD, for example if CUPS does not create a
8233 	       temporary queue for this printer, we generate a PPD by
8234 	       ourselves */
8235 	    printer_ipp_response = (num_cluster_printers == 1) ? p->prattrs :
8236 	      printer_attributes;
8237 	    if (!ppdCreateFromIPP2(buffer, sizeof(buffer), printer_ipp_response,
8238 				   make_model,
8239 				   pdl, color, duplex, conflicts, sizes,
8240 				   default_pagesize, default_color)) {
8241 	      if (errno != 0)
8242 		debug_printf("Unable to create PPD file: %s\n",
8243 			     strerror(errno));
8244 	      else
8245 		debug_printf("Unable to create PPD file: %s\n",
8246 			     ppdgenerator_msg);
8247 	      p->status = STATUS_DISAPPEARED;
8248               current_time = time(NULL);
8249 	      p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8250 	      goto cannot_create;
8251 	    } else {
8252 	      debug_printf("PPD generation successful: %s\n", ppdgenerator_msg);
8253 	      debug_printf("Created temporary PPD file: %s\n", buffer);
8254 	      ppdfile = strdup(buffer);
8255 	    }
8256 	  }
8257 
8258 	  if (num_cluster_printers != 1) {
8259 	    if (default_pagesize != NULL) {
8260 	      free(default_pagesize);
8261 	      default_pagesize = NULL;
8262 	    }
8263 	    if (make_model != NULL) {
8264 	      free(make_model);
8265 	      make_model = NULL;
8266 	    }
8267 	    if (conflicts != NULL) {
8268 	      cupsArrayDelete(conflicts);
8269 	      conflicts = NULL;
8270 	    }
8271 	    if (printer_attributes != NULL) {
8272 	      ippDelete(printer_attributes);
8273 	      printer_attributes = NULL;
8274 	    }
8275 	    if (sizes != NULL) {
8276 	      cupsArrayDelete(sizes);
8277 	      sizes = NULL;
8278 	    }
8279 	  }
8280 	} else if (IPPPrinterQueueType == PPD_NO) {
8281 	  ppdfile = NULL;
8282 
8283 	  /* Find default page size of the printer */
8284 	  attr = ippFindAttribute(p->prattrs,
8285 				  "media-default",
8286 				  IPP_TAG_ZERO);
8287 	  if (attr) {
8288 	    default_page_size = ippGetString(attr, 0, NULL);
8289 	    debug_printf("Default page size: %s\n",
8290 			 default_page_size);
8291 	    p->num_options = cupsAddOption("media-default",
8292 					   default_page_size,
8293 					   p->num_options, &(p->options));
8294 	  } else {
8295 	    attr = ippFindAttribute(p->prattrs,
8296 				    "media-ready",
8297 				    IPP_TAG_ZERO);
8298 	    if (attr) {
8299 	      default_page_size = ippGetString(attr, 0, NULL);
8300 	      debug_printf("Default page size: %s\n",
8301 			   default_page_size);
8302 	      p->num_options = cupsAddOption("media-default",
8303 					     default_page_size,
8304 					     p->num_options, &(p->options));
8305 	    } else
8306 	      debug_printf("No default page size found!\n");
8307 	  }
8308 
8309 	  /* Find maximum unprintable margins of the printer */
8310 	  if ((attr = ippFindAttribute(p->prattrs,
8311 				       "media-bottom-margin-supported",
8312 				       IPP_TAG_INTEGER)) != NULL) {
8313 	    for (i = 1, bottom = ippGetInteger(attr, 0),
8314 		   count = ippGetCount(attr);
8315 		 i < count;
8316 		 i ++)
8317 	      if (ippGetInteger(attr, i) > bottom)
8318 		bottom = ippGetInteger(attr, i);
8319 	  } else
8320 	    bottom = 1270;
8321 	  snprintf(buffer, sizeof(buffer), "%d", bottom);
8322 	  p->num_options = cupsAddOption("media-bottom-margin-default",
8323 					 buffer,
8324 					 p->num_options, &(p->options));
8325 
8326 	  if ((attr = ippFindAttribute(p->prattrs,
8327 				       "media-left-margin-supported",
8328 				       IPP_TAG_INTEGER)) != NULL) {
8329 	    for (i = 1, left = ippGetInteger(attr, 0),
8330 		   count = ippGetCount(attr);
8331 		 i < count;
8332 		 i ++)
8333 	      if (ippGetInteger(attr, i) > left)
8334 		left = ippGetInteger(attr, i);
8335 	  } else
8336 	    left = 635;
8337 	  snprintf(buffer, sizeof(buffer), "%d", left);
8338 	  p->num_options = cupsAddOption("media-left-margin-default",
8339 					 buffer,
8340 					 p->num_options, &(p->options));
8341 
8342 	  if ((attr = ippFindAttribute(p->prattrs,
8343 				       "media-right-margin-supported",
8344 				       IPP_TAG_INTEGER)) != NULL) {
8345 	    for (i = 1, right = ippGetInteger(attr, 0),
8346 		   count = ippGetCount(attr);
8347 		 i < count;
8348 		 i ++)
8349 	      if (ippGetInteger(attr, i) > right)
8350 		right = ippGetInteger(attr, i);
8351 	  } else
8352 	    right = 635;
8353 	  snprintf(buffer, sizeof(buffer), "%d", right);
8354 	  p->num_options = cupsAddOption("media-right-margin-default",
8355 					 buffer,
8356 					 p->num_options, &(p->options));
8357 
8358 	  if ((attr = ippFindAttribute(p->prattrs,
8359 				       "media-top-margin-supported",
8360 				       IPP_TAG_INTEGER)) != NULL) {
8361 	    for (i = 1, top = ippGetInteger(attr, 0),
8362 		   count = ippGetCount(attr);
8363 		 i < count;
8364 		 i ++)
8365 	      if (ippGetInteger(attr, i) > top)
8366 		top = ippGetInteger(attr, i);
8367 	  } else
8368 	    top = 1270;
8369 	  snprintf(buffer, sizeof(buffer), "%d", top);
8370 	  p->num_options = cupsAddOption("media-top-margin-default",
8371 					 buffer,
8372 					 p->num_options, &(p->options));
8373 
8374 	  debug_printf("Margins: Left: %d, Right: %d, Top: %d, Bottom: %d\n",
8375 		       left, right, top, bottom);
8376 
8377 	  /* Find best color space of the printer */
8378 	  attr = ippFindAttribute(p->prattrs,
8379 				  "pwg-raster-document-type-supported",
8380 				  IPP_TAG_ZERO);
8381 	  if (attr) {
8382 	    for (i = 0; i < ippGetCount(attr); i ++) {
8383 	      color_space = ippGetString(attr, i, NULL);
8384 	      debug_printf("Supported color space: %s\n", color_space);
8385 	      if (color_space_score(color_space) >
8386 		  color_space_score(best_color_space))
8387 		best_color_space = color_space;
8388 	    }
8389 	    debug_printf("Best color space: %s\n",
8390 			 best_color_space);
8391 	    p->num_options = cupsAddOption("print-color-mode-default",
8392 					   best_color_space,
8393 					   p->num_options, &(p->options));
8394 	  } else {
8395 	    debug_printf("No info about supported color spaces found!\n");
8396 	    p->num_options = cupsAddOption("print-color-mode-default",
8397 					   p->color == 1 ? "rgb" : "black",
8398 					   p->num_options, &(p->options));
8399 	  }
8400 
8401 	  if (p->duplex)
8402 	    p->num_options = cupsAddOption("sides-default",
8403 					   "two-sided-long-edge",
8404 					   p->num_options, &(p->options));
8405 
8406 	  p->num_options = cupsAddOption("output-format-default",
8407 					 p->pdl,
8408 					 p->num_options, &(p->options));
8409 	  p->num_options = cupsAddOption("make-and-model-default",
8410 					 remove_bad_chars(p->make_model, 0),
8411 					 p->num_options, &(p->options));
8412 
8413 	  if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
8414 	    cups_serverbin = CUPS_SERVERBIN;
8415 
8416 	  if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) {
8417 	    debug_printf("Unable to create interface script file\n");
8418 	    p->status = STATUS_DISAPPEARED;
8419             current_time = time(NULL);
8420 	    p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8421 	    goto cannot_create;
8422 	  }
8423 
8424 	  debug_printf("Creating temp script file \"%s\"\n", tempfile);
8425 
8426 	  snprintf(buffer, sizeof(buffer),
8427 		   "#!/bin/sh\n"
8428 		   "# System V interface script for printer %s generated by cups-browsed\n"
8429 		   "\n"
8430 		   "if [ $# -lt 5 -o $# -gt 6 ]; then\n"
8431 		   "  echo \"ERROR: $0 job-id user title copies options [file]\" >&2\n"
8432 		   "  exit 1\n"
8433 		   "fi\n"
8434 		   "\n"
8435 		   "# Read from given file\n"
8436 		   "if [ -n \"$6\" ]; then\n"
8437 		   "  exec \"$0\" \"$1\" \"$2\" \"$3\" \"$4\" \"$5\" < \"$6\"\n"
8438 		   "fi\n"
8439 		   "\n"
8440 		   "%s/filter/sys5ippprinter \"$1\" \"$2\" \"$3\" \"$4\" \"$5\"\n",
8441 		   p->queue_name, cups_serverbin);
8442 
8443 	  bytes = write(fd, buffer, strlen(buffer));
8444 	  if (bytes != strlen(buffer)) {
8445 	    debug_printf("Unable to write interface script into the file\n");
8446 	    p->status = STATUS_DISAPPEARED;
8447             current_time = time(NULL);
8448 	    p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8449 	    goto cannot_create;
8450 	  }
8451 
8452 	  close(fd);
8453 
8454 	  ifscript = strdup(tempfile);
8455 	}
8456       }
8457 #endif /* HAVE_CUPS_1_6 */
8458 
8459       /* Do we have default option settings in cups-browsed.conf? */
8460       if (DefaultOptions) {
8461 	debug_printf("Applying default option settings to printer %s: %s\n",
8462 		     p->queue_name, DefaultOptions);
8463 	p->num_options = cupsParseOptions(DefaultOptions, p->num_options,
8464 					  &p->options);
8465       }
8466 
8467       /* Loading saved option settings from last session */
8468       p->num_options = load_printer_options(p->queue_name, p->num_options,
8469 					    &p->options);
8470 
8471       /* Determine whether we have an IPP network printer. If not we
8472 	 have remote CUPS queue(s) and so we use an implicit class for
8473 	 load balancing. In this case we will assign an
8474 	 implicitclass://...  device URI, which makes cups-browsed find
8475 	 the best destination for each job. */
8476       loadedppd = NULL;
8477       if (cups_notifier != NULL && p->netprinter == 0) {
8478 	/* We are not an IPP network printer, so we use the device URI
8479 	   implicitclass://<queue name>/
8480 	   We use the httpAssembleURI() function here, to percent-encode
8481 	   the queue name in the URI, so that any allowed character in
8482 	   a queue name, especially the '@' when we add the server name
8483 	   to a remote queue's name, goes safely into the URI.
8484 	   The implicitclass backend uses httpSeparateURI() to decode the
8485 	   queue name.
8486 	   We never use the implicitclass backend if we do not have D-Bus
8487 	   notification from CUPS as we cannot assign a destination printer
8488 	   to an incoming job then. */
8489 	httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
8490 			"implicitclass", NULL, p->queue_name, 0, NULL);
8491 	debug_printf("Print queue %s is for remote CUPS queue(s) and we get notifications from CUPS, using implicit class device URI %s\n",
8492 		     p->queue_name, device_uri);
8493 	if (!ppdfile && !ifscript) {
8494 	  /* Having another backend than the CUPS "ipp" backend the
8495 	     options from the PPD of the queue on the server are not
8496 	     automatically used on the client any more, so we have to
8497 	     explicitly load the PPD from one of the servers, apply it
8498 	     to our local queue, and replace its "*cupsFilter(2): ..."
8499 	     lines by one line making the print data get passed through
8500 	     to the server without filtering on the client (where not
8501 	     necessarily the right filters/drivers are installed) so
8502 	     that it gets filtered on the server. In addition, we prefix
8503 	     the PPD's NickName, so that automatic PPD updating by the
8504 	     distribution's package installation/update infrastructure
8505 	     is suppressed. */
8506 	  /* Generating the ppd file for the remote cups queue */
8507 	  if (p->prattrs == NULL) {
8508 	    p->prattrs = get_printer_attributes(p->uri, NULL, 0, NULL, 0, 1);
8509 	    debug_log_out(get_printer_attributes_log);
8510 	  }
8511 	  if (p->prattrs == NULL) {
8512 	    debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n",
8513 			 p->queue_name, p->uri);
8514 	    goto cannot_create;
8515 	  }
8516 	  num_cluster_printers = 0;
8517 	  for (s = (remote_printer_t *)cupsArrayFirst(remote_printers);
8518 	       s; s = (remote_printer_t *)cupsArrayNext(remote_printers)) {
8519 	    if (!strcmp(s->queue_name, p->queue_name)) {
8520 	      if (s->status == STATUS_DISAPPEARED ||
8521 		  s->status == STATUS_UNCONFIRMED ||
8522 		  s->status == STATUS_TO_BE_RELEASED )
8523 		continue;
8524 	      num_cluster_printers++;
8525 	    }
8526 	  }
8527 	  if (num_cluster_printers == 1) {
8528 	    printer_attributes = p->prattrs;
8529 	    conflicts = NULL;
8530 	    default_pagesize = NULL;
8531 	    default_color = NULL;
8532 	    make_model = p->make_model;
8533 	    pdl = p->pdl;
8534 	    color = p->color;
8535 	    duplex = p->duplex;
8536 	    sizes = NULL;
8537 	  } else {
8538 	    make_model = (char*)malloc(sizeof(char)*256);
8539 	    printer_attributes = get_cluster_attributes(p->queue_name);
8540 	    if((attr = ippFindAttribute(printer_attributes,
8541 					"printer-make-and-model",
8542 					IPP_TAG_TEXT)) != NULL)
8543 	      strncpy(make_model, ippGetString(attr, 0, NULL),
8544 		      sizeof(make_model) - 1);
8545 	    color = 0;
8546 	    duplex = 0;
8547 	    for (r = (remote_printer_t *)cupsArrayFirst(remote_printers);
8548 		 r; r = (remote_printer_t *)cupsArrayNext(remote_printers)) {
8549 	      if (!strcmp(p->queue_name, r->queue_name)) {
8550 		if (r->color == 1)
8551 		  color = 1;
8552 		if (r->duplex == 1)
8553 		  duplex = 1;
8554 	      }
8555 	    }
8556 	    default_pagesize = (char *)malloc(sizeof(char)*32);
8557 	    debug_printf("Generated Merged Attributes for local queue %s\n",
8558 			 p->queue_name);
8559 	    conflicts = generate_cluster_conflicts(p->queue_name,
8560 						   printer_attributes);
8561 	    debug_printf("Generated Constraints for queue %s\n",p->queue_name);
8562 	    sizes = get_cluster_sizes(p->queue_name);
8563 	    get_cluster_default_attributes(&printer_attributes, p->queue_name,
8564 					   default_pagesize,&default_color);
8565 	    debug_printf("Generated Default Attributes for local queue %s\n",
8566 			 p->queue_name);
8567 	  }
8568 	  if (ppdfile == NULL) {
8569 	    /* If we do not want CUPS-generated PPDs or we cannot obtain a
8570 	       CUPS-generated PPD, for example if CUPS does not create a
8571 	       temporary queue for this printer, we generate a PPD by
8572 	       ourselves */
8573 	    printer_ipp_response = (num_cluster_printers == 1) ? p->prattrs :
8574 	      printer_attributes;
8575 	    if (!ppdCreateFromIPP2(buffer, sizeof(buffer), printer_ipp_response,
8576 				   make_model,
8577 				   pdl, color, duplex, conflicts, sizes,
8578 				   default_pagesize, default_color)) {
8579 	      if (errno != 0)
8580 		debug_printf("Unable to create PPD file: %s\n",
8581 			     strerror(errno));
8582 	      else
8583 		debug_printf("Unable to create PPD file: %s\n", ppdgenerator_msg);
8584 	      p->status = STATUS_DISAPPEARED;
8585 	      current_time = time(NULL);
8586 	      p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8587 	      goto cannot_create;
8588 	    } else {
8589 	      debug_printf("PPD generation successful: %s\n", ppdgenerator_msg);
8590 	      debug_printf("Created temporary PPD file: %s\n", buffer);
8591 	      ppdfile = strdup(buffer);
8592 	    }
8593 	  }
8594 	}
8595 
8596 	if (num_cluster_printers != 1) {
8597 	  if (default_pagesize != NULL) {
8598 	    free(default_pagesize);
8599 	    default_pagesize = NULL;
8600 	  }
8601 	  if (make_model != NULL) {
8602 	    free(make_model);
8603 	    make_model = NULL;
8604 	  }
8605 	  if (conflicts != NULL) {
8606 	    cupsArrayDelete(conflicts);
8607 	    conflicts = NULL;
8608 	  }
8609 	  if (printer_attributes != NULL) {
8610 	    ippDelete(printer_attributes);
8611 	    printer_attributes = NULL;
8612 	  }
8613 	  if (sizes != NULL) {
8614 	    cupsArrayDelete(sizes);
8615 	    sizes = NULL;
8616 	  }
8617 	}
8618       } else {
8619 	/* Device URI: using implicitclass backend for IPP network printer */
8620 	httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
8621 			"implicitclass", NULL, p->queue_name, 0, NULL);
8622 	if (strlen(device_uri) > HTTP_MAX_URI-1)
8623 	  device_uri[HTTP_MAX_URI-1] = '\0';
8624 	debug_printf("Print queue %s is for an IPP network printer, using implicitclass backend for the printer: %s\n",
8625 		     p->queue_name, device_uri);
8626       }
8627 
8628       /* PPD readily available */
8629       if (ppdfile) {
8630 	debug_printf("Using PPD %s for queue %s.\n",
8631 		     ppdfile, p->queue_name);
8632 	loadedppd = ppdfile;
8633       }
8634       if (loadedppd) {
8635 	if ((ppd = ppdOpenFile(loadedppd)) == NULL) {
8636 	  int linenum; /* Line number of error */
8637 	  ppd_status_t status = ppdLastError(&linenum);
8638 	  debug_printf("Unable to open PPD \"%s\": %s on line %d.",
8639 		       loadedppd, ppdErrorString(status), linenum);
8640           current_time = time(NULL);
8641 	  p->timeout = current_time + TIMEOUT_RETRY;
8642 	  p->no_autosave = 0;
8643 	  unlink(loadedppd);
8644 	  break;
8645 	}
8646 	ppdMarkDefaults(ppd);
8647 	cupsMarkOptions(ppd, p->num_options, p->options);
8648 	if ((out = cupsTempFile2(buf, sizeof(buf))) == NULL) {
8649 	  debug_printf("Unable to create temporary file!\n");
8650           current_time = time(NULL);
8651 	  p->timeout = current_time + TIMEOUT_RETRY;
8652 	  p->no_autosave = 0;
8653 	  ppdClose(ppd);
8654           ppd = NULL;
8655 	  unlink(loadedppd);
8656 	  break;
8657 	}
8658 	if ((in = cupsFileOpen(loadedppd, "r")) == NULL) {
8659 	  debug_printf("Unable to open the downloaded PPD file!\n");
8660           current_time = time(NULL);
8661 	  p->timeout = current_time + TIMEOUT_RETRY;
8662 	  p->no_autosave = 0;
8663 	  cupsFileClose(out);
8664 	  ppdClose(ppd);
8665           ppd = NULL;
8666 	  unlink(loadedppd);
8667 	  break;
8668 	}
8669 	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",
8670 		     loadedppd, p->queue_name,
8671 		     " and doing client-side filtering of the job" ,
8672 		     buf);
8673 	ap_remote_queue_id_line_inserted = 0;
8674 	while (cupsFileGets(in, line, sizeof(line))) {
8675 	  if (!strncmp(line, "*Default", 8)) {
8676 	    strncpy(keyword, line + 8, sizeof(keyword) - 1);
8677 	    if ((strlen(line) + 8) > 1023)
8678 	      keyword[1023] = '\0';
8679 	    for (keyptr = keyword; *keyptr; keyptr ++)
8680 	      if (*keyptr == ':' || isspace(*keyptr & 255))
8681 		break;
8682 	    *keyptr++ = '\0';
8683 	    while (isspace(*keyptr & 255))
8684 	      keyptr ++;
8685 	    if (!strcmp(keyword, "PageRegion") ||
8686 		!strcmp(keyword, "PageSize") ||
8687 		!strcmp(keyword, "PaperDimension") ||
8688 		!strcmp(keyword, "ImageableArea")) {
8689 	      if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) == NULL)
8690 		choice = ppdFindMarkedChoice(ppd, "PageRegion");
8691 	    } else
8692 	      choice = ppdFindMarkedChoice(ppd, keyword);
8693 	    if (choice && strcmp(choice->choice, keyptr)) {
8694 	      if (strcmp(choice->choice, "Custom"))
8695 		cupsFilePrintf(out, "*Default%s: %s\n", keyword,
8696 			       choice->choice);
8697 	      else if ((customval = cupsGetOption(keyword, p->num_options,
8698 						  p->options)) != NULL)
8699 		cupsFilePrintf(out, "*Default%s: %s\n", keyword, customval);
8700 	      else
8701 		cupsFilePrintf(out, "%s\n", line);
8702 	    } else
8703 	      cupsFilePrintf(out, "%s\n", line);
8704 	  } else if (strncmp(line, "*End", 4)) {
8705 	    /* Write an "APRemoteQueueID" line to make this queue marked
8706 	       as remote printer by CUPS */
8707 	    if (p->netprinter == 0 &&
8708 		strncmp(line, "*%", 2) &&
8709 		strncmp(line, "*PPD-Adobe:", 11) &&
8710 		ap_remote_queue_id_line_inserted == 0 &&
8711               !AllowResharingRemoteCUPSPrinters) {
8712 	      ap_remote_queue_id_line_inserted = 1;
8713 	      cupsFilePrintf(out, "*APRemoteQueueID: \"\"\n");
8714 	    }
8715 	    /* Simply write out the line as we read it */
8716 	    cupsFilePrintf(out, "%s\n", line);
8717 	  }
8718 	  /* Save the NickName of the PPD to check whether external
8719 	     manipulations of the print queue have replaced the PPD.
8720 	     Check whether nickname is defined too */
8721 	  if (!strncmp(line, "*NickName:", 10) && p->nickname == NULL) {
8722 	    char *ptr = NULL;
8723 	    char *end_ptr = NULL;
8724 	    int nickname_len = 0;
8725 
8726 	    ptr = strchr(line, '"');
8727 
8728 	    if (ptr == NULL)
8729 	    {
8730 	      debug_printf("Malformed *Nickname directive in PPD - no double quote in line.\n");
8731 	      continue;
8732 	    }
8733 
8734 	    ptr ++;
8735 	    end_ptr = strchr(ptr, '"');
8736 
8737 	    if (end_ptr == NULL)
8738 	    {
8739 	      debug_printf("Malformed *Nickname directive in PPD - no ending double quote\n");
8740 	      continue;
8741 	    }
8742 
8743 	    /* both pointers are null terminated, because cupsFileGets() puts
8744 	     * a null terminator into returned buffer with one line
8745 	     * here as 'line' array) and those two pointers points on two places
8746 	     * in the 'line' array.
8747 	     */
8748 	    nickname_len = strlen(ptr) - strlen(end_ptr);
8749 
8750 	    if (nickname_len == 0)
8751 	    {
8752 	      debug_printf("Malformed *Nickname directive in PPD - empty nickname.\n");
8753 	      continue;
8754 	    }
8755 
8756 	    /* alloc one more space for null terminator, calloc() will initialize
8757 	     * it to null automatically, so then we only copy a string with 'nickname_len'
8758 	     * length to get a proper null terminated p->nickname.
8759 	     */
8760 	    p->nickname = (char*)calloc(nickname_len + 1, sizeof(char));
8761 
8762 	    if (p->nickname != NULL)
8763 	      strncpy(p->nickname, ptr, nickname_len);
8764 	  }
8765 	}
8766 	cupsFilePrintf(out,"*cupsFilter2: \"application/vnd.cups-pdf application/pdf 0 -\"\n");
8767 
8768 	cupsFileClose(in);
8769 	cupsFileClose(out);
8770 	ppdClose(ppd);
8771         ppd = NULL;
8772 	unlink(loadedppd);
8773 	loadedppd = NULL;
8774 	if (ppdfile)
8775         {
8776 	  free(ppdfile);
8777           ppdfile = NULL;
8778         }
8779 	ppdfile = strdup(buf);
8780       }
8781 
8782       /* Create a new CUPS queue or modify the existing queue */
8783       request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
8784       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8785 		   "printer-uri", NULL, uri);
8786       /* Default user */
8787       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8788 		   "requesting-user-name", NULL, cupsUser());
8789       /* Queue should be enabled ... */
8790       ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
8791 		    IPP_PRINTER_IDLE);
8792       /* ... and accepting jobs */
8793       ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
8794       num_options = 0;
8795       options = NULL;
8796       /* Device URI: ipp(s)://<remote host>:631/printers/<remote queue>
8797          OR          implicitclass://<queue name>/ */
8798       num_options = cupsAddOption("device-uri", device_uri,
8799 				  num_options, &options);
8800       /* Option cups-browsed=true, marking that we have created this queue */
8801       num_options = cupsAddOption(CUPS_BROWSED_MARK "-default", "true",
8802 				  num_options, &options);
8803       /* Default option settings from printer entry */
8804       for (i = 0; i < p->num_options; i ++)
8805 	if (strcasecmp(p->options[i].name, "printer-is-shared"))
8806 	  num_options = cupsAddOption(p->options[i].name,
8807 				      p->options[i].value,
8808 				      num_options, &options);
8809       /* Encode option list into IPP attributes */
8810       cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
8811       cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
8812       /* Do it */
8813       if (ppdfile) {
8814 	debug_printf("Non-raw queue %s with PPD file: %s\n", p->queue_name, ppdfile);
8815 	ippDelete(cupsDoFileRequest(http, request, "/admin/", ppdfile));
8816 	want_raw = 0;
8817 	unlink(ppdfile);
8818 	free(ppdfile);
8819 	ppdfile = NULL;
8820       } else if (ifscript) {
8821 	debug_printf("Non-raw queue %s with interface script: %s\n", p->queue_name, ifscript);
8822 	ippDelete(cupsDoFileRequest(http, request, "/admin/", ifscript));
8823 	want_raw = 0;
8824 	unlink(ifscript);
8825 	free(ifscript);
8826 	ifscript = NULL;
8827       } else {
8828 	if (p->netprinter == 0) {
8829 	  debug_printf("Raw queue %s\n", p->queue_name);
8830 	  want_raw = 1;
8831 	} else {
8832 	  debug_printf("Queue %s keeping its current PPD file/interface script\n", p->queue_name);
8833 	  want_raw = 0;
8834 	}
8835 	ippDelete(cupsDoRequest(http, request, "/admin/"));
8836       }
8837       cupsFreeOptions(num_options, options);
8838       cups_queues_updated ++;
8839 
8840       if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
8841 	debug_printf("Unable to create/modify CUPS queue (%s)!\n",
8842 		     cupsLastErrorString());
8843         current_time = time(NULL);
8844 	p->timeout = current_time + TIMEOUT_RETRY;
8845 	p->no_autosave = 0;
8846 	break;
8847       }
8848 
8849       /* Do not share a queue which serves only to point to a remote CUPS
8850 	 printer
8851 
8852 	 We do this in a seperate IPP request as on newer CUPS versions we
8853          get an error when changing the printer-is-shared bit on a queue
8854          pointing to a remote CUPS printer, this way we assure all other
8855 	 settings be applied amd when setting the printer-is-shared to
8856          false amd this errors, we can safely ignore the error as on queues
8857 	 pointing to remote CUPS printers the bit is set to false by default
8858 	 (these printers are never shared)
8859 
8860 	 If our printer is an IPP network printer and not a CUPS queue, we
8861          keep track of whether the user has changed the printer-is-shared
8862          bit and recover this setting. The default setting for a new
8863          queue is configurable via the NewIPPPrinterQueuesShared directive
8864          in cups-browsed.conf */
8865 
8866       request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
8867       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8868 		   "printer-uri", NULL, uri);
8869       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8870 		   "requesting-user-name", NULL, cupsUser());
8871       num_options = 0;
8872       options = NULL;
8873       if (p->netprinter == 1 &&
8874 	  (val = cupsGetOption("printer-is-shared", p->num_options,
8875 			       p->options)) != NULL) {
8876 	num_options = cupsAddOption("printer-is-shared", val,
8877 				    num_options, &options);
8878 	debug_printf("Setting printer-is-shared bit to %s.\n", val);
8879       } else if (p->netprinter == 1 && NewIPPPrinterQueuesShared) {
8880 	num_options = cupsAddOption("printer-is-shared", "true",
8881 				    num_options, &options);
8882 	debug_printf("Setting printer-is-shared bit.\n");
8883       } else if (NewBrowsePollQueuesShared &&
8884       (val = cupsGetOption("printer-to-be-shared", p->num_options,
8885                p->options)) != NULL) {
8886 	num_options = cupsAddOption("printer-is-shared", "true",
8887 				    num_options, &options);
8888 	debug_printf("Setting printer-is-shared bit.\n");
8889       } else {
8890 	num_options = cupsAddOption("printer-is-shared", "false",
8891 				    num_options, &options);
8892 	debug_printf("Unsetting printer-is-shared bit.\n");
8893       }
8894       cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
8895       cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
8896       /*
8897        * Do IPP request for printer-is-shared option only when we have
8898        * network printer or if we have remote CUPS queue, do IPP request
8899        * only if we have CUPS older than 2.2.
8900        */
8901       if (p->netprinter != 0 || !HAVE_CUPS_2_2 || AllowResharingRemoteCUPSPrinters)
8902         ippDelete(cupsDoRequest(http, request, "/admin/"));
8903       else
8904         ippDelete(request);
8905       cupsFreeOptions(num_options, options);
8906       if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
8907 	debug_printf("Unable to modify the printer-is-shared bit (%s)!\n",
8908 		     cupsLastErrorString());
8909 
8910       /* If we are about to create a raw queue or turn a non-raw queue
8911 	 into a raw one, we apply the "ppd-name=raw" option to remove any
8912 	 existing PPD file assigned to the queue.
8913 
8914          Also here we do a separate IPP request as it errors in some
8915          cases. */
8916       if (want_raw) {
8917 	debug_printf("Removing local PPD file for printer %s\n", p->queue_name);
8918 	request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
8919 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8920 		     "printer-uri", NULL, uri);
8921 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8922 		     "requesting-user-name", NULL, cupsUser());
8923 	num_options = 0;
8924 	options = NULL;
8925 	num_options = cupsAddOption("ppd-name", "raw",
8926 				    num_options, &options);
8927 	cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
8928 	cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
8929 	ippDelete(cupsDoRequest(http, request, "/admin/"));
8930 	cupsFreeOptions(num_options, options);
8931 	if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
8932 	  debug_printf("Unable to remove PPD file from the print queue (%s)!\n",
8933 		       cupsLastErrorString());
8934       }
8935 
8936       /* If this queue was the default printer in its previous life, make
8937 	 it the default printer again. */
8938       queue_creation_handle_default(p->queue_name);
8939 
8940       /* If cups-browsed or a failed backend has disabled this
8941 	 queue, re-enable it. */
8942       if ((disabled_str = is_disabled(p->queue_name, "cups-browsed")) != NULL) {
8943 	enable_printer(p->queue_name);
8944 	free(disabled_str);
8945       } else if ((disabled_str =
8946 		  is_disabled(p->queue_name,
8947 			      "Printer stopped due to backend errors")) !=
8948 		 NULL) {
8949 	enable_printer(p->queue_name);
8950 	free(disabled_str);
8951       }
8952 
8953       p->status = STATUS_CONFIRMED;
8954       if (p->is_legacy) {
8955 	p->timeout = time(NULL) + BrowseTimeout;
8956 	debug_printf("starting BrowseTimeout timer for %s (%ds)\n",
8957 		     p->queue_name, BrowseTimeout);
8958       } else
8959 	p->timeout = (time_t) -1;
8960 
8961       /* Check if an HTTP timeout happened during the print queue creation
8962 	 If it does - increment p->timeouted and set status to TO_BE_CREATED
8963 	 because the creation can fall through the process, have state changed
8964 	 to STATUS_CONFIRMED and experience the timeout */
8965       /* If no timeout has happened, clear p->timeouted */
8966       if (timeout_reached == 1) {
8967 	debug_printf("Timeout happened during creation of the queue %s.\n",
8968 		     p->queue_name);
8969 	p->timeouted ++;
8970 	debug_printf("The queue %s already timeouted %d times in a row.\n",
8971 		     p->queue_name, p->timeouted);
8972 	p->status = STATUS_TO_BE_CREATED;
8973 	p->timeout = current_time + TIMEOUT_RETRY;
8974       } else if (p->timeouted != 0) {
8975 	debug_printf("Creating the queue %s went smoothly after %d timeouts.\n",
8976 		     p->queue_name, p->timeouted);
8977 	p->timeouted = 0;
8978       }
8979 
8980       p->no_autosave = 0;
8981       break;
8982 
8983     case STATUS_CONFIRMED:
8984       /* Only act if the timeout has passed */
8985       if (p->timeout > current_time)
8986 	break;
8987 
8988       if (p->is_legacy) {
8989 	/* Remove a queue based on a legacy CUPS broadcast when the
8990 	   broadcast timeout expires without a new broadcast of this
8991 	   queue from the server */
8992 	remove_printer_entry(p);
8993       } else
8994 	p->timeout = (time_t) -1;
8995 
8996       break;
8997 
8998     }
8999   }
9000 
9001   /* If we have printer entries which we did not treat yet because of
9002      update_cups_queues_max_per_call we push their timeouts by the
9003      value of pause_between_cups_queue_updates into the future, so
9004      that they only get worked on then. Also printer entries which are
9005      scheduled in a time less than the value of
9006      pause_between_cups_queue_updates will be pushed, so that
9007      update_cups_queues will run the next time only after this
9008      interval */
9009   if (p && !in_shutdown)
9010     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
9011 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
9012       if (p->timeout <= current_time + pause_between_cups_queue_updates)
9013 	p->timeout = current_time + pause_between_cups_queue_updates;
9014 
9015  cannot_create:
9016   if (printer_attributes != NULL && num_cluster_printers != 1)
9017     ippDelete(printer_attributes);
9018 
9019   if (default_pagesize != NULL && num_cluster_printers != 1)
9020     free(default_pagesize);
9021 
9022   if (conflicts != NULL && num_cluster_printers != 1)
9023     cupsArrayDelete(conflicts);
9024 
9025   if (make_model != NULL && num_cluster_printers != 1)
9026     free(make_model);
9027 
9028   if (sizes != NULL && num_cluster_printers != 1)
9029     cupsArrayDelete(sizes);
9030 
9031   if (p && !in_shutdown)
9032     remove_printer_entry(p);
9033 
9034   log_all_printers();
9035 
9036   if (in_shutdown == 0)
9037     recheck_timer ();
9038 
9039   /* Don't run this callback again */
9040   return FALSE;
9041 }
9042 
9043 static void
recheck_timer(void)9044 recheck_timer (void)
9045 {
9046   remote_printer_t *p;
9047   time_t timeout = (time_t) -1;
9048   time_t now = time(NULL);
9049 
9050   if (!gmainloop)
9051     return;
9052 
9053   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
9054        p;
9055        p = (remote_printer_t *)cupsArrayNext(remote_printers))
9056     if (p->timeout == (time_t) -1)
9057       continue;
9058     else if (now > p->timeout) {
9059       timeout = 0;
9060       break;
9061     } else if (timeout == (time_t) -1 || p->timeout - now < timeout)
9062       timeout = p->timeout - now;
9063 
9064   if (queues_timer_id)
9065     g_source_remove (queues_timer_id);
9066 
9067   if (timeout != (time_t) -1) {
9068     debug_printf("checking queues in %ds\n", timeout);
9069     queues_timer_id =
9070       g_timeout_add_seconds (timeout, update_cups_queues, NULL);
9071   } else {
9072     debug_printf("listening\n");
9073     queues_timer_id = 0;
9074   }
9075 }
9076 
9077 static gboolean
matched_filters(const char * queue_name,const char * host,uint16_t port,const char * service_name,const char * domain,void * txt)9078 matched_filters (const char *queue_name,
9079 		 const char *host,
9080 		 uint16_t port,
9081 		 const char *service_name,
9082 		 const char *domain,
9083 		 void *txt) {
9084   browse_filter_t *filter;
9085   const char *property = NULL;
9086   char buf[10];
9087 #ifdef HAVE_AVAHI
9088   AvahiStringList *entry = NULL;
9089   char *key = NULL, *value = NULL;
9090 #endif /* HAVE_AVAHI */
9091 
9092   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",
9093 	       queue_name, host, port, service_name, domain);
9094   /* Go through all BrowseFilter lines and stop if one line does not match,
9095      rejecting this printer */
9096   for (filter = cupsArrayFirst (browsefilter);
9097        filter;
9098        filter = cupsArrayNext (browsefilter)) {
9099     debug_printf("Matching with line \"BrowseFilter %s%s%s %s\"",
9100 		 (filter->sense == FILTER_NOT_MATCH ? "NOT " : ""),
9101 		 (filter->regexp && !filter->cregexp ? "EXACT " : ""),
9102 		 filter->field, (filter->regexp ? filter->regexp : ""));
9103 #ifdef HAVE_AVAHI
9104     /* Go through the TXT record to see whether this rule applies to a field
9105        in there */
9106     if (txt) {
9107       entry = avahi_string_list_find((AvahiStringList *)txt, filter->field);
9108       if (entry) {
9109 	avahi_string_list_get_pair(entry, &key, &value, NULL);
9110 	if (key) {
9111 	  debug_printf(", TXT record entry: %s = %s",
9112 		       key, (value ? value : ""));
9113 	  if (filter->regexp) {
9114 	    /* match regexp */
9115 	    if (!value)
9116 	      value = strdup("");
9117 	    if ((filter->cregexp &&
9118 		 regexec(filter->cregexp, value, 0, NULL, 0) == 0) ||
9119 		(!filter->cregexp && !strcasecmp(filter->regexp, value))) {
9120 	      if (filter->sense == FILTER_NOT_MATCH) {
9121 		avahi_free(key);
9122 		avahi_free(value);
9123 		goto filter_failed;
9124 	      }
9125 	    } else {
9126 	      if (filter->sense == FILTER_MATCH) {
9127 		avahi_free(key);
9128 		avahi_free(value);
9129 		goto filter_failed;
9130 	      }
9131 	    }
9132 	  } else {
9133 	    /* match boolean value */
9134 	    if (filter->sense == FILTER_MATCH) {
9135 	      if (!value || strcasecmp(value, "T")) {
9136 		avahi_free(key);
9137 		avahi_free(value);
9138 		goto filter_failed;
9139 	      }
9140 	    } else {
9141 	      if (value && !strcasecmp(value, "T")) {
9142 		avahi_free(key);
9143 		avahi_free(value);
9144 		goto filter_failed;
9145 	      }
9146 	    }
9147 	  }
9148 	}
9149 	avahi_free(key);
9150 	avahi_free(value);
9151 	goto filter_matched;
9152       }
9153     }
9154 #endif /* HAVE_AVAHI */
9155 
9156     /* Does one of the properties outside the TXT record match? */
9157     property = buf;
9158     buf[0] = '\0';
9159     if (!strcasecmp(filter->field, "Name") ||
9160 	!strcasecmp(filter->field, "Printer") ||
9161 	!strcasecmp(filter->field, "PrinterName") ||
9162 	!strcasecmp(filter->field, "Queue") ||
9163 	!strcasecmp(filter->field, "QueueName")) {
9164       if (queue_name)
9165 	property = queue_name;
9166     } else if (!strcasecmp(filter->field, "Host") ||
9167 	       !strcasecmp(filter->field, "HostName") ||
9168 	       !strcasecmp(filter->field, "RemoteHost") ||
9169 	       !strcasecmp(filter->field, "RemoteHostName") ||
9170 	       !strcasecmp(filter->field, "Server") ||
9171 	       !strcasecmp(filter->field, "ServerName")) {
9172       if (host)
9173 	property = host;
9174     } else if (!strcasecmp(filter->field, "Port")) {
9175       if (port)
9176 	snprintf(buf, sizeof(buf), "%d", port);
9177     } else if (!strcasecmp(filter->field, "Service") ||
9178 	       !strcasecmp(filter->field, "ServiceName")) {
9179       if (service_name)
9180 	property = service_name;
9181     } else if (!strcasecmp(filter->field, "Domain")) {
9182       if (domain)
9183 	property = domain;
9184     } else
9185       property = NULL;
9186     if (property) {
9187       if (!filter->regexp)
9188 	filter->regexp = "";
9189       if ((filter->cregexp &&
9190 	   regexec(filter->cregexp, property, 0, NULL, 0) == 0) ||
9191 	  (!filter->cregexp && !strcasecmp(filter->regexp, property))) {
9192 	if (filter->sense == FILTER_NOT_MATCH)
9193 	  goto filter_failed;
9194       } else {
9195 	if (filter->sense == FILTER_MATCH)
9196 	  goto filter_failed;
9197       }
9198       goto filter_matched;
9199     }
9200 
9201     debug_printf(": Field not found --> SKIPPED\n");
9202     continue;
9203 
9204   filter_matched:
9205     debug_printf(" --> MATCHED\n");
9206   }
9207 
9208   /* All BrowseFilter lines matching, accept this printer */
9209   debug_printf("All BrowseFilter lines matched or skipped, accepting printer %s\n",
9210 	       queue_name);
9211   return TRUE;
9212 
9213  filter_failed:
9214   debug_printf(" --> FAILED\n");
9215   debug_printf("One BrowseFilter line did not match, ignoring printer %s\n",
9216 	       queue_name);
9217   return FALSE;
9218 }
9219 
9220 static gboolean
update_netifs(gpointer data)9221 update_netifs (gpointer data)
9222 {
9223   struct ifaddrs *ifaddr, *ifa;
9224   netif_t *iface, *iface2;
9225   int i, add_to_netifs, addr_size, dupe, if_found, addr_found;
9226   char *host, buf[HTTP_MAX_HOST], *p, list[65536], *l;
9227 
9228   debug_printf("update_netifs() in THREAD %ld\n", pthread_self());
9229 
9230   update_netifs_sourceid = 0;
9231   if (getifaddrs (&ifaddr) == -1) {
9232     debug_printf("unable to get interface addresses: %s\n",
9233 		 strerror (errno));
9234     return FALSE;
9235   }
9236 
9237   while ((iface = cupsArrayFirst (netifs)) != NULL) {
9238     cupsArrayRemove (netifs, iface);
9239     free (iface->address);
9240     free (iface);
9241   }
9242   while ((host = cupsArrayFirst (local_hostnames)) != NULL) {
9243     cupsArrayRemove (local_hostnames, host);
9244     free (host);
9245   }
9246 
9247   memset(list, 0, sizeof(list));
9248   snprintf(list, sizeof(list) - 1, "Network interfaces: ");
9249   l = list + strlen(list);
9250 
9251   for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
9252     if_found = 0;
9253     addr_found = 0;
9254 
9255     netif_t *iface;
9256 
9257     add_to_netifs = 1;
9258 
9259     if (ifa->ifa_addr == NULL)
9260       continue;
9261 
9262     if (ifa->ifa_broadaddr == NULL)
9263       add_to_netifs = 0;
9264 
9265     if (ifa->ifa_flags & IFF_LOOPBACK)
9266       add_to_netifs = 0;
9267 
9268     if (!(ifa->ifa_flags & IFF_BROADCAST))
9269       add_to_netifs = 0;
9270 
9271     if (ifa->ifa_addr->sa_family == AF_INET)
9272       addr_size = sizeof (struct sockaddr_in);
9273     else if (ifa->ifa_addr->sa_family == AF_INET6)
9274       addr_size = sizeof (struct sockaddr_in6);
9275     else
9276       addr_size = 0;
9277     if (addr_size) {
9278       if (strlen(list) + strlen(ifa->ifa_name) + 1 <=
9279 	  sizeof(list)) {
9280 	snprintf(l, sizeof(list) - strlen(list) - 1,
9281 		 "%s", ifa->ifa_name);
9282 	l = list + strlen(list);
9283 	if_found = 1;
9284       }
9285       for (i = 0; i <= 1; i ++)
9286         if (getnameinfo (ifa->ifa_addr, addr_size,
9287 			 buf, HTTP_MAX_HOST, NULL, 0,
9288 			 i == 0 ? NI_NUMERICHOST : NI_NAMEREQD) == 0)
9289 	  if (buf[0]) {
9290 	    /* Cut off "%..." from IPv6 IP addresses */
9291 	    if (ifa->ifa_addr->sa_family == AF_INET6 && i == 0 &&
9292 		(p = strchr(buf, '%')) != NULL)
9293 	      *p = '\0';
9294 	    /* discard if we already have this name or address */
9295 	    dupe = 0;
9296 	    for (host = (char *)cupsArrayFirst (local_hostnames);
9297 		 host != NULL;
9298 		 host = (char *)cupsArrayNext (local_hostnames))
9299 	      if (strcasecmp(buf, host) == 0) {
9300 		dupe = 1;
9301 		break;
9302 	      }
9303 	    if (dupe == 0) {
9304 	      cupsArrayAdd (local_hostnames, strdup(buf));
9305 	      if (addr_found == 1 && strlen(list) + 3 <=
9306 		  sizeof(list)) {
9307 		snprintf(l, sizeof(list) - strlen(list) - 1,
9308 			 ", ");
9309 		l = list + strlen(list);
9310 	      }
9311 	      if (addr_found == 0 && strlen(list) + 3 <=
9312 		  sizeof(list)) {
9313 		snprintf(l, sizeof(list) - strlen(list) - 1,
9314 			 " (");
9315 		l = list + strlen(list);
9316 		addr_found = 1;
9317 	      }
9318 	      if (strlen(list) + strlen(buf) + 1 <=
9319 		  sizeof(list)) {
9320 		snprintf(l, sizeof(list) - strlen(list) - 1,
9321 			 "%s", buf);
9322 		l = list + strlen(list);
9323 	      }
9324 	    }
9325 	  }
9326     }
9327 
9328     if (add_to_netifs == 0)
9329       goto done;
9330 
9331     iface = malloc (sizeof (netif_t));
9332     if (iface == NULL) {
9333       debug_printf ("malloc failure\n");
9334       exit (1);
9335     }
9336 
9337     iface->address = malloc (HTTP_MAX_HOST);
9338     if (iface->address == NULL) {
9339       free (iface);
9340       debug_printf ("malloc failure\n");
9341       exit (1);
9342     }
9343 
9344     iface->address[0] = '\0';
9345     switch (ifa->ifa_addr->sa_family) {
9346     case AF_INET:
9347       /* copy broadcast addr/fill in port first to faciliate dupe compares */
9348       memcpy (&iface->broadcast, ifa->ifa_broadaddr,
9349 	      sizeof (struct sockaddr_in));
9350       iface->broadcast.ipv4.sin_port = htons (BrowsePort);
9351       /* discard if we already have an interface sharing the broadcast
9352 	 address */
9353       dupe = 0;
9354       for (iface2 = (netif_t *)cupsArrayFirst (netifs);
9355            iface2 != NULL;
9356            iface2 = (netif_t *)cupsArrayNext (netifs)) {
9357 	if (memcmp(&iface2->broadcast, &iface->broadcast,
9358 		   sizeof(struct sockaddr_in)) == 0) {
9359 	  dupe = 1;
9360 	  break;
9361 	}
9362       }
9363       if (dupe) break;
9364       getnameinfo (ifa->ifa_addr, sizeof (struct sockaddr_in),
9365 		   iface->address, HTTP_MAX_HOST,
9366 		   NULL, 0, NI_NUMERICHOST);
9367       break;
9368 
9369     case AF_INET6:
9370       if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *)(ifa->ifa_addr))
9371 				 ->sin6_addr))
9372 	break;
9373 
9374       /* see above for order */
9375       memcpy (&iface->broadcast, ifa->ifa_broadaddr,
9376 	      sizeof (struct sockaddr_in6));
9377       iface->broadcast.ipv6.sin6_port = htons (BrowsePort);
9378       /* discard alias addresses (identical broadcast) */
9379       dupe = 0;
9380       for (iface2 = (netif_t *)cupsArrayFirst (netifs);
9381            iface2 != NULL;
9382            iface2 = (netif_t *)cupsArrayNext (netifs)) {
9383 	if (memcmp(&iface2->broadcast, ifa->ifa_broadaddr,
9384 		   sizeof(struct sockaddr_in6)) == 0) {
9385 	  dupe = 1;
9386 	  break;
9387 	}
9388       }
9389       if (dupe) break;
9390       getnameinfo (ifa->ifa_addr, sizeof (struct sockaddr_in6),
9391 		   iface->address, HTTP_MAX_HOST, NULL, 0, NI_NUMERICHOST);
9392       break;
9393     }
9394 
9395     if (iface->address[0]) {
9396       cupsArrayAdd (netifs, iface);
9397       if (if_found == 1) {
9398 	if (addr_found == 1 && strlen(list) + 3 <= sizeof(list)) {
9399 	  snprintf(l, sizeof(list) - strlen(list) - 1,
9400 		   ", ");
9401 	  l = list + strlen(list);
9402 	}
9403 	if (addr_found == 0 && strlen(list) + 3 <= sizeof(list)) {
9404 	  snprintf(l, sizeof(list) - strlen(list) - 1,
9405 		   " (");
9406 	  l = list + strlen(list);
9407 	  addr_found = 1;
9408 	}
9409 	if (strlen(list) + strlen(iface->address) + 2 <= sizeof(list)) {
9410 	  snprintf(l, sizeof(list) - strlen(list) - 1,
9411 		   "%s*", iface->address);
9412 	  l = list + strlen(list);
9413 	}
9414       }
9415     } else {
9416       free (iface->address);
9417       free (iface);
9418     }
9419 
9420   done:
9421     if (if_found == 1) {
9422       if (addr_found == 1 && strlen(list) + 2 <= sizeof(list)) {
9423 	snprintf(l, sizeof(list) - strlen(list) - 1,
9424 		 ")");
9425 	l = list + strlen(list);
9426       }
9427       if (strlen(list) + 3 <= sizeof(list)) {
9428 	snprintf(l, sizeof(list) - strlen(list) - 1,
9429 		 ", ");
9430 	l = list + strlen(list);
9431       }
9432     }
9433   }
9434 
9435   if ((l = strrchr(list, ')')) != NULL) {
9436     if (strlen(list) + 2 <= sizeof(list))
9437     *(l + 1) = '\0';
9438   } else {
9439     if (strlen(list) + 5 <= sizeof(list))
9440       snprintf(list + strlen(list), sizeof(list) - strlen(list) - 1,
9441 	       "None");
9442   }
9443   debug_printf("%s\n", list);
9444 
9445   freeifaddrs (ifaddr);
9446 
9447   /* If run as a timeout, don't run it again. */
9448   return FALSE;
9449 }
9450 
9451 int
is_local_hostname(const char * host_name)9452 is_local_hostname(const char *host_name) {
9453   char *host;
9454 
9455   if (host_name == NULL)
9456     return 0;
9457 
9458   for (host = (char *)cupsArrayFirst (local_hostnames);
9459        host != NULL;
9460        host = (char *)cupsArrayNext (local_hostnames))
9461     if (strncasecmp(host_name, host, strlen(host)) == 0 &&
9462 	(strlen(host_name) == strlen(host) ||
9463 	 (strlen(host_name) > strlen(host) &&
9464 	  (strcasecmp(host_name + strlen(host), ".local") == 0 ||
9465 	   strcasecmp(host_name + strlen(host), ".local.") == 0))))
9466       return 1;
9467 
9468   return 0;
9469 }
9470 
9471 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)9472 examine_discovered_printer_record(const char *host,
9473 				  const char *ip,
9474 				  uint16_t port,
9475 				  char *resource,
9476 				  const char *service_name,
9477 				  const char *location,
9478 				  const char *info,
9479 				  const char *type,
9480 				  const char *domain,
9481 				  const char *interface,
9482 				  int family,
9483 				  void *txt) {
9484 
9485   char uri[HTTP_MAX_URI];
9486   char *remote_host = NULL, *pdl = NULL,
9487        *make_model = NULL;
9488   int color = 1, duplex = 1;
9489 #ifdef HAVE_AVAHI
9490   char *fields[] = { "product", "usb_MDL", "ty", NULL }, **f;
9491   AvahiStringList *entry = NULL;
9492   char *key = NULL, *value = NULL;
9493   char *note_value = NULL;
9494   char service_host_name[1024];
9495 #endif /* HAVE_AVAHI */
9496   remote_printer_t *p = NULL, key_rec;
9497   char *local_queue_name = NULL;
9498   int is_cups_queue;
9499   int raw_queue = 0;
9500   char *ptr;
9501 
9502   if (!host || !resource || !service_name || !location || !info || !type ||
9503       !domain) {
9504     debug_printf("ERROR: examine_discovered_printer_record(): Input value missing!\n");
9505     return NULL;
9506   }
9507 
9508   is_cups_queue = 0;
9509   memset(uri, 0, sizeof(uri));
9510 
9511   /* Find the remote host name.
9512      Used in constructing backup queue name, so need to sanitize.
9513      strdup() is called inside remove_bad_chars() and result is free()-able. */
9514   remote_host = remove_bad_chars(host, 1);
9515 
9516   /* If we only want to create queues for printers for which CUPS does
9517      not already auto-create queues, we check here whether we can skip
9518      this printer */
9519   if (OnlyUnsupportedByCUPS) {
9520     if (g_hash_table_find (cups_supported_remote_printers,
9521 			   local_printer_service_name_matches,
9522 			   (gpointer *)service_name)) {
9523       /* Found a DNS-SD-discovered CUPS-supported printer whose service name
9524 	 matches our discovered printer */
9525       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",
9526 		   service_name);
9527       goto fail;
9528     }
9529   }
9530 
9531 #ifdef HAVE_AVAHI
9532   if (txt) {
9533     /* Find make and model by the TXT record */
9534     for (f = fields; *f; f ++) {
9535       entry = avahi_string_list_find((AvahiStringList *)txt, *f);
9536       if (entry) {
9537 	avahi_string_list_get_pair(entry, &key, &value, NULL);
9538 	if (key && value && !strcasecmp(key, *f) && strlen(value) >= 3) {
9539 	  if (!strcasecmp(key, "product")) {
9540 	    make_model = strdup(value + 1);
9541 	    make_model[strlen(make_model) - 1] = '\0';
9542 	  } else
9543 	    make_model = strdup(value);
9544 	  avahi_free(key);
9545 	  avahi_free(value);
9546 	  break;
9547 	}
9548 	avahi_free(key);
9549 	avahi_free(value);
9550       }
9551     }
9552     /* Check by the printer-type TXT field whether the discovered printer is a
9553        CUPS queue */
9554     entry = avahi_string_list_find((AvahiStringList *)txt, "printer-type");
9555     if (entry) {
9556       avahi_string_list_get_pair(entry, &key, &value, NULL);
9557       if (key && value && strlen(value) > 1 &&
9558 	  !strcasecmp(key, "printer-type") && value[0] == '0' &&
9559 	  value[1] == 'x') {
9560 	is_cups_queue = 1;
9561       }
9562       avahi_free(key);
9563       avahi_free(value);
9564     }
9565   }
9566 #else
9567   /* Check by the resource whether the discovered printer is a CUPS queue */
9568   if (!strncasecmp(resource, "printers/", 9) ||
9569       !strncasecmp(resource, "classes/", 8))
9570     /* This is a remote CUPS queue or class */
9571     is_cups_queue = 1;
9572 #endif /* HAVE_AVAHI */
9573   /* If we do not have a TXT record the printer was not discovered via
9574      DNS-SD but via CUPS legacy or LDAP, so it is a remote CUPS queue
9575      and not an IPP network printer. */
9576   if (txt == NULL)
9577     is_cups_queue = 1;
9578   if (is_cups_queue)
9579     debug_printf("Found CUPS queue/class: %s on host %s.\n",
9580 		 strrchr(resource, '/') + 1, remote_host);
9581 #ifdef HAVE_AVAHI
9582   if (is_cups_queue) {
9583     /* If the remote queue has a PPD file, the "product" field of the
9584        TXT record is populated. If it has no PPD file the remote queue
9585        is a raw queue and so we do not know enough about the printer
9586        behind it for auto-creating a local queue pointing to it. */
9587     if (txt) {
9588       entry = avahi_string_list_find((AvahiStringList *)txt, "product");
9589       if (entry) {
9590 	avahi_string_list_get_pair(entry, &key, &value, NULL);
9591 	if (!key || !value || strcasecmp(key, "product") || value[0] != '(' ||
9592 	    value[strlen(value) - 1] != ')') {
9593 	  raw_queue = 1;
9594 	}
9595 	avahi_free(key);
9596 	avahi_free(value);
9597       } else
9598 	raw_queue = 1;
9599     } else if (domain && domain[0] != '\0')
9600       raw_queue = 1;
9601     if (raw_queue && CreateRemoteRawPrinterQueues == 0) {
9602       /* The remote CUPS queue is raw, ignore it */
9603       debug_printf("Remote DNS-SD-advertised CUPS queue %s on host %s is raw, ignored.\n",
9604 		   strrchr(resource, '/') + 1, remote_host);
9605       free (remote_host);
9606       if (make_model) free (make_model);
9607       return NULL;
9608     }
9609   } else {
9610     if (txt) {
9611       /* Find out which PDLs the printer understands */
9612       entry = avahi_string_list_find((AvahiStringList *)txt, "pdl");
9613       if (entry) {
9614 	avahi_string_list_get_pair(entry, &key, &value, NULL);
9615 	if (key && value && !strcasecmp(key, "pdl") && strlen(value) >= 3) {
9616 	  pdl = remove_bad_chars(value, 1);
9617 	}
9618 	avahi_free(key);
9619 	avahi_free(value);
9620       }
9621       /* Find out if we have a color printer */
9622       entry = avahi_string_list_find((AvahiStringList *)txt, "Color");
9623       if (entry) {
9624 	avahi_string_list_get_pair(entry, &key, &value, NULL);
9625 	if (key && value && !strcasecmp(key, "Color")) {
9626 	  if (!strcasecmp(value, "T")) color = 1;
9627 	  if (!strcasecmp(value, "F")) color = 0;
9628 	}
9629 	avahi_free(key);
9630 	avahi_free(value);
9631       }
9632       /* Find out if we have a duplex printer */
9633       entry = avahi_string_list_find((AvahiStringList *)txt, "Duplex");
9634       if (entry) {
9635 	avahi_string_list_get_pair(entry, &key, &value, NULL);
9636 	if (key && value && !strcasecmp(key, "Duplex")) {
9637 	  if (!strcasecmp(value, "T")) duplex = 1;
9638 	  if (!strcasecmp(value, "F")) duplex = 0;
9639 	}
9640 	avahi_free(key);
9641 	avahi_free(value);
9642       }
9643     }
9644   }
9645   /* Extract location from DNS-SD TXT record's "note" field */
9646   if (location[0] == '\0') {
9647     if (txt) {
9648       entry = avahi_string_list_find((AvahiStringList *)txt, "note");
9649       if (entry) {
9650 	avahi_string_list_get_pair(entry, &key, &note_value, NULL);
9651 	if (key && note_value && !strcasecmp(key, "note")) {
9652 	  debug_printf("examine_discovered_printer_record: TXT.note: |%s|\n",
9653 		       note_value); /* !! */
9654 	  location = note_value;
9655 	}
9656         avahi_free(key);
9657         /* don't avahi_free(note_value) here! */
9658       }
9659     }
9660   }
9661   /* A NULL location is only passed in from resolve_callback(), which is
9662      HAVE_AVAHI */
9663 #endif /* HAVE_AVAHI */
9664 
9665   /* Determine the device URI of the remote printer */
9666 #ifdef HAVE_AVAHI
9667   if (txt && DNSSDBasedDeviceURIs) {
9668     /* Printer is DNS-SD-discovered, so we can give a DNS-SD-service-name-based
9669        device URI to it (only if DNSSDBasedDeviceURIs config option is set) */
9670     snprintf(service_host_name, sizeof(service_host_name), "%s.%s.%s",
9671 	     service_name, type, domain);
9672     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri) - 1,
9673 		     (strcasestr(type, "_ipps") ? "ipps" : "ipp"), NULL,
9674 		     service_host_name, 0, "/%s",
9675 		     (is_cups_queue ? "cups" : ""));
9676   } else
9677 #endif /* HAVE_AVAHI */
9678     /* Printer is discovered via legacy CUPS or LDAP, so we have to give
9679        a IP-based/host-name-based URI to it ( or for DNS-SD-discovered
9680        printers if DNSSDBasedDeviceURIs config option is not set) */
9681     httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri) - 1,
9682 		     (strcasestr(type, "_ipps") ? "ipps" : "ipp"), NULL,
9683 		     (ip != NULL ? ip : host), port, "/%s", resource);
9684 
9685   /* Determine the queue name */
9686   local_queue_name = get_local_queue_name(service_name, make_model, resource,
9687 					  remote_host, &is_cups_queue, NULL);
9688   if (local_queue_name == NULL)
9689     goto fail;
9690 
9691   if (!matched_filters (local_queue_name, remote_host, port, service_name,
9692 			domain, txt)) {
9693     debug_printf("Printer %s does not match BrowseFilter lines in cups-browsed.conf, printer ignored.\n",
9694 		 local_queue_name);
9695     goto fail;
9696   }
9697 
9698 
9699   /* Update network interface info if we were discovered by LDAP
9700      or legacy CUPS, needed for the is_local_hostname() function calls.
9701      During DNS-SD discovery the update is already done by the Avahi
9702      event handler function. */
9703   if (type == NULL || type[0] == '\0')
9704     update_netifs(NULL);
9705 
9706   /* Check if we have already created a queue for the discovered
9707      printer */
9708   for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
9709        p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
9710     if (!strcasecmp(p->queue_name, local_queue_name) &&
9711 	(p->host[0] == '\0' ||
9712 	 p->status == STATUS_UNCONFIRMED ||
9713 	 p->status == STATUS_DISAPPEARED ||
9714 	 ((!strcasecmp(p->host, remote_host) ||
9715 	   (is_local_hostname(p->host) && is_local_hostname(remote_host))) &&
9716 	  (p->port == port ||
9717 	   (p->port == 631 && port == 443) ||
9718 	   (p->port == 443 && port == 631)) &&
9719 	  (txt ||
9720 	   (strlen(p->uri) - strlen(resource) > 0 &&
9721 	    !strcasecmp(p->uri + strlen(p->uri) - strlen(resource),
9722 			resource))))))
9723       break;
9724 
9725   /* Is there a local queue with the same URI as the remote queue? */
9726   if (!p) {
9727     memset(&key_rec, 0, sizeof(key_rec));
9728     key_rec.uri = uri;
9729     key_rec.host = remote_host;
9730     key_rec.port = port;
9731     key_rec.resource = resource;
9732     key_rec.service_name = (char *)service_name;
9733     key_rec.type = (char *)type;
9734     key_rec.domain = (char *)domain;
9735     if (g_hash_table_find (local_printers,
9736 			   local_printer_is_same_device, &key_rec)) {
9737       /* Found a local queue with the same URI as our discovered printer
9738 	 would get, so ignore this remote printer */
9739       debug_printf("Printer with URI %s (or IPP/IPPS equivalent) already exists, printer ignored.\n",
9740 		   uri);
9741       goto fail;
9742     }
9743 
9744     /* We need to create a local queue pointing to the
9745        discovered printer */
9746     p = create_remote_printer_entry (local_queue_name, location, info, uri,
9747 				     remote_host, ip, port, resource,
9748 				     service_name ? service_name : "", type,
9749 				     domain, interface, family, pdl, color,
9750 				     duplex, make_model, is_cups_queue);
9751   } else {
9752     debug_printf("Entry for %s (URI: %s) already exists.\n",
9753 		 p->queue_name, p->uri);
9754     /* We have already created a local queue, check whether the
9755        discovered service allows us to upgrade the queue to IPPS
9756        or whether the URI part after ipp(s):// has changed, or
9757        whether the discovered queue is discovered via DNS-SD
9758        having more info in contrary to the existing being
9759        discovered by legacy CUPS or LDAP */
9760 
9761     int downgrade = 0, upgrade = 0;
9762 
9763     /* Get first element of array of interfaces on which this printer
9764        got already discovered, as this one is "lo" when it already got
9765        discovered through the loopback interface (preferred interface) */
9766     ipp_discovery_t *ippdis = cupsArrayFirst(p->ipp_discoveries);
9767 
9768     /* Force upgrade if the found entry is marked unconfirmed or
9769        disappeared */
9770     if (p->status == STATUS_UNCONFIRMED ||
9771 	p->status == STATUS_DISAPPEARED) {
9772       upgrade = 1;
9773       debug_printf("Replacing printer entry %s (Host: %s, Port: %d) as it was marked %s. New URI: %s\n",
9774 		   p->queue_name, remote_host, port,
9775 		   (p->status == STATUS_UNCONFIRMED ? "unconfirmed" :
9776 		    "disappeared"),
9777 		   uri);
9778     /* Check if there is a downgrade */
9779     /* IPPS -> IPP */
9780     } else if ((ptr = strcasestr(type, "_ipp")) != NULL &&
9781 	       *(ptr + 4) != 's' &&
9782 	       !strncasecmp(p->uri, "ipps:", 5)) {
9783       downgrade = 1;
9784       debug_printf("Printer %s: New discovered service from host %s, port %d, URI %s is only IPP, we have already IPPS, skipping\n",
9785 		   p->queue_name, remote_host, port, uri);
9786     /* "lo" -> Any non-"lo" interface */
9787     } else if (strcasecmp(interface, "lo") &&
9788 	       ippdis && !strcasecmp(ippdis->interface, "lo")) {
9789       downgrade = 1;
9790       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",
9791 		   p->queue_name, remote_host, port, uri);
9792     /* DNS-SD -> CUPS Legacy/LDAP */
9793     } else if (p->domain != NULL && p->domain[0] != '\0' &&
9794 	       (domain == NULL || domain[0] == '\0') &&
9795 	       p->type != NULL && p->type[0] != '\0' &&
9796 	       (type == NULL || type[0] == '\0')) {
9797       downgrade = 1;
9798       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",
9799 		   p->queue_name, remote_host, port, uri);
9800     }
9801 
9802     if (downgrade == 0) {
9803       /* Check if there is an upgrade */
9804       /* IPP -> IPPS */
9805       if (strcasestr(type, "_ipps") &&
9806 	  !strncasecmp(p->uri, "ipp:", 4)) {
9807 	upgrade = 1;
9808 	debug_printf("Upgrading printer %s (Host: %s, Port: %d) to IPPS. New URI: %s\n",
9809 		     p->queue_name, remote_host, port, uri);
9810       /* Any non-"lo" interface -> "lo" */
9811       } else if (!strcasecmp(interface, "lo")) {
9812 	upgrade = 1;
9813 	debug_printf("Upgrading printer %s (Host: %s, Port: %d) to use loopback interface \"lo\". New URI: %s\n",
9814 		     p->queue_name, remote_host, port, uri);
9815       /* CUPS Legacy/LDAP -> DNS-SD */
9816       } else if ((p->domain == NULL || p->domain[0] == '\0') &&
9817 		 domain != NULL && domain[0] != '\0' &&
9818 		 (p->type == NULL || p->type[0] == '\0') &&
9819 		 type != NULL && type[0] != '\0') {
9820 	upgrade = 1;
9821 	debug_printf("Discovered printer %s (Host: %s, Port: %d, URI: %s) by DNS-SD now.\n",
9822 		     p->queue_name, remote_host, port, uri);
9823       }
9824     }
9825 
9826     /* Switch local queue over to this newly discovered service */
9827     if (upgrade == 1) {
9828       /* Remove tiemout of legacy CUPS broadcasting */
9829       if (domain != NULL && domain[0] != '\0' &&
9830 	  type != NULL && type[0] != '\0' &&
9831 	  p->is_legacy) {
9832 	p->is_legacy = 0;
9833 	if (p->status == STATUS_CONFIRMED)
9834 	  p->timeout = (time_t) -1;
9835       }
9836       free(p->queue_name);
9837       free(p->location);
9838       free(p->info);
9839       free(p->make_model);
9840       free(p->pdl);
9841       free(p->uri);
9842       free(p->host);
9843       free(p->ip);
9844       free(p->resource);
9845       free(p->service_name);
9846       free(p->type);
9847       free(p->domain);
9848       p->queue_name = strdup(local_queue_name);
9849       p->location = strdup(location);
9850       p->info = strdup(info);
9851       p->make_model = (make_model != NULL ? strdup(make_model) : NULL);
9852       p->pdl = (pdl != NULL ? strdup(pdl) : NULL);
9853       p->color = color;
9854       p->duplex = duplex;
9855       p->uri = strdup(uri);
9856       p->status = STATUS_TO_BE_CREATED;
9857       p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
9858       p->host = strdup(remote_host);
9859       p->ip = (ip != NULL ? strdup(ip) : NULL);
9860       p->port = port;
9861       p->resource = strdup(resource);
9862       p->service_name = strdup(service_name);
9863       p->type = strdup(type);
9864       p->domain = strdup(domain);
9865       debug_printf("Switched over to newly discovered entry for this printer.\n");
9866     } else
9867       debug_printf("Staying with previously discovered entry for this printer.\n");
9868 
9869     /* Mark queue entry as confirmed if the entry
9870        is unconfirmed */
9871     if (p->status == STATUS_UNCONFIRMED ||
9872 	p->status == STATUS_DISAPPEARED) {
9873       debug_printf("Marking entry for %s (URI: %s) as confirmed.\n",
9874 		   p->queue_name, p->uri);
9875       p->status = STATUS_CONFIRMED;
9876       if (p->is_legacy) {
9877 	p->timeout = time(NULL) + BrowseTimeout;
9878 	debug_printf("starting BrowseTimeout timer for %s (%ds)\n",
9879 		     p->queue_name, BrowseTimeout);
9880       } else
9881 	p->timeout = (time_t) -1;
9882       /* If this queue was the default printer in its previous life, make
9883 	 it the default printer again. */
9884       queue_creation_handle_default(p->queue_name);
9885       /* If this queue is disabled, re-enable it. */
9886       enable_printer(p->queue_name);
9887       /* Record the options, to record any changes which happened
9888 	 while cups-browsed was not running */
9889       record_printer_options(p->queue_name);
9890     }
9891 
9892     /* Gather extra info from our new discovery */
9893     if (p->uri[0] == '\0') {
9894       free (p->uri);
9895       p->uri = strdup(uri);
9896     }
9897     if (p->location[0] == '\0') {
9898       free (p->location);
9899       p->location = strdup(location);
9900     }
9901     if (p->info[0] == '\0') {
9902       free (p->info);
9903       p->info = strdup(info);
9904     }
9905     if (p->make_model == NULL || p->make_model[0] == '\0') {
9906       if (p->make_model) free (p->make_model);
9907       p->make_model = (make_model != NULL ? strdup(make_model) : NULL);
9908     }
9909     if (p->pdl == NULL || p->pdl[0] == '\0') {
9910       if (p->pdl) free (p->pdl);
9911       p->pdl = (pdl != NULL ? strdup(pdl) : NULL);
9912     }
9913     p->color = color;
9914     p->duplex = duplex;
9915     if (p->host[0] == '\0') {
9916       free (p->host);
9917       p->host = strdup(remote_host);
9918     }
9919     if (p->ip == NULL || p->ip[0] == '\0') {
9920       if (p->ip) free (p->ip);
9921       p->ip = (ip != NULL ? strdup(ip) : NULL);
9922     }
9923     if (p->port == 0)
9924       p->port = port;
9925     if (p->service_name[0] == '\0' && service_name) {
9926       free (p->service_name);
9927       p->service_name = strdup(service_name);
9928     }
9929     if (p->resource[0] == '\0') {
9930       free (p->resource);
9931       p->resource = strdup(resource);
9932     }
9933     if (p->type[0] == '\0' && type) {
9934       free (p->type);
9935       p->type = strdup(type);
9936     }
9937     if (p->domain[0] == '\0' && domain) {
9938       free (p->domain);
9939       p->domain = strdup(domain);
9940     }
9941     if (domain != NULL && domain[0] != '\0' &&
9942 	type != NULL && type[0] != '\0')
9943       ipp_discoveries_add(p->ipp_discoveries, interface, type, family);
9944     p->netprinter = is_cups_queue ? 0 : 1;
9945   }
9946 
9947  fail:
9948   free (remote_host);
9949   free (pdl);
9950   free (make_model);
9951   free (local_queue_name);
9952 #ifdef HAVE_AVAHI
9953   if (note_value) avahi_free(note_value);
9954 #endif /* HAVE_AVAHI */
9955 
9956   if (p)
9957     debug_printf("DNS-SD IDs: Service name: \"%s\", "
9958 		 "Service type: \"%s\", Domain: \"%s\"\n",
9959 		 p->service_name, p->type, p->domain);
9960 
9961   return p;
9962 }
9963 
9964 static gboolean
allowed(struct sockaddr * srcaddr)9965 allowed (struct sockaddr *srcaddr)
9966 {
9967   allow_t *allow;
9968   int i;
9969   gboolean server_allowed;
9970   allow_sense_t sense;
9971 
9972   if (browse_order == ORDER_DENY_ALLOW)
9973     /* BrowseOrder Deny,Allow: Allow server, then apply BrowseDeny lines,
9974        after that BrowseAllow lines */
9975     server_allowed = TRUE;
9976   else
9977     /* BrowseOrder Allow,Deny: Deny server, then apply BrowseAllow lines,
9978        after that BrowseDeny lines */
9979     server_allowed = FALSE;
9980 
9981   for (i = 0; i <= 1; i ++) {
9982     if (browse_order == ORDER_DENY_ALLOW)
9983       /* Treat BrowseDeny lines first, then BrowseAllow lines */
9984       sense = (i == 0 ? ALLOW_DENY : ALLOW_ALLOW);
9985     else
9986       /* Treat BrowseAllow lines first, then BrowseDeny lines */
9987       sense = (i == 0 ? ALLOW_ALLOW : ALLOW_DENY);
9988 
9989     if (server_allowed == (sense == ALLOW_ALLOW ? TRUE : FALSE))
9990       continue;
9991 
9992     if (browseallow_all && sense == ALLOW_ALLOW) {
9993       server_allowed = TRUE;
9994       continue;
9995     }
9996     if (browsedeny_all && sense == ALLOW_DENY) {
9997       server_allowed = FALSE;
9998       continue;
9999     }
10000 
10001     for (allow = cupsArrayFirst (browseallow);
10002 	 allow;
10003 	 allow = cupsArrayNext (browseallow)) {
10004       if (allow->sense != sense)
10005 	continue;
10006 
10007       switch (allow->type) {
10008       case ALLOW_INVALID:
10009 	break;
10010 
10011       case ALLOW_IP:
10012 	switch (srcaddr->sa_family) {
10013 	case AF_INET:
10014 	  if (((struct sockaddr_in *) srcaddr)->sin_addr.s_addr ==
10015 	      allow->addr.ipv4.sin_addr.s_addr) {
10016 	    server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE);
10017 	    goto match;
10018 	  }
10019 	  break;
10020 
10021 	case AF_INET6:
10022 	  if (!memcmp (&((struct sockaddr_in6 *) srcaddr)->sin6_addr,
10023 		       &allow->addr.ipv6.sin6_addr,
10024 		       sizeof (allow->addr.ipv6.sin6_addr))) {
10025 	    server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE);
10026 	    goto match;
10027 	  }
10028 	  break;
10029 	}
10030 	break;
10031 
10032       case ALLOW_NET:
10033 	switch (srcaddr->sa_family) {
10034 	  struct sockaddr_in6 *src6addr;
10035 
10036 	case AF_INET:
10037 	  if ((((struct sockaddr_in *) srcaddr)->sin_addr.s_addr &
10038 	       allow->mask.ipv4.sin_addr.s_addr) ==
10039 	      allow->addr.ipv4.sin_addr.s_addr) {
10040 	    server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE);
10041 	    goto match;
10042 	  }
10043 	  break;
10044 
10045 	case AF_INET6:
10046 	  src6addr = (struct sockaddr_in6 *) srcaddr;
10047 	  if (((src6addr->sin6_addr.s6_addr[0] &
10048 		allow->mask.ipv6.sin6_addr.s6_addr[0]) ==
10049 	       allow->addr.ipv6.sin6_addr.s6_addr[0]) &&
10050 	      ((src6addr->sin6_addr.s6_addr[1] &
10051 		allow->mask.ipv6.sin6_addr.s6_addr[1]) ==
10052 	       allow->addr.ipv6.sin6_addr.s6_addr[1]) &&
10053 	      ((src6addr->sin6_addr.s6_addr[2] &
10054 		allow->mask.ipv6.sin6_addr.s6_addr[2]) ==
10055 	       allow->addr.ipv6.sin6_addr.s6_addr[2]) &&
10056 	      ((src6addr->sin6_addr.s6_addr[3] &
10057 		allow->mask.ipv6.sin6_addr.s6_addr[3]) ==
10058 	       allow->addr.ipv6.sin6_addr.s6_addr[3])) {
10059 	    server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE);
10060 	    goto match;
10061 	  }
10062 	  break;
10063 	}
10064       }
10065     }
10066   match:
10067     continue;
10068   }
10069 
10070   return server_allowed;
10071 }
10072 
10073 #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)10074 static void resolve_callback(AvahiServiceResolver *r,
10075 			     AvahiIfIndex interface,
10076 			     AvahiProtocol protocol,
10077 			     AvahiResolverEvent event,
10078 			     const char *name,
10079 			     const char *type,
10080 			     const char *domain,
10081 			     const char *host_name,
10082 			     const AvahiAddress *address,
10083 			     uint16_t port,
10084 			     AvahiStringList *txt,
10085 			     AvahiLookupResultFlags flags,
10086 			     AVAHI_GCC_UNUSED void* userdata) {
10087   char ifname[IF_NAMESIZE];
10088   AvahiStringList *uuid_entry, *printer_type_entry;
10089   char *uuid_key, *uuid_value;
10090 
10091   debug_printf("resolve_callback() in THREAD %ld\n", pthread_self());
10092 
10093   if (r == NULL || name == NULL || type == NULL || domain == NULL)
10094     return;
10095 
10096   /* Get the interface name */
10097   if (!if_indextoname(interface, ifname)) {
10098     debug_printf("Unable to find interface name for interface %d: %s\n",
10099 		 interface, strerror(errno));
10100     strncpy(ifname, "Unknown", sizeof(ifname) - 1);
10101   }
10102 
10103   /* Ignore local queues of the cupsd we are serving for, identifying them
10104      via UUID */
10105   update_netifs(NULL);
10106   if ((flags & AVAHI_LOOKUP_RESULT_LOCAL) || !strcasecmp(ifname, "lo") ||
10107       is_local_hostname(host_name)) {
10108     update_local_printers ();
10109     uuid_value = NULL;
10110     if (txt && (uuid_entry = avahi_string_list_find(txt, "UUID")))
10111       avahi_string_list_get_pair(uuid_entry, &uuid_key, &uuid_value, NULL);
10112     if (uuid_value && g_hash_table_find (local_printers,
10113 					 local_printer_has_uuid,
10114 					 uuid_value)) {
10115       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",
10116 		   name, type, domain, host_name, port, ifname,
10117 		   (address ?
10118 		    (address->proto == AVAHI_PROTO_INET ? "IPv4" :
10119 		     address->proto == AVAHI_PROTO_INET6 ? "IPv6" :
10120 		     "IPv4/IPv6 Unknown") :
10121 		    "IPv4/IPv6 Unknown"), uuid_value);
10122       goto ignore;
10123     }
10124     if (txt &&
10125 	(printer_type_entry = avahi_string_list_find(txt, "printer-type")) &&
10126 	strcasestr(type, "_ipps")) {
10127       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",
10128 		   name, type, domain, host_name, port, ifname,
10129 		   (address ?
10130 		    (address->proto == AVAHI_PROTO_INET ? "IPv4" :
10131 		     address->proto == AVAHI_PROTO_INET6 ? "IPv6" :
10132 		     "IPv4/IPv6 Unknown") :
10133 		    "IPv4/IPv6 Unknown"),
10134 		   (uuid_value ? uuid_value : "(unknown)"));
10135       goto ignore;
10136     }
10137   }
10138 
10139   /* Called whenever a service has been resolved successfully or timed out */
10140 
10141   switch (event) {
10142 
10143   /* Resolver error */
10144   case AVAHI_RESOLVER_FAILURE:
10145     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",
10146 		 name, type, domain, host_name, port, ifname,
10147 		 (address ?
10148 		  (address->proto == AVAHI_PROTO_INET ? "IPv4" :
10149 		   address->proto == AVAHI_PROTO_INET6 ? "IPv6" :
10150 		   "IPv4/IPv6 Unknown") :
10151 		  "IPv4/IPv6 Unknown"),
10152 		 avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
10153     break;
10154 
10155   /* New remote printer found */
10156   case AVAHI_RESOLVER_FOUND: {
10157     AvahiStringList *rp_entry, *adminurl_entry;
10158     char *rp_key, *rp_value, *adminurl_key, *adminurl_value;
10159 
10160     debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s).\n",
10161 		 name, type, domain, host_name, port, ifname,
10162 		 (address ?
10163 		  (address->proto == AVAHI_PROTO_INET ? "IPv4" :
10164 		   address->proto == AVAHI_PROTO_INET6 ? "IPv6" :
10165 		   "IPv4/IPv6 Unknown") :
10166 		  "IPv4/IPv6 Unknown"));
10167 
10168     /* Ignore if terminated (by SIGTERM) */
10169     if (terminating) {
10170       debug_printf("Avahi Resolver: Ignoring because cups-browsed is terminating.\n");
10171       break;
10172     }
10173 
10174     if (txt && (rp_entry = avahi_string_list_find(txt, "rp")))
10175       avahi_string_list_get_pair(rp_entry, &rp_key, &rp_value, NULL);
10176     else {
10177       rp_key = strdup("rp");
10178       rp_value = strdup("");
10179     }
10180     if (txt && (adminurl_entry = avahi_string_list_find(txt, "adminurl")))
10181       avahi_string_list_get_pair(adminurl_entry, &adminurl_key,
10182 				 &adminurl_value, NULL);
10183     else {
10184       adminurl_key = strdup("adminurl");
10185       if (host_name && (adminurl_value = malloc(strlen(host_name) + 8)) != NULL)
10186 	sprintf(adminurl_value, "http://%s", host_name);
10187       else
10188 	adminurl_value = strdup("");
10189     }
10190 
10191     /* If we create queues only for local IPP printers (like IPP-over-USB
10192        with ippusbxd) check whether the entry is local and skip if not.
10193        We also check for remote CUPS (with "printer-type" TXT field) as this
10194        option is only for IPP network printers */
10195     if (CreateIPPPrinterQueues == IPP_PRINTERS_LOCAL_ONLY &&
10196 	strcasecmp(ifname, "lo") &&
10197 	(!txt || avahi_string_list_find(txt, "printer-type") == NULL)) {
10198       debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, not a local service.\n",
10199 		   name, type, domain);
10200       goto clean_up;
10201     }
10202 
10203     if (txt && rp_key && rp_value && adminurl_key && adminurl_value &&
10204 	!strcasecmp(rp_key, "rp") && !strcasecmp(adminurl_key, "adminurl")) {
10205       char *p, instance[64];
10206       /* Extract instance from DNSSD service name (to serve as info field) */
10207       p = strstr(name, " @ ");
10208       if (p) {
10209 	int n;
10210 	n = p - name;
10211 	if (n >= sizeof(instance))
10212 	  n = sizeof(instance) - 1;
10213 	strncpy(instance, name, sizeof(instance) - 1);
10214 	instance[n] = '\0';
10215 	debug_printf("Avahi-Resolver: Instance: %s\n", instance); /* !! */
10216       } else {
10217 	instance[0] = '\0';
10218       }
10219       /* Determine the remote printer's IP */
10220       if (IPBasedDeviceURIs != IP_BASED_URIS_NO ||
10221 	  (!browseallow_all && cupsArrayCount(browseallow) > 0)) {
10222 	struct sockaddr saddr;
10223 	struct sockaddr *addr = &saddr;
10224 	char *addrstr;
10225 	int addrlen;
10226 	int addrfound = 0;
10227 	if ((addrstr = calloc(256, sizeof(char))) == NULL) {
10228 	  debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, could not allocate memory to determine IP address.\n",
10229 		       name, type, domain);
10230 	  goto clean_up;
10231 	}
10232 	if (address &&
10233 	    address->proto == AVAHI_PROTO_INET &&
10234 	    IPBasedDeviceURIs != IP_BASED_URIS_IPV6_ONLY) {
10235 	  avahi_address_snprint(addrstr, 256, address);
10236 	  addr->sa_family = AF_INET;
10237 	  if (inet_aton(addrstr,
10238 			&((struct sockaddr_in *) addr)->sin_addr) &&
10239 	      allowed(addr))
10240 	    addrfound = 1;
10241 	} else if (address &&
10242 		   address->proto == AVAHI_PROTO_INET6 &&
10243 		   interface != AVAHI_IF_UNSPEC &&
10244 		   IPBasedDeviceURIs != IP_BASED_URIS_IPV4_ONLY) {
10245 	  strncpy(addrstr, "[v1.", sizeof(addrstr) - 1);
10246 	  avahi_address_snprint(addrstr + 4, 256 - 6, address);
10247 	  addrlen = strlen(addrstr + 4);
10248 	  addr->sa_family = AF_INET6;
10249 	  if (inet_pton(AF_INET6, addrstr + 4,
10250 			&((struct sockaddr_in6 *) addr)->sin6_addr) &&
10251 	      allowed(addr)) {
10252 	    if (!strncasecmp(addrstr + 4, "fe", 2) &&
10253 		(addrstr[6] == '8' || addrstr[6] == '9' ||
10254 		 addrstr[6] == 'A' || addrstr[6] == 'B' ||
10255 		 addrstr[6] == 'a' || addrstr[6] == 'B'))
10256 	      /* Link-local address, needs specification of interface */
10257 	      snprintf(addrstr + addrlen + 4, 256 -
10258 		       addrlen - 4, "%%%s]",
10259 		       ifname);
10260 	    else {
10261 	      addrstr[addrlen + 4] = ']';
10262 	      addrstr[addrlen + 5] = '\0';
10263 	    }
10264 	    addrfound = 1;
10265 	  }
10266 	} else
10267 	  debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s': No IP address information available.\n",
10268 		       name, type, domain);
10269 	if (addrfound == 1) {
10270 	  /* Check remote printer type and create appropriate local queue to
10271 	     point to it */
10272 	  if (IPBasedDeviceURIs != IP_BASED_URIS_NO ||
10273 	      !host_name) {
10274 	    debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with IP address %s.\n",
10275 			 name, type, domain, addrstr);
10276 	    examine_discovered_printer_record((strcasecmp(ifname, "lo") ?
10277 					       host_name : "localhost"),
10278 					      addrstr, port, rp_value, name,
10279 					      "", instance, type, domain,
10280 					      ifname, addr->sa_family, txt);
10281 	  } else
10282 	    examine_discovered_printer_record((strcasecmp(ifname, "lo") ?
10283 					       host_name : "localhost"),
10284 					      NULL, port, rp_value,
10285 					      name, "", instance, type,
10286 					      domain, ifname, addr->sa_family,
10287 					      txt);
10288 	} else
10289 	  debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, could not determine IP address.\n",
10290 		       name, type, domain);
10291 	free(addrstr);
10292       } else {
10293 	/* Check remote printer type and create appropriate local queue to
10294 	   point to it */
10295 	if (host_name)
10296 	  examine_discovered_printer_record((strcasecmp(ifname, "lo") ?
10297 					     host_name : "localhost"),
10298 					    NULL, port, rp_value,
10299 					    name, "", instance, type, domain,
10300 					    ifname,
10301 					    (address->proto ==
10302 					     AVAHI_PROTO_INET ? AF_INET :
10303 					     (address->proto ==
10304 					      AVAHI_PROTO_INET6 ? AF_INET6 :
10305 					      0)),
10306 					    txt);
10307 	else
10308 	  debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, host name not supplied.\n",
10309 		       name, type, domain);
10310       }
10311     }
10312 
10313     clean_up:
10314 
10315     /* Clean up */
10316 
10317     if (rp_entry) {
10318       avahi_free(rp_key);
10319       avahi_free(rp_value);
10320     } else {
10321       free(rp_key);
10322       free(rp_value);
10323     }
10324     if (adminurl_entry) {
10325       avahi_free(adminurl_key);
10326       avahi_free(adminurl_value);
10327     } else {
10328       free(adminurl_key);
10329       free(adminurl_value);
10330     }
10331     break;
10332   }
10333   }
10334 
10335  ignore:
10336   avahi_service_resolver_free(r);
10337 
10338   if (in_shutdown == 0)
10339     recheck_timer ();
10340 }
10341 
browse_callback(AvahiServiceBrowser * b,AvahiIfIndex interface,AvahiProtocol protocol,AvahiBrowserEvent event,const char * name,const char * type,const char * domain,AvahiLookupResultFlags flags,void * userdata)10342 static void browse_callback(AvahiServiceBrowser *b,
10343 			    AvahiIfIndex interface,
10344 			    AvahiProtocol protocol,
10345 			    AvahiBrowserEvent event,
10346 			    const char *name,
10347 			    const char *type,
10348 			    const char *domain,
10349 			    AvahiLookupResultFlags flags,
10350 			    void* userdata) {
10351 
10352   AvahiClient *c = userdata;
10353   char ifname[IF_NAMESIZE];
10354 
10355   debug_printf("browse_callback() in THREAD %ld\n", pthread_self());
10356 
10357   if (b == NULL)
10358     return;
10359 
10360   /* Called whenever a new services becomes available on the LAN or
10361      is removed from the LAN */
10362 
10363   switch (event) {
10364 
10365   /* Avahi browser error */
10366   case AVAHI_BROWSER_FAILURE:
10367 
10368     debug_printf("Avahi Browser: ERROR: %s\n",
10369 		 avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
10370     g_main_loop_quit(gmainloop);
10371     g_main_context_wakeup(NULL);
10372     return;
10373 
10374   /* New service (remote printer) */
10375   case AVAHI_BROWSER_NEW:
10376 
10377     if (c == NULL || name == NULL || type == NULL || domain == NULL)
10378       return;
10379 
10380     /* Get the interface name */
10381     if (!if_indextoname(interface, ifname)) {
10382       debug_printf("Unable to find interface name for interface %d: %s\n",
10383 		   interface, strerror(errno));
10384       strncpy(ifname, "Unknown", sizeof(ifname) - 1);
10385     }
10386 
10387     debug_printf("Avahi Browser: NEW: service '%s' of type '%s' in domain '%s' on interface '%s' (%s)\n",
10388 		 name, type, domain, ifname,
10389 		 protocol != AVAHI_PROTO_UNSPEC ?
10390 		 avahi_proto_to_string(protocol) : "Unknown");
10391 
10392     /* Ignore if terminated (by SIGTERM) */
10393     if (terminating) {
10394       debug_printf("Avahi Browser: Ignoring because cups-browsed is terminating.\n");
10395       break;
10396     }
10397 
10398     /* We ignore the returned resolver object. In the callback
10399        function we free it. If the server is terminated before
10400        the callback function is called the server will free
10401        the resolver for us. */
10402 
10403     if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c)))
10404       debug_printf("Failed to resolve service '%s': %s\n",
10405 		   name, avahi_strerror(avahi_client_errno(c)));
10406     break;
10407 
10408   /* A service (remote printer) has disappeared */
10409   case AVAHI_BROWSER_REMOVE: {
10410     remote_printer_t *p;
10411 
10412     if (name == NULL || type == NULL || domain == NULL)
10413       return;
10414 
10415     /* Get the interface name */
10416     if (!if_indextoname(interface, ifname)) {
10417       debug_printf("Unable to find interface name for interface %d: %s\n",
10418 		   interface, strerror(errno));
10419       strncpy(ifname, "Unknown", sizeof(ifname) - 1);
10420     }
10421 
10422     debug_printf("Avahi Browser: REMOVE: service '%s' of type '%s' in domain '%s' on interface '%s' (%s)\n",
10423 		 name, type, domain, ifname,
10424 		 protocol != AVAHI_PROTO_UNSPEC ?
10425 		 avahi_proto_to_string(protocol) : "Unknown");
10426 
10427     /* Ignore if terminated (by SIGTERM) */
10428     if (terminating) {
10429       debug_printf("Avahi Browser: Ignoring because cups-browsed is terminating.\n");
10430       break;
10431     }
10432 
10433     /* Check whether we have listed this printer */
10434     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
10435 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
10436       if (p->status != STATUS_DISAPPEARED &&
10437 	  p->status != STATUS_TO_BE_RELEASED &&
10438 	  !strcasecmp(p->service_name, name) &&
10439 	  !strcasecmp(p->domain, domain))
10440 	break;
10441     if (p) {
10442       int family =
10443 	(protocol == AVAHI_PROTO_INET ? AF_INET :
10444 	 (protocol == AVAHI_PROTO_INET6 ? AF_INET6 : 0));
10445       if (p->ipp_discoveries) {
10446 	ipp_discovery_t *ippdis;
10447 	for (ippdis = cupsArrayFirst(p->ipp_discoveries); ippdis;
10448 	     ippdis = cupsArrayNext(p->ipp_discoveries))
10449 	  if (!strcasecmp(ippdis->interface, ifname) &&
10450 	      !strcasecmp(ippdis->type, type) &&
10451 	      ippdis->family == family) {
10452 	    debug_printf("Discovered instance for printer with Service name \"%s\", Domain \"%s\" unregistered: Interface \"%s\", Service type: \"%s\", Protocol: \"%s\"\n",
10453 			 p->service_name, p->domain,
10454 			 ippdis->interface, ippdis->type,
10455 			 (ippdis->family == AF_INET ? "IPv4" :
10456 			  (ippdis->family == AF_INET6 ? "IPv6" : "Unknown")));
10457 	    cupsArrayRemove(p->ipp_discoveries, (void *)ippdis);
10458 	    ipp_discoveries_list(p->ipp_discoveries);
10459 	    break;
10460 	  }
10461 	/* Remove the entry if no discovered instances are left */
10462 	if (cupsArrayCount(p->ipp_discoveries) == 0) {
10463 	  debug_printf("Removing printer with Service name \"%s\", Domain \"%s\", all discovered instances disappeared.\n",
10464 		       p->service_name, p->domain);
10465 	  remove_printer_entry(p);
10466 	}
10467       }
10468 
10469       if (in_shutdown == 0)
10470 	recheck_timer ();
10471     }
10472     break;
10473   }
10474 
10475   /* All cached Avahi events are treated now */
10476   case AVAHI_BROWSER_ALL_FOR_NOW:
10477   case AVAHI_BROWSER_CACHE_EXHAUSTED:
10478     debug_printf("Avahi Browser: %s\n",
10479 		 event == AVAHI_BROWSER_CACHE_EXHAUSTED ?
10480 		 "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
10481     break;
10482   }
10483 
10484 }
10485 
avahi_browser_shutdown()10486 void avahi_browser_shutdown() {
10487   remote_printer_t *p;
10488 
10489   avahi_present = 0;
10490 
10491   /* Remove all queues which we have set up based on DNS-SD discovery*/
10492   if (cupsArrayCount(remote_printers) > 0) {
10493     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
10494 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
10495       if (p->type && p->type[0]) {
10496 	if (KeepGeneratedQueuesOnShutdown) {
10497 	  if (p->status != STATUS_TO_BE_RELEASED &&
10498 	      p->status != STATUS_DISAPPEARED) {
10499 	    p->status = STATUS_UNCONFIRMED;
10500 	    p->timeout = time(NULL) + TIMEOUT_CONFIRM;
10501 	  }
10502 	} else {
10503 	  if (p->status != STATUS_TO_BE_RELEASED)
10504 	    p->status = STATUS_DISAPPEARED;
10505 	  p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
10506 	}
10507       }
10508     }
10509     if (in_shutdown == 0)
10510       recheck_timer();
10511     else
10512       update_cups_queues(NULL);
10513   }
10514 
10515   /* Free the data structures for DNS-SD browsing */
10516   if (sb1) {
10517     avahi_service_browser_free(sb1);
10518     sb1 = NULL;
10519   }
10520   if (sb2) {
10521     avahi_service_browser_free(sb2);
10522     sb2 = NULL;
10523   }
10524 
10525   /* Switch on auto shutdown mode */
10526   if (autoshutdown_avahi && in_shutdown == 0) {
10527     autoshutdown = 1;
10528     debug_printf("Avahi server disappeared, switching to auto shutdown mode ...\n");
10529     /* If there are no printers or no jobs schedule the shutdown in
10530        autoshutdown_timeout seconds */
10531     if (!autoshutdown_exec_id &&
10532 	(cupsArrayCount(remote_printers) == 0 ||
10533 	 (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
10534       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);
10535       autoshutdown_exec_id =
10536 	g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
10537 			       NULL);
10538     }
10539   }
10540 }
10541 
avahi_shutdown()10542 void avahi_shutdown() {
10543   avahi_browser_shutdown();
10544   if (client) {
10545     avahi_client_free(client);
10546     client = NULL;
10547   }
10548   if (glib_poll) {
10549     avahi_glib_poll_free(glib_poll);
10550     glib_poll = NULL;
10551   }
10552 }
10553 
client_callback(AvahiClient * c,AvahiClientState state,AVAHI_GCC_UNUSED void * userdata)10554 static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
10555   int error;
10556 
10557   if (c == NULL)
10558     return;
10559 
10560   /* Called whenever the client or server state changes */
10561   switch (state) {
10562 
10563   /* avahi-daemon available */
10564   case AVAHI_CLIENT_S_REGISTERING:
10565   case AVAHI_CLIENT_S_RUNNING:
10566   case AVAHI_CLIENT_S_COLLISION:
10567 
10568     debug_printf("Avahi server connection got available, setting up service browsers.\n");
10569 
10570     /* Create the service browsers */
10571     if (!sb1)
10572       if (!(sb1 =
10573 	    avahi_service_browser_new(c, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
10574 				      "_ipp._tcp", NULL, 0, browse_callback,
10575 				      c))) {
10576 	debug_printf("ERROR: Failed to create service browser for IPP: %s\n",
10577 		     avahi_strerror(avahi_client_errno(c)));
10578       }
10579     if (!sb2)
10580       if (!(sb2 =
10581 	    avahi_service_browser_new(c, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
10582 				      "_ipps._tcp", NULL, 0, browse_callback,
10583 				      c))) {
10584 	debug_printf("ERROR: Failed to create service browser for IPPS: %s\n",
10585 		     avahi_strerror(avahi_client_errno(c)));
10586       }
10587 
10588     avahi_present = 1;
10589 
10590     /* switch off auto shutdown mode */
10591     if (autoshutdown_avahi) {
10592       autoshutdown = 0;
10593       debug_printf("Avahi server available, switching to permanent mode ...\n");
10594       /* If there is still an active auto shutdown timer, kill it */
10595       if (autoshutdown_exec_id) {
10596 	debug_printf ("We have left auto shutdown mode, killing auto shutdown timer.\n");
10597 	g_source_remove(autoshutdown_exec_id);
10598 	autoshutdown_exec_id = 0;
10599       }
10600     }
10601 
10602     break;
10603 
10604   /* Avahi client error */
10605   case AVAHI_CLIENT_FAILURE:
10606 
10607     if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
10608       debug_printf("Avahi server disappeared, shutting down service browsers, removing DNS-SD-discovered print queues.\n");
10609       avahi_browser_shutdown();
10610       /* Renewing client */
10611       avahi_client_free(client);
10612       client = avahi_client_new(avahi_glib_poll_get(glib_poll),
10613 				AVAHI_CLIENT_NO_FAIL,
10614 				client_callback, NULL, &error);
10615       if (!client) {
10616 	debug_printf("ERROR: Failed to create client: %s\n",
10617 		     avahi_strerror(error));
10618 	BrowseRemoteProtocols &= ~BROWSE_DNSSD;
10619 	avahi_shutdown();
10620       }
10621     } else {
10622       debug_printf("ERROR: Avahi server connection failure: %s\n",
10623 		   avahi_strerror(avahi_client_errno(c)));
10624       g_main_loop_quit(gmainloop);
10625       g_main_context_wakeup(NULL);
10626     }
10627     break;
10628 
10629   default:
10630     break;
10631   }
10632 }
10633 
avahi_init()10634 void avahi_init() {
10635   int error;
10636 
10637   if (BrowseRemoteProtocols & BROWSE_DNSSD) {
10638     /* Allocate main loop object */
10639     if (!glib_poll)
10640       if (!(glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT))) {
10641 	debug_printf("ERROR: Failed to create glib poll object.\n");
10642 	goto avahi_init_fail;
10643       }
10644 
10645     /* Allocate a new client */
10646     if (!client)
10647       client = avahi_client_new(avahi_glib_poll_get(glib_poll),
10648 				AVAHI_CLIENT_NO_FAIL,
10649 				client_callback, NULL, &error);
10650 
10651     /* Check wether creating the client object succeeded */
10652     if (!client) {
10653       debug_printf("ERROR: Failed to create client: %s\n",
10654 		   avahi_strerror(error));
10655       goto avahi_init_fail;
10656     }
10657 
10658     return;
10659 
10660   avahi_init_fail:
10661     BrowseRemoteProtocols &= ~BROWSE_DNSSD;
10662     avahi_shutdown();
10663   }
10664 }
10665 #endif /* HAVE_AVAHI */
10666 
10667 /*
10668  * A CUPS printer has been discovered via CUPS Browsing
10669  * or with BrowsePoll
10670  */
10671 void
found_cups_printer(const char * remote_host,const char * uri,const char * location,const char * info)10672 found_cups_printer (const char *remote_host, const char *uri,
10673 		    const char *location, const char *info)
10674 {
10675   char scheme[32];
10676   char username[64];
10677   char host[HTTP_MAX_HOST];
10678   char resource[HTTP_MAX_URI];
10679   int port;
10680   netif_t *iface;
10681   char local_resource[HTTP_MAX_URI];
10682   char service_name[HTTP_MAX_URI];
10683   char *c;
10684   int hl;
10685   remote_printer_t *printer;
10686 
10687   memset(scheme, 0, sizeof(scheme));
10688   memset(username, 0, sizeof(username));
10689   memset(host, 0, sizeof(host));
10690   memset(resource, 0, sizeof(resource));
10691   memset(local_resource, 0, sizeof(local_resource));
10692 
10693   httpSeparateURI (HTTP_URI_CODING_ALL, uri,
10694 		   scheme, sizeof(scheme) - 1,
10695 		   username, sizeof(username) - 1,
10696 		   host, sizeof(host) - 1,
10697 		   &port,
10698 		   resource, sizeof(resource)- 1);
10699 
10700   /* Check this isn't one of our own broadcasts */
10701   for (iface = cupsArrayFirst (netifs);
10702        iface;
10703        iface = cupsArrayNext (netifs))
10704     if (!strcasecmp (host, iface->address))
10705       break;
10706   if (iface) {
10707     debug_printf("ignoring own broadcast on %s\n",
10708 		 iface->address);
10709     return;
10710   }
10711 
10712   if (strncasecmp (resource, "/printers/", 10) &&
10713       strncasecmp (resource, "/classes/", 9)) {
10714     debug_printf("Don't understand URI: %s\n", uri);
10715     return;
10716   }
10717 
10718   strncpy (local_resource, resource + 1, sizeof (local_resource) - 1);
10719   local_resource[sizeof (local_resource) - 1] = '\0';
10720   c = strchr (local_resource, '?');
10721   if (c)
10722     *c = '\0';
10723 
10724   /* Build the DNS-SD service name which CUPS would give to this printer
10725      when DNS-SD-broadcasting it */
10726   snprintf(service_name, sizeof (service_name), "%s @ %s",
10727 	   (info ? info : strchr(local_resource, '/') + 1), host);
10728   /* Cut off trailing ".local" of host name */
10729   hl = strlen(service_name);
10730   if (hl > 6 && !strcasecmp(service_name + hl - 6, ".local"))
10731     service_name[hl - 6] = '\0';
10732   if (hl > 7 && !strcasecmp(service_name + hl - 7, ".local."))
10733     service_name[hl - 7] = '\0';
10734   /* DNS-SD service name has max. 63 characters */
10735   service_name[63] = '\0';
10736 
10737   debug_printf("CUPS browsing: Remote host: %s; Port: %d; Remote queue name: %s; Service Name: %s\n",
10738 	       host, port, strchr(local_resource, '/') + 1, service_name);
10739 
10740   printer = examine_discovered_printer_record(host, NULL, port, local_resource,
10741 					      service_name,
10742 					      location ? location : "",
10743 					      info ? info : "", "", "", "", 0,
10744 					      NULL);
10745 
10746   if (printer &&
10747       (printer->domain == NULL || printer->domain[0] == '\0' ||
10748        printer->type == NULL || printer->type[0] == '\0')) {
10749     printer->is_legacy = 1;
10750 
10751     if (printer->status != STATUS_TO_BE_CREATED) {
10752       printer->timeout = time(NULL) + BrowseTimeout;
10753       debug_printf("starting BrowseTimeout timer for %s (%ds)\n",
10754 		   printer->queue_name, BrowseTimeout);
10755     }
10756   }
10757 
10758   if (printer && NewBrowsePollQueuesShared &&
10759       (HAVE_CUPS_1_6 || (!HAVE_CUPS_1_6 && !printer->is_legacy)))
10760     printer->num_options = cupsAddOption("printer-to-be-shared", "true", printer->num_options, &(printer->options));
10761 
10762 }
10763 
10764 gboolean
process_browse_data(GIOChannel * source,GIOCondition condition,gpointer data)10765 process_browse_data (GIOChannel *source,
10766 		     GIOCondition condition,
10767 		     gpointer data)
10768 {
10769   char packet[2048];
10770   http_addr_t srcaddr;
10771   socklen_t srclen;
10772   ssize_t got;
10773   unsigned int type;
10774   unsigned int state;
10775   char remote_host[256];
10776   char uri[1024];
10777   char location[1024];
10778   char info[1024];
10779   char *c = NULL, *end = NULL;
10780 
10781   debug_printf("process_browse_data() in THREAD %ld\n", pthread_self());
10782 
10783   memset(packet, 0, sizeof(packet));
10784   memset(remote_host, 0, sizeof(remote_host));
10785   memset(uri, 0, sizeof(uri));
10786   memset(info, 0, sizeof(info));
10787 
10788   srclen = sizeof (srcaddr);
10789   got = recvfrom (browsesocket, packet, sizeof (packet) - 1, 0,
10790 		  &srcaddr.addr, &srclen);
10791   if (got == -1) {
10792     debug_printf ("cupsd-browsed: error receiving browse packet: %s\n",
10793 		  strerror (errno));
10794     /* Remove this I/O source */
10795     return FALSE;
10796   }
10797 
10798   packet[got] = '\0';
10799   httpAddrString (&srcaddr, remote_host, sizeof (remote_host) - 1);
10800 
10801   /* Check this packet is allowed */
10802   if (!allowed ((struct sockaddr *) &srcaddr)) {
10803     debug_printf("browse packet from %s disallowed\n",
10804 		 remote_host);
10805     return TRUE;
10806   }
10807 
10808   debug_printf("browse packet received from %s\n",
10809 	       remote_host);
10810 
10811   if (sscanf (packet, "%x%x%1023s", &type, &state, uri) < 3) {
10812     debug_printf("incorrect browse packet format\n");
10813     return TRUE;
10814   }
10815 
10816   info[0] = '\0';
10817 
10818   /* do not read OOB */
10819   end = packet + sizeof(packet);
10820   c = strchr (packet, '\"');
10821   if (c >= end)
10822     return TRUE;
10823 
10824   if (c) {
10825     /* Extract location field */
10826     {
10827       int i;
10828       c++;
10829       for (i = 0;
10830 	   i < sizeof (location) - 1 && *c != '\"' && c < end;
10831 	   i++, c++)
10832 	location[i] = *c;
10833       location[i] = '\0';
10834       debug_printf("process_browse_data: location: |%s|\n", location); /* !! */
10835     }
10836     for (; c < end && *c != '\"'; c++)
10837       ;
10838 
10839     if (c >= end)
10840       return TRUE;
10841 
10842     if (*c == '\"') {
10843       for (c++; c < end && isspace(*c); c++)
10844 	;
10845     }
10846 
10847     if (c >= end)
10848       return TRUE;
10849 
10850     /* Is there an info field? */
10851     if (*c == '\"') {
10852       int i;
10853       c++;
10854       for (i = 0;
10855 	   i < sizeof (info) - 1 && *c != '\"' && c < end;
10856 	   i++, c++)
10857 	info[i] = *c;
10858       info[i] = '\0';
10859       debug_printf("process_browse_data: info: |%s|\n", info); /* !! */
10860     }
10861   }
10862   if (c >= end)
10863     return TRUE;
10864 
10865   if (!(type & CUPS_PRINTER_DELETE))
10866     found_cups_printer (remote_host, uri, location, info);
10867 
10868   if (in_shutdown == 0)
10869     recheck_timer ();
10870 
10871   /* Don't remove this I/O source */
10872   return TRUE;
10873 }
10874 
10875 static void
broadcast_browse_packets(gpointer data,gpointer user_data)10876 broadcast_browse_packets (gpointer data, gpointer user_data)
10877 {
10878   browse_data_t *bdata = data;
10879   netif_t *browse;
10880   char packet[2048];
10881   char uri[HTTP_MAX_URI];
10882   char scheme[32];
10883   char username[64];
10884   char host[HTTP_MAX_HOST];
10885   int port;
10886   char resource[HTTP_MAX_URI];
10887 
10888   debug_printf("broadcast_browse_packets() in THREAD %ld\n", pthread_self());
10889 
10890   for (browse = (netif_t *)cupsArrayFirst (netifs);
10891        browse != NULL;
10892        browse = (netif_t *)cupsArrayNext (netifs)) {
10893     /* Replace 'localhost' with our IP address on this interface */
10894     httpSeparateURI(HTTP_URI_CODING_ALL, bdata->uri,
10895 		    scheme, sizeof(scheme),
10896 		    username, sizeof(username),
10897 		    host, sizeof(host),
10898 		    &port,
10899 		    resource, sizeof(resource));
10900     httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof (uri),
10901 		    scheme, username, browse->address, port, resource);
10902 
10903     if (snprintf (packet, sizeof (packet),
10904 		  "%x "     /* type */
10905 		  "%x "     /* state */
10906 		  "%s "     /* uri */
10907 		  "\"%s\" " /* location */
10908 		  "\"%s\" " /* info */
10909 		  "\"%s\" " /* make-and-model */
10910 		  "lease-duration=%d" /* BrowseTimeout */
10911 		  "%s%s" /* other browse options */
10912 		  "\n",
10913 		  bdata->type,
10914 		  bdata->state,
10915 		  uri,
10916 		  bdata->location,
10917 		  bdata->info,
10918 		  bdata->make_model,
10919 		  BrowseTimeout,
10920 		  bdata->browse_options ? " " : "",
10921 		  bdata->browse_options ? bdata->browse_options : "")
10922 	>= sizeof (packet)) {
10923       debug_printf ("oversize packet not sent\n");
10924       continue;
10925     }
10926 
10927     debug_printf("packet to send:\n%s", packet);
10928 
10929     int err = sendto (browsesocket, packet,
10930 		      strlen (packet), 0,
10931 		      &browse->broadcast.addr,
10932 		      httpAddrLength (&browse->broadcast));
10933     if (err == -1)
10934       debug_printf("cupsd-browsed: sendto returned %d: %s\n",
10935 		   err, strerror (errno));
10936   }
10937 }
10938 
10939 gboolean
send_browse_data(gpointer data)10940 send_browse_data (gpointer data)
10941 {
10942   debug_printf("send_browse_data() in THREAD %ld\n", pthread_self());
10943   update_netifs (NULL);
10944   res_init ();
10945   update_local_printers ();
10946   g_list_foreach (browse_data, broadcast_browse_packets, NULL);
10947   g_timeout_add_seconds (BrowseInterval, send_browse_data, NULL);
10948 
10949   /* Stop this timeout handler, we called a new one */
10950   return FALSE;
10951 }
10952 
10953 static browsepoll_printer_t *
new_browsepoll_printer(const char * uri_supported,const char * location,const char * info)10954 new_browsepoll_printer (const char *uri_supported,
10955 			const char *location,
10956 			const char *info)
10957 {
10958   browsepoll_printer_t *printer = g_malloc (sizeof (browsepoll_printer_t));
10959   printer->uri_supported = g_strdup (uri_supported);
10960   printer->location = g_strdup (location);
10961   printer->info = g_strdup (info);
10962   return printer;
10963 }
10964 
10965 static void
browsepoll_printer_free(gpointer data)10966 browsepoll_printer_free (gpointer data)
10967 {
10968   browsepoll_printer_t *printer = data;
10969   debug_printf("browsepoll_printer_free() in THREAD %ld\n", pthread_self());
10970   free (printer->uri_supported);
10971   free (printer->location);
10972   free (printer->info);
10973   free (printer);
10974 }
10975 
10976 static void
browse_poll_get_printers(browsepoll_t * context,http_t * conn)10977 browse_poll_get_printers (browsepoll_t *context, http_t *conn)
10978 {
10979   static const char * const rattrs[] = { "printer-uri-supported",
10980 					 "printer-location",
10981 					 "printer-info"};
10982   ipp_t *request, *response = NULL;
10983   ipp_attribute_t *attr;
10984   GList *printers = NULL;
10985 
10986   debug_printf ("cups-browsed [BrowsePoll %s:%d]: CUPS-Get-Printers\n",
10987 		context->server, context->port);
10988 
10989   request = ippNewRequest(CUPS_GET_PRINTERS);
10990   if (context->major > 0) {
10991     debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
10992 		 context->server, context->port, context->major,
10993 		 context->minor);
10994     ippSetVersion (request, context->major, context->minor);
10995   }
10996 
10997   ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
10998 		 "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]),
10999 		 NULL,
11000 		 rattrs);
11001 
11002   /* Ask the server to exclude printers that are remote or not shared,
11003      or implicit classes. */
11004   ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
11005 		 "printer-type-mask",
11006 		 CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
11007 		 CUPS_PRINTER_NOT_SHARED);
11008   ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
11009 		 "printer-type", 0);
11010 
11011   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
11012 		"requesting-user-name", NULL, cupsUser ());
11013 
11014   response = cupsDoRequest(conn, request, "/");
11015   if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
11016     debug_printf("cups-browsed [BrowsePoll %s:%d]: failed: %s\n",
11017 		 context->server, context->port, cupsLastErrorString ());
11018     goto fail;
11019   }
11020 
11021   for (attr = ippFirstAttribute(response); attr;
11022        attr = ippNextAttribute(response)) {
11023     browsepoll_printer_t *printer;
11024     const char *uri, *location, *info;
11025 
11026     while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
11027       attr = ippNextAttribute(response);
11028 
11029     if (!attr)
11030       break;
11031 
11032     uri = NULL;
11033     info = NULL;
11034     location = NULL;
11035     while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
11036       if (!strcasecmp (ippGetName(attr), "printer-uri-supported") &&
11037 	  ippGetValueTag(attr) == IPP_TAG_URI)
11038 	uri = ippGetString(attr, 0, NULL);
11039       else if (!strcasecmp (ippGetName(attr), "printer-location") &&
11040 	       ippGetValueTag(attr) == IPP_TAG_TEXT)
11041 	location = ippGetString(attr, 0, NULL);
11042       else if (!strcasecmp (ippGetName(attr), "printer-info") &&
11043 	       ippGetValueTag(attr) == IPP_TAG_TEXT)
11044 	info = ippGetString(attr, 0, NULL);
11045       attr = ippNextAttribute(response);
11046     }
11047 
11048     if (uri) {
11049       found_cups_printer (context->server, uri, location, info);
11050       printer = new_browsepoll_printer (uri, location, info);
11051       printers = g_list_insert (printers, printer, 0);
11052     }
11053 
11054     if (!attr)
11055       break;
11056   }
11057 
11058   g_list_free_full (context->printers, browsepoll_printer_free);
11059   context->printers = printers;
11060 
11061 fail:
11062   if (response)
11063     ippDelete(response);
11064 }
11065 
11066 static void
browse_poll_create_subscription(browsepoll_t * context,http_t * conn)11067 browse_poll_create_subscription (browsepoll_t *context, http_t *conn)
11068 {
11069   static const char * const events[] = { "printer-added",
11070 					 "printer-changed",
11071 					 "printer-config-changed",
11072 					 "printer-modified",
11073 					 "printer-deleted",
11074 					 "printer-state-changed" };
11075   ipp_t *request, *response = NULL;
11076   ipp_attribute_t *attr;
11077 
11078   debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Create-Subscription\n",
11079 		context->server, context->port);
11080 
11081   request = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION);
11082   if (context->major > 0) {
11083     debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
11084 		 context->server, context->port, context->major,
11085 		 context->minor);
11086     ippSetVersion (request, context->major, context->minor);
11087   }
11088 
11089   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
11090 		"printer-uri", NULL, "/");
11091   ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
11092 		"notify-pull-method", NULL, "ippget");
11093   ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_CHARSET,
11094 		"notify-charset", NULL, "utf-8");
11095   ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
11096 		"requesting-user-name", NULL, cupsUser ());
11097   ippAddStrings (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
11098 		 "notify-events", sizeof (events) / sizeof (events[0]),
11099 		 NULL, events);
11100   ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
11101 		 "notify-time-interval", BrowseInterval);
11102 
11103   response = cupsDoRequest (conn, request, "/");
11104   if (!response ||
11105       ippGetStatusCode (response) > IPP_STATUS_OK_EVENTS_COMPLETE) {
11106     debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n",
11107 		 context->server, context->port, cupsLastErrorString ());
11108     context->subscription_id = -1;
11109     context->can_subscribe = FALSE;
11110     goto fail;
11111   }
11112 
11113   for (attr = ippFirstAttribute(response); attr;
11114        attr = ippNextAttribute(response)) {
11115     if (ippGetGroupTag (attr) == IPP_TAG_SUBSCRIPTION) {
11116       if (ippGetValueTag (attr) == IPP_TAG_INTEGER &&
11117 	  !strcasecmp (ippGetName (attr), "notify-subscription-id")) {
11118 	context->subscription_id = ippGetInteger (attr, 0);
11119 	debug_printf("cups-browsed [BrowsePoll %s:%d]: subscription ID=%d\n",
11120 		     context->server, context->port, context->subscription_id);
11121 	break;
11122       }
11123     }
11124   }
11125 
11126   if (!attr) {
11127     debug_printf("cups-browsed [BrowsePoll %s:%d]: no ID returned\n",
11128 		 context->server, context->port);
11129     context->subscription_id = -1;
11130     context->can_subscribe = FALSE;
11131   }
11132 
11133 fail:
11134   if (response)
11135     ippDelete(response);
11136 }
11137 
11138 static void
browse_poll_cancel_subscription(browsepoll_t * context)11139 browse_poll_cancel_subscription (browsepoll_t *context)
11140 {
11141   ipp_t *request, *response = NULL;
11142   http_t *conn = httpConnectEncryptShortTimeout (context->server, context->port,
11143 						 HTTP_ENCRYPT_IF_REQUESTED);
11144 
11145   if (conn == NULL) {
11146     debug_printf("cups-browsed [BrowsePoll %s:%d]: connection failure "
11147 		 "attempting to cancel\n", context->server, context->port);
11148     return;
11149   }
11150 
11151   httpSetTimeout(conn, HttpRemoteTimeout, http_timeout_cb, NULL);
11152 
11153   debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Cancel-Subscription\n",
11154 		context->server, context->port);
11155 
11156   request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
11157   if (context->major > 0) {
11158     debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
11159 		 context->server, context->port, context->major,
11160 		 context->minor);
11161     ippSetVersion (request, context->major, context->minor);
11162   }
11163 
11164   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
11165 		"printer-uri", NULL, "/");
11166   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
11167 		"requesting-user-name", NULL, cupsUser ());
11168   ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
11169 		 "notify-subscription-id", context->subscription_id);
11170 
11171   response = cupsDoRequest (conn, request, "/");
11172   if (!response ||
11173       ippGetStatusCode (response) > IPP_STATUS_OK_EVENTS_COMPLETE)
11174     debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n",
11175 		 context->server, context->port, cupsLastErrorString ());
11176 
11177   if (response)
11178     ippDelete(response);
11179   if (conn)
11180     httpClose (conn);
11181 }
11182 
11183 static gboolean
browse_poll_get_notifications(browsepoll_t * context,http_t * conn)11184 browse_poll_get_notifications (browsepoll_t *context, http_t *conn)
11185 {
11186   ipp_t *request, *response = NULL;
11187   ipp_status_t status;
11188   gboolean get_printers = FALSE;
11189 
11190   debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Get-Notifications\n",
11191 		context->server, context->port);
11192 
11193   request = ippNewRequest(IPP_GET_NOTIFICATIONS);
11194   if (context->major > 0) {
11195     debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
11196 		 context->server, context->port, context->major,
11197 		 context->minor);
11198     ippSetVersion (request, context->major, context->minor);
11199   }
11200 
11201   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
11202 		"printer-uri", NULL, "/");
11203   ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
11204 		"requesting-user-name", NULL, cupsUser ());
11205   ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
11206 		 "notify-subscription-ids", context->subscription_id);
11207   ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
11208 		 "notify-sequence-numbers", context->sequence_number + 1);
11209 
11210   response = cupsDoRequest (conn, request, "/");
11211   if (!response)
11212     status = cupsLastError ();
11213   else
11214     status = ippGetStatusCode (response);
11215 
11216   if (status == IPP_STATUS_ERROR_NOT_FOUND) {
11217     /* Subscription lease has expired. */
11218     debug_printf ("cups-browsed [BrowsePoll %s:%d]: Lease expired\n",
11219 		  context->server, context->port);
11220     browse_poll_create_subscription (context, conn);
11221     get_printers = TRUE;
11222   } else if (status > IPP_STATUS_OK_EVENTS_COMPLETE) {
11223     debug_printf("cups-browsed [BrowsePoll %s:%d]: failed: %s\n",
11224 		 context->server, context->port, cupsLastErrorString ());
11225     context->can_subscribe = FALSE;
11226     browse_poll_cancel_subscription (context);
11227     context->subscription_id = -1;
11228     context->sequence_number = 0;
11229     get_printers = TRUE;
11230   }
11231 
11232   if (!get_printers) {
11233     ipp_attribute_t *attr;
11234     gboolean seen_event = FALSE;
11235     int last_seq = context->sequence_number;
11236     if (response == NULL)
11237       return FALSE;
11238     for (attr = ippFirstAttribute(response); attr;
11239 	 attr = ippNextAttribute(response))
11240       if (ippGetGroupTag (attr) == IPP_TAG_EVENT_NOTIFICATION) {
11241 	/* There is a printer-* event here. */
11242 	seen_event = TRUE;
11243 
11244 	if (!strcmp (ippGetName (attr), "notify-sequence-number") &&
11245 	    ippGetValueTag (attr) == IPP_TAG_INTEGER)
11246 	  last_seq = ippGetInteger (attr, 0);
11247       }
11248 
11249     if (seen_event) {
11250       debug_printf("cups-browsed [BrowsePoll %s:%d]: printer-* event\n",
11251 		   context->server, context->port);
11252       context->sequence_number = last_seq;
11253       get_printers = TRUE;
11254     } else
11255       debug_printf("cups-browsed [BrowsePoll %s:%d]: no events\n",
11256 		   context->server, context->port);
11257   }
11258 
11259   if (response)
11260     ippDelete (response);
11261 
11262   return get_printers;
11263 }
11264 
11265 static void
browsepoll_printer_keepalive(gpointer data,gpointer user_data)11266 browsepoll_printer_keepalive (gpointer data, gpointer user_data)
11267 {
11268   browsepoll_printer_t *printer = data;
11269   const char *server = user_data;
11270   debug_printf("browsepoll_printer_keepalive() in THREAD %ld\n",
11271 	       pthread_self());
11272   found_cups_printer (server, printer->uri_supported, printer->location,
11273 		      printer->info);
11274 }
11275 
11276 gboolean
browse_poll(gpointer data)11277 browse_poll (gpointer data)
11278 {
11279   browsepoll_t *context = data;
11280   http_t *conn = NULL;
11281   gboolean get_printers = FALSE;
11282 
11283   debug_printf("browse_poll() in THREAD %ld\n", pthread_self());
11284 
11285   debug_printf ("browse polling %s:%d\n",
11286 		context->server, context->port);
11287 
11288   res_init ();
11289 
11290   conn = httpConnectEncryptShortTimeout (context->server, context->port,
11291 					 HTTP_ENCRYPT_IF_REQUESTED);
11292   if (conn == NULL) {
11293     debug_printf("cups-browsed [BrowsePoll %s:%d]: failed to connect\n",
11294 		 context->server, context->port);
11295     goto fail;
11296   }
11297 
11298   httpSetTimeout(conn, HttpRemoteTimeout, http_timeout_cb, NULL);
11299 
11300   if (context->can_subscribe) {
11301     if (context->subscription_id == -1) {
11302       /* The first time this callback is run we need to create the IPP
11303        * subscription to watch to printer-* events. */
11304       browse_poll_create_subscription (context, conn);
11305       get_printers = TRUE;
11306     } else
11307       /* On subsequent runs, check for notifications using our
11308        * subscription. */
11309       get_printers = browse_poll_get_notifications (context, conn);
11310   }
11311   else
11312     get_printers = TRUE;
11313 
11314   update_local_printers ();
11315   inhibit_local_printers_update = TRUE;
11316   if (get_printers)
11317     browse_poll_get_printers (context, conn);
11318   else
11319     g_list_foreach (context->printers, browsepoll_printer_keepalive,
11320 		    context->server);
11321 
11322   inhibit_local_printers_update = FALSE;
11323 
11324   if (in_shutdown == 0)
11325     recheck_timer ();
11326 
11327  fail:
11328 
11329   if (conn)
11330     httpClose (conn);
11331 
11332   /* Call a new timeout handler so that we run again */
11333   g_timeout_add_seconds (BrowseInterval, browse_poll, data);
11334 
11335   /* Stop this timeout handler, we called a new one */
11336   return FALSE;
11337 }
11338 
11339 #ifdef HAVE_LDAP
11340 gboolean
browse_ldap_poll(gpointer data)11341 browse_ldap_poll (gpointer data)
11342 {
11343   char                  *tmpFilter;     /* Query filter */
11344   int                   filterLen;
11345 
11346   debug_printf("browse_ldap_poll() in THREAD %ld\n", pthread_self());
11347 
11348   /* do real stuff here */
11349   if (!BrowseLDAPDN) {
11350     debug_printf("Need to set BrowseLDAPDN to use LDAP browsing!\n");
11351     BrowseLocalProtocols &= ~BROWSE_LDAP;
11352     BrowseRemoteProtocols &= ~BROWSE_LDAP;
11353 
11354     return FALSE;
11355   } else {
11356     if (!BrowseLDAPInitialised) {
11357       BrowseLDAPInitialised = TRUE;
11358       /*
11359        * Query filter string
11360        */
11361       if (BrowseLDAPFilter)
11362 	filterLen = snprintf(NULL, 0, "(&%s%s)", LDAP_BROWSE_FILTER,
11363 			     BrowseLDAPFilter);
11364       else
11365 	filterLen = strlen(LDAP_BROWSE_FILTER);
11366 
11367       tmpFilter = (char *)malloc(filterLen + 1);
11368       if (!tmpFilter) {
11369 	debug_printf("Could not allocate memory for LDAP browse query filter!\n");
11370 	BrowseLocalProtocols &= ~BROWSE_LDAP;
11371 	BrowseRemoteProtocols &= ~BROWSE_LDAP;
11372 	return FALSE;
11373       }
11374 
11375       if (BrowseLDAPFilter) {
11376 	snprintf(tmpFilter, filterLen + 1, "(&%s%s)", LDAP_BROWSE_FILTER,
11377 		 BrowseLDAPFilter);
11378 	free(BrowseLDAPFilter);
11379 	BrowseLDAPFilter = NULL;
11380       } else
11381 	strcpy(tmpFilter, LDAP_BROWSE_FILTER);
11382 
11383       BrowseLDAPFilter = tmpFilter;
11384 
11385       /*
11386        * Open LDAP handle...
11387        */
11388 
11389       BrowseLDAPHandle = ldap_new_connection();
11390     }
11391 
11392     cupsdUpdateLDAPBrowse();
11393     if (in_shutdown == 0)
11394       recheck_timer();
11395   }
11396 
11397   /* Call a new timeout handler so that we run again */
11398   g_timeout_add_seconds (BrowseInterval, browse_ldap_poll, data);
11399 
11400   /* Stop this timeout handler, we called a new one */
11401   return FALSE;
11402 }
11403 #endif /* HAVE_LDAP */
11404 
11405 static void
sigterm_handler(int sig)11406 sigterm_handler(int sig) {
11407   (void)sig;    /* remove compiler warnings... */
11408 
11409   if (terminating) {
11410     debug_printf("Caught signal %d while already terminating.\n", sig);
11411     return;
11412   }
11413   terminating = 1; /* ignore any further callbacks and break loops */
11414   /* Flag that we should stop and return... */
11415   g_main_loop_quit(gmainloop);
11416   g_main_context_wakeup(NULL);
11417   debug_printf("Caught signal %d, shutting down ...\n", sig);
11418 }
11419 
11420 static void
sigusr1_handler(int sig)11421 sigusr1_handler(int sig) {
11422   (void)sig;    /* remove compiler warnings... */
11423 
11424   /* Turn off auto shutdown mode... */
11425   autoshutdown = 0;
11426   debug_printf("Caught signal %d, switching to permanent mode ...\n", sig);
11427   /* If there is still an active auto shutdown timer, kill it */
11428   if (autoshutdown_exec_id) {
11429     debug_printf ("We have left auto shutdown mode, killing auto shutdown timer.\n");
11430     g_source_remove(autoshutdown_exec_id);
11431     autoshutdown_exec_id = 0;
11432   }
11433 }
11434 
11435 static void
sigusr2_handler(int sig)11436 sigusr2_handler(int sig) {
11437   (void)sig;    /* remove compiler warnings... */
11438 
11439   /* Turn on auto shutdown mode... */
11440   autoshutdown = 1;
11441   debug_printf("Caught signal %d, switching to auto shutdown mode ...\n", sig);
11442   /* If there are no printers or no jobs schedule the shutdown in
11443      autoshutdown_timeout seconds */
11444   if (!autoshutdown_exec_id &&
11445       (cupsArrayCount(remote_printers) == 0 ||
11446        (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
11447     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);
11448     autoshutdown_exec_id =
11449       g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
11450              NULL);
11451   }
11452 }
11453 
11454 static int
read_browseallow_value(const char * value,allow_sense_t sense)11455 read_browseallow_value (const char *value, allow_sense_t sense)
11456 {
11457   char *p;
11458   struct in_addr addr;
11459   allow_t *allow;
11460 
11461   if (value && !strcasecmp (value, "all")) {
11462     if (sense == ALLOW_ALLOW) {
11463       browseallow_all = TRUE;
11464       return 0;
11465     } else if (sense == ALLOW_DENY) {
11466       browsedeny_all = TRUE;
11467       return 0;
11468     } else
11469       return 1;
11470   }
11471 
11472   allow = calloc (1, sizeof (allow_t));
11473   allow->sense = sense;
11474   if (value == NULL)
11475     goto fail;
11476   p = strchr (value, '/');
11477   if (p) {
11478     char *s = strdup (value);
11479     s[p - value] = '\0';
11480 
11481     if (!inet_aton (s, &addr)) {
11482       free (s);
11483       goto fail;
11484     }
11485 
11486     free (s);
11487     allow->type = ALLOW_NET;
11488     allow->addr.ipv4.sin_addr.s_addr = addr.s_addr;
11489 
11490     p++;
11491     if (strchr (p, '.')) {
11492       if (inet_aton (p, &addr))
11493 	allow->mask.ipv4.sin_addr.s_addr = addr.s_addr;
11494       else
11495 	goto fail;
11496     } else {
11497       char *endptr;
11498       unsigned long bits = strtoul (p, &endptr, 10);
11499       if (p == endptr)
11500 	goto fail;
11501 
11502       if (bits > 32)
11503 	goto fail;
11504 
11505       allow->mask.ipv4.sin_addr.s_addr = htonl (((0xffffffff << (32 - bits)) &
11506 						 0xffffffff));
11507     }
11508   } else if (inet_aton (value, &addr)) {
11509     allow->type = ALLOW_IP;
11510     allow->addr.ipv4.sin_addr.s_addr = addr.s_addr;
11511   } else
11512     goto fail;
11513 
11514   cupsArrayAdd (browseallow, allow);
11515   return 0;
11516 
11517 fail:
11518   allow->type = ALLOW_INVALID;
11519   cupsArrayAdd (browseallow, allow);
11520   return 1;
11521 }
11522 
11523 void
read_configuration(const char * filename)11524 read_configuration (const char *filename)
11525 {
11526   cups_file_t *fp;
11527   int i, linenum = 0;
11528   char line[HTTP_MAX_BUFFER];
11529   char *value = NULL, *ptr, *ptr2, *start;
11530   const char *delim = " \t,";
11531   int browse_allow_line_found = 0;
11532   int browse_deny_line_found = 0;
11533   int browse_order_line_found = 0;
11534   int browse_line_found = 0;
11535   browse_filter_t *filter = NULL;
11536   int browse_filter_options, exact_match, err;
11537   char errbuf[1024];
11538   cluster_t *cluster = NULL;
11539 
11540   if (!filename)
11541     filename = CUPS_SERVERROOT "/cups-browsed.conf";
11542 
11543   if ((fp = cupsFileOpen(filename, "r")) == NULL) {
11544     debug_printf("unable to open configuration file; "
11545 		 "using defaults\n");
11546     return;
11547   }
11548 
11549   i = 0;
11550   linenum = -1;
11551   /* First, we read the option settings supplied on the command line via
11552      "-o ..." in the order given on the command line, then we read the lines
11553      of the configuration file. This means that if there are contradicting
11554      settings on the command line and in the configuration file, the setting
11555      in the configuration file is used. */
11556   while ((i < cupsArrayCount(command_line_config) &&
11557 	  (value = cupsArrayIndex(command_line_config, i++)) &&
11558 	  strncpy(line, value, sizeof(line) - 1) &&
11559 	  ((strlen(value) > HTTP_MAX_BUFFER-1) ?
11560 	   line[HTTP_MAX_BUFFER-1] = '\0':  1)) ||
11561 	 cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) {
11562     if (linenum < 0) {
11563       /* We are still reading options from the command line ("-o ..."),
11564 	 separate key (line) and value (value) */
11565       value = line;
11566       while (*value && !isspace(*value) && !(*value == '='))
11567 	value ++;
11568       if (*value) {
11569 	*value = '\0';
11570 	value ++;
11571 	while (*value && (isspace(*value) || (*value == '=')))
11572 	  value ++;
11573       }
11574     }
11575 
11576     debug_printf("Reading config%s: %s %s\n",
11577 		 (linenum < 0 ? " (from command line)" : ""), line, value);
11578     if (!strcasecmp(line, "DebugLogging") && value) {
11579       char *p, *saveptr;
11580       p = strtok_r (value, delim, &saveptr);
11581       while (p) {
11582 	if (!strcasecmp(p, "file")) {
11583 	  if (debug_logfile == 0) {
11584 	    debug_logfile = 1;
11585 	    start_debug_logging();
11586 	  }
11587 	} else if (!strcasecmp(p, "stderr"))
11588 	  debug_stderr = 1;
11589 	else if (strcasecmp(p, "none"))
11590 	  debug_printf("Unknown debug logging mode '%s'\n", p);
11591 
11592 	p = strtok_r (NULL, delim, &saveptr);
11593       }
11594     } else if (!strcasecmp(line, "CacheDir") && value) {
11595       if (value[0] != '\0')
11596 	strncpy(cachedir, value, sizeof(cachedir) - 1);
11597     } else if (!strcasecmp(line, "LogDir") && value) {
11598       if (value[0] != '\0')
11599 	strncpy(logdir, value, sizeof(logdir) - 1);
11600     } else if ((!strcasecmp(line, "BrowseProtocols") ||
11601 		!strcasecmp(line, "BrowseLocalProtocols") ||
11602 		!strcasecmp(line, "BrowseRemoteProtocols")) && value) {
11603       int protocols = 0;
11604       char *p, *saveptr;
11605       p = strtok_r (value, delim, &saveptr);
11606       while (p) {
11607 	if (!strcasecmp(p, "dnssd"))
11608 	  protocols |= BROWSE_DNSSD;
11609 	else if (!strcasecmp(p, "cups"))
11610 	  protocols |= BROWSE_CUPS;
11611 	else if (!strcasecmp(p, "ldap"))
11612 	  protocols |= BROWSE_LDAP;
11613 	else if (strcasecmp(p, "none"))
11614 	  debug_printf("Unknown protocol '%s'\n", p);
11615 
11616 	p = strtok_r (NULL, delim, &saveptr);
11617       }
11618 
11619       if (!strcasecmp(line, "BrowseLocalProtocols"))
11620 	BrowseLocalProtocols = protocols;
11621       else if (!strcasecmp(line, "BrowseRemoteProtocols"))
11622 	BrowseRemoteProtocols = protocols;
11623       else
11624 	BrowseLocalProtocols = BrowseRemoteProtocols = protocols;
11625     } else if (!strcasecmp(line, "BrowsePoll") && value) {
11626       browsepoll_t **old = BrowsePoll;
11627       BrowsePoll = realloc (BrowsePoll,
11628 			    (NumBrowsePoll + 1) *
11629 			    sizeof (browsepoll_t *));
11630       if (!BrowsePoll) {
11631 	debug_printf("unable to realloc: ignoring BrowsePoll line\n");
11632 	BrowsePoll = old;
11633       } else {
11634 	char *colon, *slash;
11635 	browsepoll_t *b = g_malloc0 (sizeof (browsepoll_t));
11636 	debug_printf("Adding BrowsePoll server: %s\n", value);
11637 	b->server = strdup (value);
11638 	b->port = BrowsePort;
11639 	b->can_subscribe = TRUE; /* first assume subscriptions work */
11640 	b->subscription_id = -1;
11641 	slash = strchr (b->server, '/');
11642 	if (slash) {
11643 	  *slash++ = '\0';
11644 	  if (!strcasecmp (slash, "version=1.0")) {
11645 	    b->major = 1;
11646 	    b->minor = 0;
11647 	  } else if (!strcasecmp (slash, "version=1.1")) {
11648 	    b->major = 1;
11649 	    b->minor = 1;
11650 	  } else if (!strcasecmp (slash, "version=2.0")) {
11651 	    b->major = 2;
11652 	    b->minor = 0;
11653 	  } else if (!strcasecmp (slash, "version=2.1")) {
11654 	    b->major = 2;
11655 	    b->minor = 1;
11656 	  } else if (!strcasecmp (slash, "version=2.2")) {
11657 	    b->major = 2;
11658 	    b->minor = 2;
11659 	  } else {
11660 	    debug_printf ("ignoring unknown server option: %s\n", slash);
11661 	  }
11662 	} else
11663 	  b->major = 0;
11664 
11665 	colon = strchr (b->server, ':');
11666 	if (colon) {
11667 	  char *endptr;
11668 	  unsigned long n;
11669 	  *colon++ = '\0';
11670 	  n = strtoul (colon, &endptr, 10);
11671 	  if (endptr != colon && n < INT_MAX)
11672 	    b->port = (int) n;
11673 	}
11674 
11675 	BrowsePoll[NumBrowsePoll++] = b;
11676       }
11677     } else if (!strcasecmp(line, "BrowseAllow")) {
11678       if (read_browseallow_value (value, ALLOW_ALLOW))
11679 	debug_printf ("BrowseAllow value \"%s\" not understood\n",
11680 		      value);
11681       else {
11682 	browse_allow_line_found = 1;
11683 	browse_line_found = 1;
11684       }
11685     } else if (!strcasecmp(line, "BrowseDeny")) {
11686       if (read_browseallow_value (value, ALLOW_DENY))
11687 	debug_printf ("BrowseDeny value \"%s\" not understood\n",
11688 		      value);
11689       else {
11690 	browse_deny_line_found = 1;
11691 	browse_line_found = 1;
11692       }
11693     } else if (!strcasecmp(line, "BrowseOrder") && value) {
11694       if (!strncasecmp(value, "Allow", 5) &&
11695 	  strcasestr(value, "Deny")) { /* Allow,Deny */
11696 	browse_order = ORDER_ALLOW_DENY;
11697 	browse_order_line_found = 1;
11698 	browse_line_found = 1;
11699       } else if (!strncasecmp(value, "Deny", 4) &&
11700 		 strcasestr(value, "Allow")) { /* Deny,Allow */
11701 	browse_order = ORDER_DENY_ALLOW;
11702 	browse_order_line_found = 1;
11703 	browse_line_found = 1;
11704       } else
11705 	debug_printf ("BrowseOrder value \"%s\" not understood\n",
11706 		      value);
11707     } else if (!strcasecmp(line, "BrowseFilter") && value) {
11708       ptr = value;
11709       /* Skip white space */
11710       while (*ptr && isspace(*ptr)) ptr ++;
11711       /* Premature line end */
11712       if (!*ptr) goto browse_filter_fail;
11713       filter = calloc (1, sizeof (browse_filter_t));
11714       if (!filter) goto browse_filter_fail;
11715       browse_filter_options = 1;
11716       filter->sense = FILTER_MATCH;
11717       exact_match = 0;
11718       while (browse_filter_options) {
11719 	if (!strncasecmp(ptr, "NOT", 3) && *(ptr + 3) &&
11720 	    isspace(*(ptr + 3))) {
11721 	  /* Accept remote printers where regexp does NOT match or where
11722 	     the boolean field is false */
11723 	  filter->sense = FILTER_NOT_MATCH;
11724 	  ptr += 4;
11725 	  /* Skip white space until next word */
11726 	  while (*ptr && isspace(*ptr)) ptr ++;
11727 	  /* Premature line end without field name */
11728 	  if (!*ptr) goto browse_filter_fail;
11729 	} else if (!strncasecmp(ptr, "EXACT", 5) && *(ptr + 5) &&
11730 		   isspace(*(ptr + 5))) {
11731 	  /* Consider the rest of the line after the field name a string which
11732 	     has to match the field exactly */
11733 	  exact_match = 1;
11734 	  ptr += 6;
11735 	  /* Skip white space until next word */
11736 	  while (*ptr && isspace(*ptr)) ptr ++;
11737 	  /* Premature line end without field name */
11738 	  if (!*ptr) goto browse_filter_fail;
11739 	} else
11740 	  /* No more options, consider next word the name of the field which
11741 	     should match the regexp */
11742 	  browse_filter_options = 0;
11743       }
11744       start = ptr;
11745       while (*ptr && !isspace(*ptr)) ptr ++;
11746       if (*ptr) {
11747 	/* Mark end of the field name */
11748 	*ptr = '\0';
11749 	/* Skip white space until regexp or line end */
11750 	ptr ++;
11751 	while (*ptr && isspace(*ptr)) ptr ++;
11752       }
11753       filter->field = strdup(start);
11754       if (!*ptr) {
11755 	/* Only field name and no regexp is given, so this rule is
11756 	   about matching a boolean value */
11757 	filter->regexp = NULL;
11758 	filter->cregexp = NULL;
11759       } else {
11760 	/* The rest of the line is the regexp, store and compile it */
11761 	filter->regexp = strdup(ptr);
11762 	if (!exact_match) {
11763 	  /* Compile the regexp only if the line does not require an exact
11764 	     match (using the EXACT option */
11765 	  filter->cregexp = calloc(1, sizeof (regex_t));
11766 	  if ((err = regcomp(filter->cregexp, filter->regexp,
11767 			     REG_EXTENDED | REG_ICASE)) != 0) {
11768 	    regerror(err, filter->cregexp, errbuf, sizeof(errbuf));
11769 	    debug_printf ("BrowseFilter line with error in regular expression \"%s\": %s\n",
11770 			  filter->regexp, errbuf);
11771 	    goto browse_filter_fail;
11772 	  }
11773 	} else
11774 	  filter->cregexp = NULL;
11775       }
11776       cupsArrayAdd (browsefilter, filter);
11777       continue;
11778     browse_filter_fail:
11779       if (filter) {
11780 	if (filter->field)
11781 	  free(filter->field);
11782 	if (filter->regexp)
11783 	  free(filter->regexp);
11784 	if (filter->cregexp)
11785 	  regfree(filter->cregexp);
11786 	free(filter);
11787 	filter = NULL;
11788       }
11789     } else if ((!strcasecmp(line, "BrowseInterval") || !strcasecmp(line, "BrowseTimeout")) && value) {
11790       int t = atoi(value);
11791       if (t >= 0) {
11792 	if (!strcasecmp(line, "BrowseInterval"))
11793 	  BrowseInterval = t;
11794 	else if (!strcasecmp(line, "BrowseTimeout"))
11795 	  BrowseTimeout = t;
11796 
11797 	debug_printf("Set %s to %d sec.\n",
11798 		     line, t);
11799       } else
11800 	debug_printf("Invalid %s value: %d\n",
11801 		     line, t);
11802     } else if (!strcasecmp(line, "DomainSocket") && value) {
11803       if (value[0] != '\0') {
11804 	if (DomainSocket != NULL)
11805 	  free(DomainSocket);
11806 	DomainSocket = strdup(value);
11807       }
11808     } else if ((!strcasecmp(line, "HttpLocalTimeout") || !strcasecmp(line, "HttpRemoteTimeout")) && value) {
11809       int t = atoi(value);
11810       if (t >= 0) {
11811 	if (!strcasecmp(line, "HttpLocalTimeout"))
11812 	  HttpLocalTimeout = t;
11813 	else if (!strcasecmp(line, "HttpRemoteTimeout"))
11814 	  HttpRemoteTimeout = t;
11815 
11816 	debug_printf("Set %s to %d sec.\n",
11817 		     line, t);
11818       } else
11819 	debug_printf("Invalid %s value: %d\n",
11820 		     line, t);
11821     } else if (!strcasecmp(line, "NotifLeaseDuration") && value) {
11822       int t = atoi(value);
11823       if (t >= 300) {
11824 	  notify_lease_duration = t;
11825 	debug_printf("Set %s to %d sec.\n",
11826 		     line, t);
11827       } else
11828 	debug_printf("Invalid %s value: %d\n",
11829 		     line, t);
11830     } else if (!strcasecmp(line, "HttpMaxRetries") && value) {
11831       int t = atoi(value);
11832       if (t > 0) {
11833 	HttpMaxRetries = t;
11834 
11835 	debug_printf("Set %s to %d retries.\n",
11836 		     line, t);
11837       } else
11838 	debug_printf("Invalid %s value: %d\n",
11839 		     line, t);
11840     } else if (!strcasecmp(line, "DNSSDBasedDeviceURIs") && value) {
11841       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11842 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11843 	DNSSDBasedDeviceURIs = 1;
11844       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11845 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11846 	DNSSDBasedDeviceURIs = 0;
11847     } else if (!strcasecmp(line, "IPBasedDeviceURIs") && value) {
11848       if (!strcasecmp(value, "IPv4") || !strcasecmp(value, "IPv4Only"))
11849 	IPBasedDeviceURIs = IP_BASED_URIS_IPV4_ONLY;
11850       else if (!strcasecmp(value, "IPv6") || !strcasecmp(value, "IPv6Only"))
11851 	IPBasedDeviceURIs = IP_BASED_URIS_IPV6_ONLY;
11852       else if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11853 	       !strcasecmp(value, "on") || !strcasecmp(value, "1") ||
11854 	       !strcasecmp(value, "IP") || !strcasecmp(value, "IPAddress"))
11855 	IPBasedDeviceURIs = IP_BASED_URIS_ANY;
11856       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11857 	       !strcasecmp(value, "off") || !strcasecmp(value, "0") ||
11858 	       !strcasecmp(value, "Name") || !strcasecmp(value, "HostName"))
11859 	IPBasedDeviceURIs = IP_BASED_URIS_NO;
11860     } else if (!strcasecmp(line, "LocalQueueNamingRemoteCUPS") && value) {
11861       if (strcasestr(value, "DNSSD") || strcasestr(value, "DNS-SD"))
11862 	LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_DNSSD;
11863       else if (strcasestr(value, "Make") && strcasestr(value, "Model"))
11864 	LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_MAKE_MODEL;
11865       else if (strcasestr(value, "Remote") || strcasestr(value, "Name"))
11866 	LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_REMOTE_NAME;
11867     } else if (!strcasecmp(line, "LocalQueueNamingIPPPrinter") && value) {
11868       if (strcasestr(value, "DNSSD") || strcasestr(value, "DNS-SD"))
11869 	LocalQueueNamingIPPPrinter = LOCAL_QUEUE_NAMING_DNSSD;
11870       else if (strcasestr(value, "Make") && strcasestr(value, "Model"))
11871 	LocalQueueNamingIPPPrinter = LOCAL_QUEUE_NAMING_MAKE_MODEL;
11872     } else if (!strcasecmp(line, "OnlyUnsupportedByCUPS") && value) {
11873       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11874 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11875 	OnlyUnsupportedByCUPS = 1;
11876       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11877 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11878 	OnlyUnsupportedByCUPS = 0;
11879     } else if (!strcasecmp(line, "UseCUPSGeneratedPPDs") && value) {
11880       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11881 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11882 	UseCUPSGeneratedPPDs = 1;
11883       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11884 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11885 	UseCUPSGeneratedPPDs = 0;
11886     } else if (!strcasecmp(line, "CreateRemoteRawPrinterQueues") && value) {
11887       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11888 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11889 	CreateRemoteRawPrinterQueues = 1;
11890       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11891 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11892 	CreateRemoteRawPrinterQueues = 0;
11893     } else if (!strcasecmp(line, "CreateRemoteCUPSPrinterQueues") && value) {
11894       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11895 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11896 	CreateRemoteCUPSPrinterQueues = 1;
11897       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11898 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11899 	CreateRemoteCUPSPrinterQueues = 0;
11900     } else if (!strcasecmp(line, "CreateIPPPrinterQueues") && value) {
11901       if (!strcasecmp(value, "all") ||
11902 	  !strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11903 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11904 	CreateIPPPrinterQueues = IPP_PRINTERS_ALL;
11905       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11906 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11907 	CreateIPPPrinterQueues = IPP_PRINTERS_NO;
11908       else if (strcasestr(value, "local") || strcasestr(value, "usb"))
11909 	CreateIPPPrinterQueues = IPP_PRINTERS_LOCAL_ONLY;
11910       else if (strcasestr(value, "driver") && strcasestr(value, "less"))
11911 	CreateIPPPrinterQueues = IPP_PRINTERS_DRIVERLESS;
11912       else if (strcasestr(value, "every") || strcasestr(value, "pwg"))
11913 	CreateIPPPrinterQueues = IPP_PRINTERS_PWGRASTER;
11914       else if (strcasestr(value, "apple") || strcasestr(value, "air"))
11915 	CreateIPPPrinterQueues = IPP_PRINTERS_APPLERASTER;
11916       else if (strcasestr(value, "pclm") || strcasestr(value, "pcl-m"))
11917 	CreateIPPPrinterQueues = IPP_PRINTERS_PCLM;
11918       else if (strcasestr(value, "pdf"))
11919 	CreateIPPPrinterQueues = IPP_PRINTERS_PDF;
11920     } else if (!strcasecmp(line, "IPPPrinterQueueType") && value) {
11921       if (!strncasecmp(value, "Auto", 4))
11922 	IPPPrinterQueueType = PPD_YES;
11923       else if (!strncasecmp(value, "PPD", 3))
11924 	IPPPrinterQueueType = PPD_YES;
11925       else if (!strncasecmp(value, "NoPPD", 5))
11926 	IPPPrinterQueueType = PPD_NO;
11927       else if (!strncasecmp(value, "Interface", 9))
11928 	IPPPrinterQueueType = PPD_NO;
11929     } else if (!strcasecmp(line, "NewIPPPrinterQueuesShared") && value) {
11930       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11931 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11932 	NewIPPPrinterQueuesShared = 1;
11933       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11934 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11935 	NewIPPPrinterQueuesShared = 0;
11936     } else if(!strcasecmp(line, "DebugLogFileSize") && value) {
11937       int val = atoi(value);
11938       if(val<=0){
11939         DebugLogFileSize=0;
11940       }
11941       else DebugLogFileSize=val;
11942     } else if (!strcasecmp(line, "AllowResharingRemoteCUPSPrinters") && value) {
11943       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11944 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11945 	AllowResharingRemoteCUPSPrinters = 1;
11946       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11947 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11948 	AllowResharingRemoteCUPSPrinters = 0;
11949     } else if (!strcasecmp(line, "NewBrowsePollQueuesShared") && value) {
11950       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11951 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11952 	NewBrowsePollQueuesShared = 1;
11953       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11954 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11955 	NewBrowsePollQueuesShared = 0;
11956     } else if (!strcasecmp(line, "KeepGeneratedQueuesOnShutdown") && value) {
11957       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11958 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11959 	KeepGeneratedQueuesOnShutdown = 1;
11960       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11961 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11962 	KeepGeneratedQueuesOnShutdown = 0;
11963     } else if (!strcasecmp(line, "AutoClustering") && value) {
11964       if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11965 	  !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11966 	AutoClustering = 1;
11967       else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11968 	       !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11969 	AutoClustering = 0;
11970     } else if (!strcasecmp(line, "Cluster") && value) {
11971       ptr = value;
11972       ptr2 = NULL;
11973       /* Skip white space */
11974       while (*ptr && isspace(*ptr)) ptr ++;
11975       /* Premature line end */
11976       if (!*ptr) goto cluster_fail;
11977       /* Find the local queue name for the cluster */
11978       start = ptr;
11979       while (*ptr && !isspace(*ptr) && *ptr != ':') ptr ++;
11980       if (*ptr) {
11981 	/* Mark end of the local queue name */
11982 	*ptr = '\0';
11983 	/* Skip colon and white space until next word or line end */
11984 	ptr ++;
11985 	while (*ptr && (isspace(*ptr) || *ptr == ':')) ptr ++;
11986       }
11987       /* Empty queue name */
11988       if (strlen(start) <= 0)
11989 	goto cluster_fail;
11990       /* Clean queue name */
11991       ptr2 = remove_bad_chars(start, 0);
11992       /* Check whether we have already a cluster with this name */
11993       for (cluster = cupsArrayFirst(clusters);
11994 	   cluster;
11995 	   cluster = cupsArrayNext(clusters))
11996 	if (!strcasecmp(ptr2, cluster->local_queue_name)) {
11997 	  debug_printf("Duplicate cluster with queue name \"%s\".\n",
11998 		       ptr2);
11999 	  cluster = NULL;
12000 	  goto cluster_fail;
12001 	}
12002       /* Create the new cluster definition */
12003       cluster = calloc (1, sizeof (cluster_t));
12004       if (!cluster) goto cluster_fail;
12005       cluster->local_queue_name = ptr2;
12006       cluster->members = cupsArrayNew(NULL, NULL);
12007       ptr2 = NULL;
12008       if (!*ptr) {
12009 	/* Only local queue name given, so assume this name as the only
12010 	   member name (only remote queues with this name match) */
12011 	cupsArrayAdd(cluster->members, remove_bad_chars(ptr2, 2));
12012       } else {
12013 	/* The rest of the line lists one or more member queue names */
12014 	while (*ptr) {
12015 	  start = ptr;
12016 	  while (*ptr && !isspace(*ptr)) ptr ++;
12017 	  if (*ptr) {
12018 	    /* Mark end of the current word */
12019 	    *ptr = '\0';
12020 	    /* Skip white space until next word or line end */
12021 	    ptr ++;
12022 	    while (*ptr && isspace(*ptr)) ptr ++;
12023 	  }
12024 	  /* Add member queue name to the list */
12025 	  if (strlen(start) > 0)
12026 	    cupsArrayAdd(cluster->members, remove_bad_chars(start, 2));
12027 	}
12028       }
12029       cupsArrayAdd (clusters, cluster);
12030       if (ptr2 != NULL) {
12031         free(ptr2);
12032         ptr2 = NULL;
12033       }
12034       continue;
12035     cluster_fail:
12036       if (cluster) {
12037 	if (cluster->local_queue_name)
12038 	  free(cluster->local_queue_name);
12039 	if (cluster->members) {
12040 	  while ((ptr = cupsArrayFirst (cluster->members)) != NULL) {
12041 	    cupsArrayRemove (cluster->members, ptr);
12042 	    free (ptr);
12043 	  }
12044 	  cupsArrayDelete (cluster->members);
12045 	}
12046 	free(cluster);
12047         cluster = NULL;
12048       }
12049       if (ptr2 != NULL) {
12050         free(ptr2);
12051         ptr2 = NULL;
12052       }
12053     } else if (!strcasecmp(line, "LoadBalancing") && value) {
12054       if (!strncasecmp(value, "QueueOnClient", 13))
12055 	LoadBalancingType = QUEUE_ON_CLIENT;
12056       else if (!strncasecmp(value, "QueueOnServers", 14))
12057 	LoadBalancingType = QUEUE_ON_SERVERS;
12058     } else if (!strcasecmp(line, "DefaultOptions") && value) {
12059       if (DefaultOptions == NULL && strlen(value) > 0)
12060 	DefaultOptions = strdup(value);
12061     } else if (!strcasecmp(line, "AutoShutdown") && value) {
12062       char *p, *saveptr;
12063       p = strtok_r (value, delim, &saveptr);
12064       while (p) {
12065 	if (!strcasecmp(p, "On") || !strcasecmp(p, "Yes") ||
12066 	    !strcasecmp(p, "True") || !strcasecmp(p, "1")) {
12067 	  autoshutdown = 1;
12068 	  debug_printf("Turning on auto shutdown mode.\n");
12069 	} else if (!strcasecmp(p, "Off") || !strcasecmp(p, "No") ||
12070 		   !strcasecmp(p, "False") || !strcasecmp(p, "0")) {
12071 	  autoshutdown = 0;
12072 	  debug_printf("Turning off auto shutdown mode (permanent mode).\n");
12073 	} else if (!strcasecmp(p, "avahi")) {
12074 	  autoshutdown_avahi = 1;
12075 	  debug_printf("Turning on auto shutdown control by appearing and disappearing of the Avahi server.\n");
12076 	} else if (strcasecmp(p, "none"))
12077 	  debug_printf("Unknown mode '%s'\n", p);
12078 	p = strtok_r (NULL, delim, &saveptr);
12079       }
12080     } else if (!strcasecmp(line, "AutoShutdownTimeout") && value) {
12081       int t = atoi(value);
12082       if (t >= 0) {
12083 	autoshutdown_timeout = t;
12084 	debug_printf("Set auto shutdown timeout to %d sec.\n",
12085 		     t);
12086       } else
12087 	debug_printf("Invalid auto shutdown timeout value: %d\n",
12088 		     t);
12089     } else if (!strcasecmp(line, "AutoShutdownOn") && value) {
12090       int success = 0;
12091       if (!strncasecmp(value, "no", 2)) {
12092 	if (strcasestr(value + 2, "queue")) {
12093 	  autoshutdown_on = NO_QUEUES;
12094 	  success = 1;
12095 	} else if (strcasestr(value + 2, "job")) {
12096 	  autoshutdown_on = NO_JOBS;
12097 	  success = 1;
12098 	}
12099       }
12100       if (success)
12101 	debug_printf("Set auto shutdown inactivity type to no %s.\n",
12102 		     autoshutdown_on == NO_QUEUES ? "queues" : "jobs");
12103       else
12104 	debug_printf("Invalid auto shutdown inactivity type value: %s\n",
12105 		     value);
12106     } else if (!strcasecmp(line, "UpdateCUPSQueuesMaxPerCall") && value) {
12107       int n = atoi(value);
12108       if (n >= 0) {
12109 	update_cups_queues_max_per_call = n;
12110 	if (n > 0)
12111 	  debug_printf("Set maximum of CUPS queue updates per call of update_cups_queues() to %d.\n",
12112 		       n);
12113 	else
12114 	  debug_printf("Do not limit the number of CUPS queue updates per call of update_cups_queues().\n");
12115       } else
12116 	debug_printf("Invalid value for maximum number of CUPS queue updates per call of update_cups_queues(): %d\n",
12117 		     n);
12118     } else if (!strcasecmp(line, "PauseBetweenCUPSQueueUpdates") && value) {
12119       int t = atoi(value);
12120       if (t >= 0) {
12121 	pause_between_cups_queue_updates = t;
12122 	debug_printf("Set pause between calls of update_cups_queues() to %d sec.\n",
12123 		     t);
12124       } else
12125 	debug_printf("Invalid value for pause between calls of update_cups_queues(): %d\n",
12126 		     t);
12127     }
12128 #ifdef HAVE_LDAP
12129     else if (!strcasecmp(line, "BrowseLDAPBindDN") && value) {
12130       if (value[0] != '\0')
12131 	BrowseLDAPBindDN = strdup(value);
12132     }
12133 #  ifdef HAVE_LDAP_SSL
12134     else if (!strcasecmp(line, "BrowseLDAPCACertFile") && value) {
12135       if (value[0] != '\0')
12136 	BrowseLDAPCACertFile = strdup(value);
12137     }
12138 #  endif /* HAVE_LDAP_SSL */
12139     else if (!strcasecmp(line, "BrowseLDAPDN") && value) {
12140       if (value[0] != '\0')
12141 	BrowseLDAPDN = strdup(value);
12142     } else if (!strcasecmp(line, "BrowseLDAPPassword") && value) {
12143       if (value[0] != '\0')
12144 	BrowseLDAPPassword = strdup(value);
12145     } else if (!strcasecmp(line, "BrowseLDAPServer") && value) {
12146       if (value[0] != '\0')
12147 	BrowseLDAPServer = strdup(value);
12148     } else if (!strcasecmp(line, "BrowseLDAPFilter") && value) {
12149       if (value[0] != '\0')
12150 	BrowseLDAPFilter = strdup(value);
12151     }
12152 #endif /* HAVE_LDAP */
12153   }
12154 
12155   if (browse_line_found == 0) {
12156     /* No "Browse..." lines at all */
12157     browseallow_all = 1;
12158     browse_order = ORDER_DENY_ALLOW;
12159     debug_printf("No \"Browse...\" line at all, accept all servers (\"BrowseOrder Deny,Allow\").\n");
12160   } else if (browse_order_line_found == 0) {
12161     /* No "BrowseOrder" line */
12162     if (browse_allow_line_found == 0) {
12163       /* Only "BrowseDeny" lines */
12164       browse_order = ORDER_DENY_ALLOW;
12165       debug_printf("No \"BrowseOrder\" line and only \"BrowseDeny\" lines, accept all except what matches the \"BrowseDeny\" lines  (\"BrowseOrder Deny,Allow\").\n");
12166     } else if (browse_deny_line_found == 0) {
12167       /* Only "BrowseAllow" lines */
12168       browse_order = ORDER_ALLOW_DENY;
12169       debug_printf("No \"BrowseOrder\" line and only \"BrowseAllow\" lines, deny all except what matches the \"BrowseAllow\" lines  (\"BrowseOrder Allow,Deny\").\n");
12170     } else {
12171       /* Default for "BrowseOrder" */
12172       browse_order = ORDER_DENY_ALLOW;
12173       debug_printf("No \"BrowseOrder\" line, use \"BrowseOrder Deny,Allow\" as default.\n");
12174     }
12175   }
12176 
12177   cupsFileClose(fp);
12178 }
12179 
12180 static void
defer_update_netifs(void)12181 defer_update_netifs (void)
12182 {
12183   if (update_netifs_sourceid)
12184     g_source_remove (update_netifs_sourceid);
12185 
12186   update_netifs_sourceid = g_timeout_add_seconds (10, update_netifs, NULL);
12187 }
12188 
12189 static void
nm_properties_changed(GDBusProxy * proxy,GVariant * changed_properties,const gchar * const * invalidated_properties,gpointer user_data)12190 nm_properties_changed (GDBusProxy *proxy,
12191 		       GVariant *changed_properties,
12192 		       const gchar *const *invalidated_properties,
12193 		       gpointer user_data)
12194 {
12195   GVariantIter *iter;
12196   const gchar *key;
12197   GVariant *value;
12198   debug_printf("nm_properties_changed() in THREAD %ld\n", pthread_self());
12199   g_variant_get (changed_properties, "a{sv}", &iter);
12200   while (g_variant_iter_loop (iter, "{&sv}", &key, &value)) {
12201     if (!strcmp (key, "ActiveConnections")) {
12202       debug_printf ("NetworkManager ActiveConnections changed\n");
12203       defer_update_netifs ();
12204       break;
12205     }
12206   }
12207 
12208   g_variant_iter_free (iter);
12209 }
12210 
12211 static void
find_previous_queue(gpointer key,gpointer value,gpointer user_data)12212 find_previous_queue (gpointer key,
12213 		     gpointer value,
12214 		     gpointer user_data)
12215 {
12216   const char *name = key;
12217   const local_printer_t *printer = value;
12218   remote_printer_t *p;
12219   debug_printf("find_previous_queue() in THREAD %ld\n", pthread_self());
12220   if (printer->cups_browsed_controlled) {
12221     /* Queue found, add to our list */
12222     p = create_remote_printer_entry (name, "", "", "", "", "",
12223 				     0, "", "", "", "", "", 0, NULL, 0, 0, NULL,
12224 				     -1);
12225     if (p) {
12226       /* Mark as unconfirmed, if no Avahi report of this queue appears
12227 	 in a certain time frame, we will remove the queue */
12228       p->status = STATUS_UNCONFIRMED;
12229 
12230       if (BrowseRemoteProtocols & BROWSE_CUPS)
12231 	p->timeout = time(NULL) + BrowseInterval * 3 / 2;
12232       else
12233 	p->timeout = time(NULL) + TIMEOUT_CONFIRM;
12234 
12235       p->slave_of = NULL;
12236       debug_printf("Found CUPS queue %s (URI: %s) from previous session.\n",
12237 		   p->queue_name, p->uri);
12238     } else
12239       debug_printf("ERROR: Unable to create print queue entry for printer of previous session: %s (%s).\n",
12240 		   name, printer->device_uri);
12241   }
12242 }
12243 
main(int argc,char * argv[])12244 int main(int argc, char*argv[]) {
12245   int ret = 1;
12246   http_t *http;
12247   int i;
12248   char *val;
12249   remote_printer_t *p;
12250   GDBusProxy *proxy = NULL;
12251   GError *error = NULL;
12252   int subscription_id = 0;
12253 
12254   /* Initialise the command_line_config array */
12255   command_line_config = cupsArrayNew(NULL, NULL);
12256 
12257   /* Initialise the browseallow array */
12258   browseallow = cupsArrayNew(NULL, NULL);
12259 
12260   /* Initialise the browsefilter array */
12261   browsefilter = cupsArrayNew(NULL, NULL);
12262 
12263   /* Initialise the clusters array */
12264   clusters = cupsArrayNew(NULL, NULL);
12265 
12266   /* Read command line options */
12267   if (argc >= 2) {
12268     for (i = 1; i < argc; i++)
12269       if (!strcasecmp(argv[i], "--debug") || !strcasecmp(argv[i], "-d") ||
12270 	  !strncasecmp(argv[i], "-v", 2)) {
12271 	/* Turn on debug output mode if requested */
12272 	debug_stderr = 1;
12273 	debug_printf("Reading command line option %s, turning on debug mode (Log on standard error).\n",
12274 		     argv[i]);
12275       } else if (!strcasecmp(argv[i], "--logfile") ||
12276 		 !strcasecmp(argv[i], "-l")) {
12277 	/* Turn on debug log file mode if requested */
12278 	if (debug_logfile == 0) {
12279 	  debug_logfile = 1;
12280 	  start_debug_logging();
12281 	  debug_printf("Reading command line option %s, turning on debug mode (Log into log file %s).\n",
12282 		       argv[i], debug_log_file);
12283 	}
12284       } else if (!strncasecmp(argv[i], "-c", 2)) {
12285 	/* Alternative configuration file */
12286 	val = argv[i] + 2;
12287 	if (strlen(val) == 0) {
12288 	  i ++;
12289 	  if (i < argc && *argv[i] != '-')
12290 	    val = argv[i];
12291 	  else
12292 	    val = NULL;
12293 	}
12294 	if (val) {
12295 	  alt_config_file = strdup(val);
12296 	  debug_printf("Reading command line option -c %s, using alternative configuration file.\n",
12297 		       alt_config_file);
12298 	} else {
12299 	  fprintf(stderr,
12300 		  "Reading command line option -c, no alternative configuration file name supplied.\n\n");
12301 	  goto help;
12302 	}
12303       } else if (!strncasecmp(argv[i], "-o", 2)) {
12304 	/* Configuration option via command line */
12305 	val = argv[i] + 2;
12306 	if (strlen(val) == 0) {
12307 	  i ++;
12308 	  if (i < argc && *argv[i] != '-')
12309 	    val = argv[i];
12310 	  else
12311 	    val = NULL;
12312 	}
12313 	if (val) {
12314 	  cupsArrayAdd (command_line_config, strdup(val));
12315 	  debug_printf("Reading command line option -o %s, applying extra configuration option.\n",
12316 		       val);
12317 	} else {
12318 	  fprintf(stderr,
12319 		  "Reading command line option -o, no extra configuration option supplied.\n\n");
12320 	  goto help;
12321 	}
12322       } else if (!strncasecmp(argv[i], "--autoshutdown-timeout", 22)) {
12323 	debug_printf("Reading command line: %s\n", argv[i]);
12324 	if (argv[i][22] == '=' && argv[i][23])
12325 	  val = argv[i] + 23;
12326 	else if (!argv[i][22] && i < argc -1) {
12327 	  i++;
12328 	  debug_printf("Reading command line: %s\n", argv[i]);
12329 	  val = argv[i];
12330 	} else {
12331 	  fprintf(stderr, "Expected auto shutdown timeout setting after \"--autoshutdown-timeout\" option.\n\n");
12332 	  goto help;
12333 	}
12334 	int t = atoi(val);
12335 	if (t >= 0) {
12336 	  autoshutdown_timeout = t;
12337 	  debug_printf("Set auto shutdown timeout to %d sec.\n",
12338 		       t);
12339 	} else {
12340 	  fprintf(stderr, "Invalid auto shutdown timeout value: %d\n\n",
12341 		  t);
12342 	  goto help;
12343 	}
12344       } else if (!strncasecmp(argv[i], "--autoshutdown-on", 17)) {
12345 	debug_printf("Reading command line: %s\n", argv[i]);
12346 	if (argv[i][17] == '=' && argv[i][18])
12347 	  val = argv[i] + 18;
12348 	else if (!argv[i][17] && i < argc - 1) {
12349 	  i++;
12350 	  debug_printf("Reading command line: %s\n", argv[i]);
12351 	  val = argv[i];
12352 	} else {
12353 	  fprintf(stderr, "Expected auto shutdown inactivity type (\"no-queues\" or \"no-jobs\") after \"--autoshutdown-on\" option.\n\n");
12354 	  goto help;
12355 	}
12356 	int success = 0;
12357 	if (!strncasecmp(val, "no", 2)) {
12358 	  if (strcasestr(val + 2, "queue")) {
12359 	    autoshutdown_on = NO_QUEUES;
12360 	    success = 1;
12361 	  } else if (strcasestr(val + 2, "job")) {
12362 	    autoshutdown_on = NO_JOBS;
12363 	    success = 1;
12364 	  }
12365 	}
12366 	if (success)
12367 	  debug_printf("Set auto shutdown inactivity type to no %s.\n",
12368 		       autoshutdown_on == NO_QUEUES ? "queues" : "jobs");
12369 	else
12370 	  debug_printf("Invalid auto shutdown inactivity type value: %s\n",
12371 		       val);
12372       } else if (!strncasecmp(argv[i], "--autoshutdown", 14)) {
12373 	debug_printf("Reading command line: %s\n", argv[i]);
12374 	if (argv[i][14] == '=' && argv[i][15])
12375 	  val = argv[i] + 15;
12376 	else if (!argv[i][14] && i < argc -1) {
12377 	  i++;
12378 	  debug_printf("Reading command line: %s\n", argv[i]);
12379 	  val = argv[i];
12380 	} else {
12381 	  fprintf(stderr, "Expected auto shutdown setting after \"--autoshutdown\" option.\n\n");
12382 	  goto help;
12383 	}
12384 	if (!strcasecmp(val, "On") || !strcasecmp(val, "Yes") ||
12385 	    !strcasecmp(val, "True") || !strcasecmp(val, "1")) {
12386 	  autoshutdown = 1;
12387 	  debug_printf("Turning on auto shutdown mode.\n");
12388 	} else if (!strcasecmp(val, "Off") || !strcasecmp(val, "No") ||
12389 		   !strcasecmp(val, "False") || !strcasecmp(val, "0")) {
12390 	  autoshutdown = 0;
12391 	  debug_printf("Turning off auto shutdown mode (permanent mode).\n");
12392 	} else if (!strcasecmp(val, "avahi")) {
12393 	  autoshutdown_avahi = 1;
12394 	  debug_printf("Turning on auto shutdown control by appearing and disappearing of the Avahi server.\n");
12395 	} else if (strcasecmp(val, "none")) {
12396 	  fprintf(stderr, "Unknown mode '%s'\n\n", val);
12397 	  goto help;
12398 	}
12399       } else if (!strcasecmp(argv[i], "--version") ||
12400 		 !strcasecmp(argv[i], "--help") || !strcasecmp(argv[i], "-h")) {
12401 	/* Help!! */
12402 	goto help;
12403       } else {
12404 	/* Unknown option */
12405 	fprintf(stderr,
12406 		"Reading command line option %s, unknown command line option.\n\n",
12407 		argv[i]);
12408         goto help;
12409       }
12410   }
12411 
12412   debug_printf("cups-browsed of cups-filters version "VERSION" starting.\n");
12413 
12414   /* Read in cups-browsed.conf */
12415   read_configuration (alt_config_file);
12416 
12417   /* Set the paths of the auxiliary files */
12418   if (cachedir[0] == '\0')
12419     strncpy(cachedir, DEFAULT_CACHEDIR, sizeof(cachedir) - 1);
12420   if (logdir[0] == '\0')
12421     strncpy(logdir, DEFAULT_LOGDIR, sizeof(logdir) - 1);
12422   strncpy(local_default_printer_file, cachedir,
12423 	  sizeof(local_default_printer_file) - 1);
12424   strncpy(local_default_printer_file + strlen(cachedir),
12425 	  LOCAL_DEFAULT_PRINTER_FILE,
12426 	  sizeof(local_default_printer_file) - strlen(cachedir) - 1);
12427   strncpy(remote_default_printer_file, cachedir,
12428 	  sizeof(remote_default_printer_file) - 1);
12429   strncpy(remote_default_printer_file + strlen(cachedir),
12430 	  REMOTE_DEFAULT_PRINTER_FILE,
12431 	  sizeof(remote_default_printer_file) - strlen(cachedir) - 1);
12432   strncpy(save_options_file, cachedir,
12433 	  sizeof(save_options_file) - 1);
12434   strncpy(save_options_file + strlen(cachedir),
12435 	  SAVE_OPTIONS_FILE,
12436 	  sizeof(save_options_file) - strlen(cachedir) - 1);
12437   strncpy(debug_log_file, logdir,
12438 	  sizeof(debug_log_file) - 1);
12439   strncpy(debug_log_file + strlen(logdir),
12440 	  DEBUG_LOG_FILE,
12441 	  sizeof(debug_log_file) - strlen(logdir) - 1);
12442 
12443   strncpy(debug_log_file_bckp, logdir,
12444 	  sizeof(debug_log_file_bckp) - 1);
12445   strncpy(debug_log_file_bckp + strlen(logdir),
12446 	  DEBUG_LOG_FILE_2,
12447 	  sizeof(debug_log_file_bckp) - strlen(logdir) - 1);
12448 
12449   if (debug_logfile == 1)
12450     start_debug_logging();
12451 
12452   debug_printf("main() in THREAD %ld\n", pthread_self());
12453 
12454   /* If a port is selected via the IPP_PORT environment variable,
12455      set this first */
12456   if (getenv("IPP_PORT") != NULL) {
12457     snprintf(local_server_str, sizeof(local_server_str) - 1,
12458 	     "localhost:%s", getenv("IPP_PORT"));
12459     local_server_str[sizeof(local_server_str) - 1] = '\0';
12460     cupsSetServer(local_server_str);
12461     debug_printf("Set port on which CUPS is listening via env variable: IPP_PORT=%s\n",
12462 		 getenv("IPP_PORT"));
12463   }
12464 
12465   /* Point to selected CUPS server or domain socket via the CUPS_SERVER
12466      environment variable or DomainSocket configuration file option.
12467      Default to localhost:631 (and not to CUPS default to override
12468      client.conf files as cups-browsed works only with a local CUPS
12469      daemon, not with remote ones. */
12470   local_server_str[0] = '\0';
12471   if (getenv("CUPS_SERVER") != NULL) {
12472     strncpy(local_server_str, getenv("CUPS_SERVER"),
12473 	    sizeof(local_server_str) - 1);
12474     local_server_str[sizeof(local_server_str) - 1] = '\0';
12475     cupsSetServer(local_server_str);
12476     debug_printf("Set host/port/domain socket which CUPS is listening via env variable: CUPS_SERVER=%s\n",
12477 		 getenv("CUPS_SERVER"));
12478   } else {
12479     if (DomainSocket != NULL) {
12480       debug_printf("Set host/port/domain socket on which CUPS is listening via cups-browsed directive DomainSocket: %s\n",
12481 		   DomainSocket);
12482       struct stat sockinfo;               /* Domain socket information */
12483       if (strcasecmp(DomainSocket, "None") != 0 &&
12484 	  strcasecmp(DomainSocket, "Off") != 0 &&
12485 	  !stat(DomainSocket, &sockinfo) &&
12486 	  (sockinfo.st_mode & S_IROTH) != 0 &&
12487 	  (sockinfo.st_mode & S_IWOTH) != 0) {
12488 	strncpy(local_server_str, DomainSocket,
12489 		sizeof(local_server_str) - 1);
12490 	local_server_str[sizeof(local_server_str) - 1] = '\0';
12491 	cupsSetServer(local_server_str);
12492       } else
12493 	debug_printf("DomainSocket %s not accessible: %s\n",
12494 		     DomainSocket, strerror(errno));
12495     }
12496   }
12497   if (local_server_str[0])
12498     setenv("CUPS_SERVER", local_server_str, 1);
12499 
12500   if (BrowseLocalProtocols & BROWSE_DNSSD) {
12501     debug_printf("Local support for DNSSD not implemented\n");
12502     BrowseLocalProtocols &= ~BROWSE_DNSSD;
12503   }
12504 
12505   if (BrowseLocalProtocols & BROWSE_LDAP) {
12506     debug_printf("Local support for LDAP not implemented\n");
12507     BrowseLocalProtocols &= ~BROWSE_LDAP;
12508   }
12509 
12510 #ifndef HAVE_AVAHI
12511   if (BrowseRemoteProtocols & BROWSE_DNSSD) {
12512     debug_printf("Remote support for DNSSD not supported\n");
12513     BrowseRemoteProtocols &= ~BROWSE_DNSSD;
12514   }
12515 #endif /* HAVE_AVAHI */
12516 
12517 #ifndef HAVE_LDAP
12518   if (BrowseRemoteProtocols & BROWSE_LDAP) {
12519     debug_printf("Remote support for LDAP not supported\n");
12520     BrowseRemoteProtocols &= ~BROWSE_LDAP;
12521   }
12522 #endif /* HAVE_LDAP */
12523 
12524   /* Wait for CUPS daemon to start */
12525   while ((http = http_connect_local ()) == NULL)
12526     sleep(1);
12527 
12528   /* Initialise the array of network interfaces */
12529   netifs = cupsArrayNew(NULL, NULL);
12530   local_hostnames = cupsArrayNew(NULL, NULL);
12531   update_netifs (NULL);
12532 
12533   local_printers = g_hash_table_new_full (g_str_hash,
12534 					  g_str_equal,
12535 					  g_free,
12536 					  free_local_printer);
12537   cups_supported_remote_printers = g_hash_table_new_full (g_str_hash,
12538 							  g_str_equal,
12539 							  g_free,
12540 							  free_local_printer);
12541 
12542   /* Read out the currently defined CUPS queues and find the ones which we
12543      have added in an earlier session */
12544   update_local_printers ();
12545   if ((val = get_cups_default_printer()) != NULL) {
12546     default_printer = strdup(val);
12547     free(val);
12548   }
12549   remote_printers = cupsArrayNew(NULL, NULL);
12550   g_hash_table_foreach (local_printers, find_previous_queue, NULL);
12551 
12552   /* Redirect SIGINT and SIGTERM so that we do a proper shutdown, removing
12553      the CUPS queues which we have created
12554      Use SIGUSR1 and SIGUSR2 to turn off and turn on auto shutdown mode
12555      resp. */
12556 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
12557   sigset(SIGTERM, sigterm_handler);
12558   sigset(SIGINT, sigterm_handler);
12559   sigset(SIGUSR1, sigusr1_handler);
12560   sigset(SIGUSR2, sigusr2_handler);
12561   debug_printf("Using signal handler SIGSET\n");
12562 #elif defined(HAVE_SIGACTION)
12563   struct sigaction action; /* Actions for POSIX signals */
12564   memset(&action, 0, sizeof(action));
12565   sigemptyset(&action.sa_mask);
12566   sigaddset(&action.sa_mask, SIGTERM);
12567   action.sa_handler = sigterm_handler;
12568   sigaction(SIGTERM, &action, NULL);
12569   sigemptyset(&action.sa_mask);
12570   sigaddset(&action.sa_mask, SIGINT);
12571   action.sa_handler = sigterm_handler;
12572   sigaction(SIGINT, &action, NULL);
12573   sigemptyset(&action.sa_mask);
12574   sigaddset(&action.sa_mask, SIGUSR1);
12575   action.sa_handler = sigusr1_handler;
12576   sigaction(SIGUSR1, &action, NULL);
12577   sigemptyset(&action.sa_mask);
12578   sigaddset(&action.sa_mask, SIGUSR2);
12579   action.sa_handler = sigusr2_handler;
12580   sigaction(SIGUSR2, &action, NULL);
12581   debug_printf("Using signal handler SIGACTION\n");
12582 #else
12583   signal(SIGTERM, sigterm_handler);
12584   signal(SIGINT, sigterm_handler);
12585   signal(SIGUSR1, sigusr1_handler);
12586   signal(SIGUSR2, sigusr2_handler);
12587   debug_printf("Using signal handler SIGNAL\n");
12588 #endif /* HAVE_SIGSET */
12589 
12590 #ifdef HAVE_AVAHI
12591   if (autoshutdown_avahi)
12592     autoshutdown = 1;
12593   avahi_init();
12594 #endif /* HAVE_AVAHI */
12595 
12596   if (autoshutdown == 1) {
12597     /* If there are no printers or no jobs schedule the shutdown in
12598        autoshutdown_timeout seconds */
12599     if (!autoshutdown_exec_id &&
12600 	(cupsArrayCount(remote_printers) == 0 ||
12601 	 (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
12602       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);
12603       autoshutdown_exec_id =
12604 	g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
12605 			       NULL);
12606     }
12607   }
12608 
12609   if (BrowseLocalProtocols & BROWSE_CUPS ||
12610       BrowseRemoteProtocols & BROWSE_CUPS) {
12611     /* Set up our CUPS Browsing socket */
12612     browsesocket = socket (AF_INET, SOCK_DGRAM, 0);
12613     if (browsesocket == -1) {
12614       debug_printf("failed to create CUPS Browsing socket: %s\n",
12615 		   strerror (errno));
12616     } else {
12617       struct sockaddr_in addr;
12618       memset (&addr, 0, sizeof (addr));
12619       addr.sin_addr.s_addr = htonl (INADDR_ANY);
12620       addr.sin_family = AF_INET;
12621       addr.sin_port = htons (BrowsePort);
12622       if (bind (browsesocket, (struct sockaddr *)&addr, sizeof (addr))) {
12623 	debug_printf("failed to bind CUPS Browsing socket: %s\n",
12624 		     strerror (errno));
12625 	close (browsesocket);
12626 	browsesocket = -1;
12627       } else {
12628 	int on = 1;
12629 	if (setsockopt (browsesocket, SOL_SOCKET, SO_BROADCAST,
12630 			&on, sizeof (on))) {
12631 	  debug_printf("failed to allow broadcast: %s\n",
12632 		       strerror (errno));
12633 	  BrowseLocalProtocols &= ~BROWSE_CUPS;
12634 	}
12635       }
12636     }
12637 
12638     if (browsesocket == -1) {
12639       BrowseLocalProtocols &= ~BROWSE_CUPS;
12640       BrowseRemoteProtocols &= ~BROWSE_CUPS;
12641     }
12642   }
12643 
12644   if (BrowseLocalProtocols == 0 &&
12645       BrowseRemoteProtocols == 0 &&
12646       !BrowsePoll) {
12647     debug_printf("nothing left to do\n");
12648     ret = 0;
12649     goto fail;
12650   }
12651 
12652   /* Override the default password callback so we don't end up
12653    * prompting for it. */
12654   cupsSetPasswordCB2 (password_callback, NULL);
12655 
12656   /* Watch NetworkManager for network interface changes */
12657   proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
12658 					 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
12659 					 NULL, /* GDBusInterfaceInfo */
12660 					 "org.freedesktop.NetworkManager",
12661 					 "/org/freedesktop/NetworkManager",
12662 					 "org.freedesktop.NetworkManager",
12663 					 NULL, /* GCancellable */
12664 					 NULL); /* GError */
12665 
12666   if (proxy)
12667     g_signal_connect (proxy,
12668 		      "g-properties-changed",
12669 		      G_CALLBACK (nm_properties_changed),
12670 		      NULL);
12671 
12672   /* Run the main loop */
12673   gmainloop = g_main_loop_new (NULL, FALSE);
12674   recheck_timer ();
12675 
12676   if (BrowseRemoteProtocols & BROWSE_CUPS) {
12677     GIOChannel *browse_channel = g_io_channel_unix_new (browsesocket);
12678     g_io_channel_set_close_on_unref (browse_channel, FALSE);
12679     g_io_add_watch (browse_channel, G_IO_IN, process_browse_data, NULL);
12680   }
12681 
12682   if (BrowseLocalProtocols & BROWSE_CUPS) {
12683     debug_printf ("will send browse data every %ds\n",
12684 		  BrowseInterval);
12685     g_idle_add (send_browse_data, NULL);
12686   }
12687 
12688 #ifdef HAVE_LDAP
12689   if (BrowseRemoteProtocols & BROWSE_LDAP) {
12690     debug_printf ("will browse poll LDAP every %ds\n",
12691 		  BrowseInterval);
12692     g_idle_add (browse_ldap_poll, NULL);
12693   }
12694 #endif /* HAVE_LDAP */
12695 
12696   if (BrowsePoll) {
12697     size_t index;
12698     for (index = 0;
12699 	 index < NumBrowsePoll;
12700 	 index++) {
12701       debug_printf ("will browse poll %s every %ds\n",
12702 		    BrowsePoll[index]->server, BrowseInterval);
12703       g_idle_add (browse_poll, BrowsePoll[index]);
12704     }
12705   }
12706 
12707   /* Subscribe to CUPS' D-Bus notifications and create a proxy to receive
12708      the notifications */
12709   subscription_id = create_subscription ();
12710   g_timeout_add_seconds (notify_lease_duration / 2,
12711 			 renew_subscription_timeout,
12712 			 &subscription_id);
12713   cups_notifier = cups_notifier_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
12714 							0,
12715 							NULL,
12716 							CUPS_DBUS_PATH,
12717 							NULL,
12718 							&error);
12719   if (error) {
12720     fprintf (stderr, "Error creating cups notify handler: %s", error->message);
12721     g_error_free (error);
12722     cups_notifier = NULL;
12723   }
12724   if (cups_notifier != NULL) {
12725     g_signal_connect (cups_notifier, "printer-state-changed",
12726 		      G_CALLBACK (on_printer_state_changed), NULL);
12727     g_signal_connect (cups_notifier, "job-state",
12728 		      G_CALLBACK (on_job_state), NULL);
12729     g_signal_connect (cups_notifier, "printer-deleted",
12730 		      G_CALLBACK (on_printer_deleted), NULL);
12731     g_signal_connect (cups_notifier, "printer-modified",
12732 		      G_CALLBACK (on_printer_modified), NULL);
12733   }
12734 
12735   /* If auto shutdown is active and we do not find any printers initially,
12736      schedule the shutdown in autoshutdown_timeout seconds */
12737   if (autoshutdown && !autoshutdown_exec_id &&
12738       cupsArrayCount(remote_printers) == 0) {
12739     debug_printf ("No printers found to make available, shutting down in %d sec...\n",
12740 		  autoshutdown_timeout);
12741     autoshutdown_exec_id =
12742       g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute, NULL);
12743   }
12744 
12745   g_main_loop_run (gmainloop);
12746 
12747   debug_printf("main loop exited\n");
12748   g_main_loop_unref (gmainloop);
12749   gmainloop = NULL;
12750   ret = 0;
12751 
12752 fail:
12753 
12754   /* Clean up things */
12755 
12756   in_shutdown = 1;
12757 
12758   if (proxy)
12759     g_object_unref (proxy);
12760 
12761   /* Remove all queues which we have set up */
12762   if (KeepGeneratedQueuesOnShutdown == 0)
12763     for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
12764 	 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
12765       if (p->status != STATUS_TO_BE_RELEASED)
12766 	p->status = STATUS_DISAPPEARED;
12767       p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
12768     }
12769   update_cups_queues(NULL);
12770 
12771   cancel_subscription (subscription_id);
12772   if (cups_notifier)
12773     g_object_unref (cups_notifier);
12774 
12775   if (BrowsePoll) {
12776     size_t index;
12777     for (index = 0;
12778 	 index < NumBrowsePoll;
12779 	 index++) {
12780       if (BrowsePoll[index]->can_subscribe &&
12781 	  BrowsePoll[index]->subscription_id != -1)
12782 	browse_poll_cancel_subscription (BrowsePoll[index]);
12783 
12784       free (BrowsePoll[index]->server);
12785       g_list_free_full (BrowsePoll[index]->printers,
12786 			browsepoll_printer_free);
12787       free (BrowsePoll[index]);
12788     }
12789 
12790     free (BrowsePoll);
12791   }
12792 
12793   if (local_printers_context) {
12794     browse_poll_cancel_subscription (local_printers_context);
12795 #ifdef HAVE_CUPS_2_0
12796     free(local_printers_context->server);
12797 #endif
12798     g_list_free_full (local_printers_context->printers,
12799 		      browsepoll_printer_free);
12800     free (local_printers_context);
12801   }
12802 
12803   http_close_local ();
12804 
12805 #ifdef HAVE_AVAHI
12806   avahi_shutdown();
12807 #endif /* HAVE_AVAHI */
12808 
12809 #ifdef HAVE_LDAP
12810   if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP) &&
12811       BrowseLDAPHandle) {
12812     ldap_disconnect(BrowseLDAPHandle);
12813     BrowseLDAPHandle = NULL;
12814   }
12815 #endif /* HAVE_LDAP */
12816 
12817   if (browsesocket != -1)
12818     close (browsesocket);
12819 
12820   g_hash_table_destroy (local_printers);
12821   g_hash_table_destroy (cups_supported_remote_printers);
12822 
12823   if (BrowseLocalProtocols & BROWSE_CUPS)
12824     g_list_free_full (browse_data, browse_data_free);
12825 
12826   /* Close log file if we have one */
12827   if (debug_logfile == 1)
12828     stop_debug_logging();
12829 
12830   if (deleted_master != NULL)
12831     free(deleted_master);
12832   if (DefaultOptions != NULL)
12833     free(DefaultOptions);
12834   if (DomainSocket != NULL)
12835     free(DomainSocket);
12836 
12837   return ret;
12838 
12839  help:
12840 
12841   fprintf(stderr,
12842 	  "cups-browsed of cups-filters version "VERSION"\n\n"
12843 	  "Usage: cups-browsed [options]\n"
12844 	  "Options:\n"
12845 	  "  -c cups-browsed.conf    Set alternative cups-browsed.conf file to use.\n"
12846 	  "  -d\n"
12847 	  "  -v\n"
12848 	  "  --debug                 Run in debug mode (logging to stderr).\n"
12849 	  "  -l\n"
12850 	  "  --logfile               Run in debug mode (logging into file).\n"
12851 	  "  -h\n"
12852 	  "  --help\n"
12853 	  "  --version               Show this usage message.\n"
12854 	  "  -o Option=Value         Supply configuration option via command line,\n"
12855 	  "                          options are the same as in cups-browsed.conf.\n"
12856 	  "  --autoshutdown=<mode>   Automatically shut down cups-browsed when inactive:\n"
12857 	  "                          <mode> can be set to Off, On, or avahi, where Off\n"
12858 	  "                          means that cups-browsed stays running permanently\n"
12859 	  "                          (default), On means that it shuts down after 30\n"
12860 	  "                          seconds (or any given timeout) of inactivity, and\n"
12861 	  "                          avahi means that cups-browsed shuts down when\n"
12862 	  "                          avahi-daemon shuts down.\n"
12863 	  "  --autoshutdown-timout=<time> Timeout (in seconds) for auto-shutdown.\n"
12864 	  "  --autoshutdown-on=<type> Type of inactivity which leads to an auto-\n"
12865 	  "                          shutdown: If <type> is \"no-queues\", the shutdown\n"
12866 	  "                          is triggered by not having any cups-browsed-created\n"
12867 	  "                          print queue any more. With <type> being \"no-jobs\"\n"
12868 	  "                          shutdown is initiated by no job being printed\n"
12869 	  "                          on any cups-browsed-generated print queue any more.\n"
12870 	  "                          \"no-queues\" is the default.\n"
12871 	  );
12872 
12873   return 1;
12874 }
12875