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, ¬e_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