1 /***
2 This file is part of cups-filters.
3
4 This file is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
8
9 This file is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12 Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with avahi; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 USA.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <ctype.h>
25 #include <errno.h>
26 #if defined(__OpenBSD__) || defined(__FreeBSD__)
27 #include <sys/socket.h>
28 #endif /* __OpenBSD__ */
29 #include <sys/types.h>
30 #include <net/if.h>
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 #include <resolv.h>
34 #include <stdio.h>
35 #include <sys/stat.h>
36 #include <ifaddrs.h>
37 #include <stdlib.h>
38 #include <time.h>
39 #include <signal.h>
40 #include <regex.h>
41 #include <pthread.h>
42
43 #include <glib.h>
44
45 #ifdef HAVE_AVAHI
46 #include <avahi-client/client.h>
47 #include <avahi-client/lookup.h>
48
49 #include <avahi-glib/glib-watch.h>
50 #include <avahi-common/malloc.h>
51 #include <avahi-common/error.h>
52 #endif /* HAVE_AVAHI */
53
54 #include <gio/gio.h>
55
56
57 #ifdef HAVE_LDAP
58 # ifdef __sun
59 # include <lber.h>
60 # endif /* __sun */
61 # include <ldap.h>
62 # ifdef HAVE_LDAP_SSL_H
63 # include <ldap_ssl.h>
64 # endif /* HAVE_LDAP_SSL_H */
65 #endif /* HAVE_LDAP */
66
67
68 #ifdef HAVE_LDAP
69 LDAP *BrowseLDAPHandle = NULL;
70 /* Handle to LDAP server */
71 char *BrowseLDAPBindDN = NULL,
72 /* LDAP login DN */
73 *BrowseLDAPDN = NULL,
74 /* LDAP search DN */
75 *BrowseLDAPPassword = NULL,
76 /* LDAP login password */
77 *BrowseLDAPServer = NULL,
78 /* LDAP server to use */
79 *BrowseLDAPFilter = NULL;
80 /* LDAP query filter */
81 int BrowseLDAPUpdate = TRUE,
82 /* enables LDAP updates */
83 BrowseLDAPInitialised = FALSE;
84 /* the init stuff has been done */
85 # ifdef HAVE_LDAP_SSL
86 char *BrowseLDAPCACertFile = NULL;
87 /* LDAP CA CERT file to use */
88 # endif /* HAVE_LDAP_SSL */
89 #endif /* HAVE_LDAP */
90
91
92 #ifdef HAVE_LDAP
93 #define LDAP_BROWSE_FILTER "(objectclass=cupsPrinter)"
94 static LDAP *ldap_new_connection(void);
95 static LDAP *ldap_reconnect(void);
96 static void ldap_disconnect(LDAP *ld);
97 static int ldap_search_rec(LDAP *ld, char *base, int scope,
98 char *filter, char *attrs[],
99 int attrsonly, LDAPMessage **res);
100 static int ldap_getval_firststring(LDAP *ld, LDAPMessage *entry,
101 char *attr, char *retval,
102 unsigned long maxsize);
103 static void ldap_freeres(LDAPMessage *entry);
104 # ifdef HAVE_LDAP_REBIND_PROC
105 # if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
106 static int ldap_rebind_proc(LDAP *RebindLDAPHandle,
107 LDAP_CONST char *refsp,
108 ber_tag_t request,
109 ber_int_t msgid,
110 void *params);
111 # else
112 static int ldap_rebind_proc(LDAP *RebindLDAPHandle,
113 char **dnp,
114 char **passwdp,
115 int *authmethodp,
116 int freeit,
117 void *arg);
118 # endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
119 # endif /* HAVE_LDAP_REBIND_PROC */
120 #endif /* HAVE_LDAP */
121
122 #include <cups/cups.h>
123 #include <cups/ppd.h>
124 #include <cups/raster.h>
125 #include <cupsfilters/ipp.h>
126 #include <cupsfilters/ppdgenerator.h>
127
128 #include "cups-notifier.h"
129
130 /* Attribute to mark a CUPS queue as created by us */
131 #define CUPS_BROWSED_MARK "cups-browsed"
132 #define AUTO_OPTION "auto"
133
134 /* Attribute to tell the implicitclass backend the destination queue for
135 the current job */
136 #define CUPS_BROWSED_DEST_PRINTER "cups-browsed-dest-printer"
137
138 /* Timeout values in sec */
139 #define TIMEOUT_IMMEDIATELY -1
140 #define TIMEOUT_CONFIRM 10
141 #define TIMEOUT_RETRY 10
142 #define TIMEOUT_REMOVE -1
143 #define TIMEOUT_CHECK_LIST 2
144
145 #define CUPS_DBUS_NAME "org.cups.cupsd.Notifier"
146 #define CUPS_DBUS_PATH "/org/cups/cupsd/Notifier"
147 #define CUPS_DBUS_INTERFACE "org.cups.cupsd.Notifier"
148
149 #define DEFAULT_CACHEDIR "/var/cache/cups"
150 #define DEFAULT_LOGDIR "/var/log/cups"
151 #define LOCAL_DEFAULT_PRINTER_FILE "/cups-browsed-local-default-printer"
152 #define REMOTE_DEFAULT_PRINTER_FILE "/cups-browsed-remote-default-printer"
153 #define SAVE_OPTIONS_FILE "/cups-browsed-options-%s"
154 #define DEBUG_LOG_FILE "/cups-browsed_log"
155 #define DEBUG_LOG_FILE_2 "/cups-browsed_previous_logs"
156
157 /* Status of remote printer */
158 typedef enum printer_status_e {
159 STATUS_UNCONFIRMED = 0, /* Generated in a previous session */
160 STATUS_CONFIRMED, /* Avahi confirms UNCONFIRMED printer */
161 STATUS_TO_BE_CREATED, /* Scheduled for creation */
162 STATUS_DISAPPEARED, /* Scheduled for removal */
163 STATUS_TO_BE_RELEASED /* Scheduled for release from cups-browsed */
164 } printer_status_t;
165
166 /* Data structure for taking note of each time the remote printer
167 appears as a discovered IPP service */
168 typedef struct ipp_discovery_s {
169 char *interface;
170 char *type;
171 int family;
172 } ipp_discovery_t;
173
174 /* Data structure for remote printers */
175 typedef struct remote_printer_s {
176 char *queue_name;
177 char *location;
178 char *info;
179 char *uri;
180 char *make_model;
181 char *pdl;
182 int color;
183 int duplex;
184 ipp_t *prattrs;
185 char *nickname;
186 int num_options;
187 cups_option_t *options;
188 printer_status_t status;
189 time_t timeout;
190 void *slave_of;
191 int last_printer;
192 char *host;
193 char *ip;
194 int port;
195 char *resource;
196 char *service_name;
197 char *type;
198 char *domain;
199 cups_array_t *ipp_discoveries;
200 int no_autosave;
201 int overwritten;
202 int netprinter;
203 int is_legacy;
204 int timeouted;
205 } remote_printer_t;
206
207 /* Data structure for network interfaces */
208 typedef struct netif_s {
209 char *address;
210 http_addr_t broadcast;
211 } netif_t;
212
213 /* Data structures for browse allow/deny rules */
214 typedef enum browse_order_e {
215 ORDER_ALLOW_DENY,
216 ORDER_DENY_ALLOW
217 } browse_order_t;
218 typedef enum allow_type_e {
219 ALLOW_IP,
220 ALLOW_NET,
221 ALLOW_INVALID
222 } allow_type_t;
223 typedef enum allow_sense_e {
224 ALLOW_ALLOW,
225 ALLOW_DENY
226 } allow_sense_t;
227 typedef struct allow_s {
228 allow_type_t type;
229 allow_sense_t sense;
230 http_addr_t addr;
231 http_addr_t mask;
232 } allow_t;
233
234 /* Data structures for browse filter rules */
235 typedef enum filter_sense_s {
236 FILTER_MATCH,
237 FILTER_NOT_MATCH
238 } filter_sense_t;
239 typedef struct browse_filter_s {
240 filter_sense_t sense;
241 char *field;
242 char *regexp;
243 regex_t *cregexp;
244 } browse_filter_t;
245
246 /* Data structure for a printer discovered using BrowsePoll */
247 typedef struct browsepoll_printer_s {
248 char *uri_supported;
249 char *location;
250 char *info;
251 } browsepoll_printer_t;
252
253 /* Data structure for a BrowsePoll server */
254 typedef struct browsepoll_s {
255 char *server;
256 int port;
257 int major;
258 int minor;
259 gboolean can_subscribe;
260 int subscription_id;
261 int sequence_number;
262
263 /* Remember which printers we discovered. This way we can just ask
264 * if anything has changed, and if not we know these printers are
265 * still there. */
266 GList *printers; /* of browsepoll_printer_t */
267 } browsepoll_t;
268
269 /* Data structure for destination list obtained with cupsEnumDests() */
270 typedef struct dest_list_s {
271 int num_dests;
272 cups_dest_t *dests;
273 } dest_list_t;
274
275 /* Local printer (key is name) */
276 typedef struct local_printer_s {
277 char *device_uri;
278 char *uuid;
279 gboolean cups_browsed_controlled;
280 } local_printer_t;
281
282 /* Browse data to send for local printer */
283 typedef struct browse_data_s {
284 int type;
285 int state;
286 char *uri;
287 char *location;
288 char *info;
289 char *make_model;
290 char *browse_options;
291 } browse_data_t;
292
293 /* Data structure for manual definition of load-balancing clusters */
294 typedef struct cluster_s {
295 char *local_queue_name;
296 cups_array_t *members;
297 } cluster_t;
298
299 /* Ways how to represent the remote printer's IP in the device URI */
300 typedef enum ip_based_uris_e {
301 IP_BASED_URIS_NO,
302 IP_BASED_URIS_ANY,
303 IP_BASED_URIS_IPV4_ONLY,
304 IP_BASED_URIS_IPV6_ONLY
305 } ip_based_uris_t;
306
307 /* Ways how to name local queues for remote printers */
308 typedef enum local_queue_naming_e {
309 LOCAL_QUEUE_NAMING_DNSSD,
310 LOCAL_QUEUE_NAMING_MAKE_MODEL,
311 LOCAL_QUEUE_NAMING_REMOTE_NAME
312 } local_queue_naming_t;
313
314 /* Automatically create queues for IPP network printers: No, only for
315 IPP printers, for all printers */
316 typedef enum create_ipp_printer_queues_e {
317 IPP_PRINTERS_NO,
318 IPP_PRINTERS_LOCAL_ONLY,
319 IPP_PRINTERS_PWGRASTER,
320 IPP_PRINTERS_APPLERASTER,
321 IPP_PRINTERS_PCLM,
322 IPP_PRINTERS_PDF,
323 IPP_PRINTERS_DRIVERLESS,
324 IPP_PRINTERS_ALL
325 } create_ipp_printer_queues_t;
326
327 /* Ways how to set up a queue for an IPP network printer */
328 typedef enum ipp_queue_type_e {
329 PPD_YES,
330 PPD_NO
331 } ipp_queue_type_t;
332
333 /* Ways how we can do load balancing on remote queues with the same name */
334 typedef enum load_balancing_type_e {
335 QUEUE_ON_CLIENT,
336 QUEUE_ON_SERVERS
337 } load_balancing_type_t;
338
339 /* Ways how inactivity for auto-shutdown is defined */
340 typedef enum autoshutdown_inactivity_type_e {
341 NO_QUEUES,
342 NO_JOBS
343 } autoshutdown_inactivity_type_t;
344
345 typedef struct media_size_s{
346 int x;
347 int y;
348 }media_size_t;
349
350 typedef struct pagesize_range_s{
351 int x_dim_min;
352 int x_dim_max;
353 int y_dim_min;
354 int y_dim_max;
355 }pagesize_range_t;
356
357 typedef struct media_col_s{
358 int x,y,top_margin,bottom_margin,left_margin,right_margin;
359 char *media_source,*media_type;
360 }media_col_t;
361
362 typedef struct default_str_attribute_s{
363 char* value;
364 int count;
365 }default_str_attribute_t;
366
367 typedef struct resolution_count_s{
368 res_t *res;
369 int count;
370 }resolution_count_t;
371
372 typedef struct mediacol_count_s{
373 media_col_t *data;
374 int count;
375 }mediacol_count_t;
376
377 typedef struct pagesize_count_s{
378 char* pagesize;
379 int count;
380 }pagesize_count_t;
381
382
383 cups_array_t *remote_printers;
384 static char *alt_config_file = NULL;
385 static cups_array_t *command_line_config;
386 static cups_array_t *netifs;
387 static cups_array_t *local_hostnames;
388 static cups_array_t *browseallow;
389 static gboolean browseallow_all = FALSE;
390 static gboolean browsedeny_all = FALSE;
391 static browse_order_t browse_order;
392 static cups_array_t *browsefilter;
393
394 static GHashTable *local_printers;
395 static GHashTable *cups_supported_remote_printers;
396 static browsepoll_t *local_printers_context = NULL;
397 static http_t *local_conn = NULL;
398 static gboolean inhibit_local_printers_update = FALSE;
399
400 static GList *browse_data = NULL;
401
402 static CupsNotifier *cups_notifier = NULL;
403
404 static GMainLoop *gmainloop = NULL;
405 #ifdef HAVE_AVAHI
406 static AvahiGLibPoll *glib_poll = NULL;
407 static AvahiClient *client = NULL;
408 static AvahiServiceBrowser *sb1 = NULL, *sb2 = NULL;
409 static int avahi_present = 0;
410 #endif /* HAVE_AVAHI */
411 #ifdef HAVE_LDAP
412 static const char * const ldap_attrs[] =/* CUPS LDAP attributes */
413 {
414 "printerDescription",
415 "printerLocation",
416 "printerMakeAndModel",
417 "printerType",
418 "printerURI",
419 NULL
420 };
421 #endif /* HAVE_LDAP */
422 static guint queues_timer_id = 0;
423 static int browsesocket = -1;
424
425 #define BROWSE_DNSSD (1<<0)
426 #define BROWSE_CUPS (1<<1)
427 #define BROWSE_LDAP (1<<2)
428 static unsigned int BrowseLocalProtocols = 0;
429 static unsigned int BrowseRemoteProtocols = BROWSE_DNSSD;
430 static unsigned int BrowseInterval = 60;
431 static unsigned int BrowseTimeout = 300;
432 static uint16_t BrowsePort = 631;
433 static browsepoll_t **BrowsePoll = NULL;
434 static unsigned int NewBrowsePollQueuesShared = 0;
435 static unsigned int AllowResharingRemoteCUPSPrinters = 0;
436 static unsigned int DebugLogFileSize = 300;
437 static size_t NumBrowsePoll = 0;
438 static guint update_netifs_sourceid = 0;
439 static char local_server_str[1024];
440 static char *DomainSocket = NULL;
441 static unsigned int HttpLocalTimeout = 5;
442 static unsigned int HttpRemoteTimeout = 10;
443 static unsigned int HttpMaxRetries = 5;
444 static unsigned int DNSSDBasedDeviceURIs = 1;
445 static ip_based_uris_t IPBasedDeviceURIs = IP_BASED_URIS_NO;
446 #ifdef NAMING_MAKE_MODEL
447 static local_queue_naming_t LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_MAKE_MODEL;
448 #else
449 # ifdef NAMING_REMOTE_NAME
450 static local_queue_naming_t LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_REMOTE_NAME;
451 # else
452 static local_queue_naming_t LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_DNSSD;
453 # endif
454 #endif
455 static local_queue_naming_t LocalQueueNamingIPPPrinter=LOCAL_QUEUE_NAMING_DNSSD;
456 static unsigned int OnlyUnsupportedByCUPS = 0;
457 static unsigned int UseCUPSGeneratedPPDs = 0;
458 static unsigned int CreateRemoteRawPrinterQueues = 0;
459 static unsigned int CreateRemoteCUPSPrinterQueues = 1;
460 #ifdef ONLY_LOCAL_IPP_PRINTERS_AUTO_SETUP
461 static create_ipp_printer_queues_t CreateIPPPrinterQueues = IPP_PRINTERS_LOCAL_ONLY;
462 #else
463 #ifdef ONLY_DRIVERLESS_IPP_PRINTERS_AUTO_SETUP
464 static create_ipp_printer_queues_t CreateIPPPrinterQueues = IPP_PRINTERS_DRIVERLESS;
465 #else
466 static create_ipp_printer_queues_t CreateIPPPrinterQueues = IPP_PRINTERS_ALL;
467 #endif
468 #endif
469 #ifdef SAVING_CREATED_QUEUES
470 static unsigned int KeepGeneratedQueuesOnShutdown = 1;
471 #else
472 static unsigned int KeepGeneratedQueuesOnShutdown = 0;
473 #endif
474 static ipp_queue_type_t IPPPrinterQueueType = PPD_YES;
475 static int NewIPPPrinterQueuesShared = 0;
476 static int AutoClustering = 1;
477 static cups_array_t *clusters;
478 static load_balancing_type_t LoadBalancingType = QUEUE_ON_CLIENT;
479 static char *DefaultOptions = NULL;
480 static int update_cups_queues_max_per_call = 10;
481 static int pause_between_cups_queue_updates = 1;
482 static remote_printer_t *deleted_master = NULL;
483 static int terminating = 0; /* received SIGTERM, ignore callbacks,
484 break loops */
485 static int in_shutdown = 0;
486 static int autoshutdown = 0;
487 static int autoshutdown_avahi = 0;
488 static int autoshutdown_timeout = 30;
489 static autoshutdown_inactivity_type_t autoshutdown_on = NO_QUEUES;
490 static guint autoshutdown_exec_id = 0;
491 static const char *default_printer = NULL;
492 static unsigned int notify_lease_duration = 86400;
493
494 static int debug_stderr = 0;
495 static int debug_logfile = 0;
496 static FILE *lfp = NULL;
497
498 static char cachedir[1024];
499 static char logdir[1024];
500 static char local_default_printer_file[2048];
501 static char remote_default_printer_file[2048];
502 static char save_options_file[2048];
503 static char debug_log_file[2048];
504 static char debug_log_file_bckp[2048];
505
506 /*Contains ppd keywords which are written by ppdgenerator.c in the ppd file.*/
507 static char* ppd_keywords[] =
508 {
509 "PageSize",
510 "PageRegion",
511 "InputSlot",
512 "MediaType",
513 "ColorModel",
514 "Duplex",
515 "OutputBin",
516 "StapleLocation",
517 "FoldType",
518 "PunchMedia",
519 "Booklet",
520 "cupsFinishingTemplate",
521 "cupsPrintQuality",
522 "print-content-optimize",
523 "print-rendering-intent",
524 "print-scaling",
525 };
526
527 /* Static global variable for indicating we have reached the HTTP timeout */
528 static int timeout_reached = 0;
529
530 static void recheck_timer (void);
531 static void browse_poll_create_subscription (browsepoll_t *context,
532 http_t *conn);
533 static gboolean browse_poll_get_notifications (browsepoll_t *context,
534 http_t *conn);
535 static remote_printer_t
536 *examine_discovered_printer_record(const char *host,
537 const char *ip,
538 uint16_t port,
539 char *resource,
540 const char *service_name,
541 const char *location,
542 const char *info,
543 const char *type,
544 const char *domain,
545 const char *interface,
546 int family,
547 void *txt);
548
549 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5)
550 #define HAVE_CUPS_1_6 1
551 #endif
552
553 /*
554 * Option 'printer-is-shared' cannot be set on remote CUPS
555 * queue and requests for setting it ends with error since
556 * 2.1.1. Define HAVE_CUPS_2_2 to do not send IPP request
557 * for setting 'printer-is-shared' option on remote CUPS queues
558 * for newer versions of CUPS.
559 */
560 #if (CUPS_VERSION_MAJOR > 2) || (CUPS_VERSION_MINOR > 1)
561 #define HAVE_CUPS_2_2 1
562 #else
563 #define HAVE_CUPS_2_2 0
564 #endif
565
566 /*
567 * CUPS 1.6 makes various structures private and
568 * introduces these ippGet and ippSet functions
569 * for all of the fields in these structures.
570 * http://www.cups.org/str.php?L3928
571 * We define (same signatures) our own accessors when CUPS < 1.6.
572 */
573 #ifndef HAVE_CUPS_1_6
574 const char *
ippGetName(ipp_attribute_t * attr)575 ippGetName(ipp_attribute_t *attr)
576 {
577 return (attr->name);
578 }
579
580 ipp_op_t
ippGetOperation(ipp_t * ipp)581 ippGetOperation(ipp_t *ipp)
582 {
583 return (ipp->request.op.operation_id);
584 }
585
586 ipp_status_t
ippGetStatusCode(ipp_t * ipp)587 ippGetStatusCode(ipp_t *ipp)
588 {
589 return (ipp->request.status.status_code);
590 }
591
592 ipp_tag_t
ippGetGroupTag(ipp_attribute_t * attr)593 ippGetGroupTag(ipp_attribute_t *attr)
594 {
595 return (attr->group_tag);
596 }
597
598 ipp_tag_t
ippGetValueTag(ipp_attribute_t * attr)599 ippGetValueTag(ipp_attribute_t *attr)
600 {
601 return (attr->value_tag);
602 }
603
604 int
ippGetCount(ipp_attribute_t * attr)605 ippGetCount(ipp_attribute_t *attr)
606 {
607 return (attr->num_values);
608 }
609
610 int
ippGetInteger(ipp_attribute_t * attr,int element)611 ippGetInteger(ipp_attribute_t *attr,
612 int element)
613 {
614 return (attr->values[element].integer);
615 }
616
617 int
ippGetBoolean(ipp_attribute_t * attr,int element)618 ippGetBoolean(ipp_attribute_t *attr,
619 int element)
620 {
621 return (attr->values[element].boolean);
622 }
623
624 const char *
ippGetString(ipp_attribute_t * attr,int element,const char ** language)625 ippGetString(ipp_attribute_t *attr,
626 int element,
627 const char **language)
628 {
629 return (attr->values[element].string.text);
630 }
631
632 ipp_attribute_t *
ippFirstAttribute(ipp_t * ipp)633 ippFirstAttribute(ipp_t *ipp)
634 {
635 if (!ipp)
636 return (NULL);
637 return (ipp->current = ipp->attrs);
638 }
639
640 ipp_attribute_t *
ippNextAttribute(ipp_t * ipp)641 ippNextAttribute(ipp_t *ipp)
642 {
643 if (!ipp || !ipp->current)
644 return (NULL);
645 return (ipp->current = ipp->current->next);
646 }
647
648 int
ippSetVersion(ipp_t * ipp,int major,int minor)649 ippSetVersion(ipp_t *ipp, int major, int minor)
650 {
651 if (!ipp || major < 0 || minor < 0)
652 return (0);
653 ipp->request.any.version[0] = major;
654 ipp->request.any.version[1] = minor;
655 return (1);
656 }
657 #endif
658
659
660 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 6)
661 #define HAVE_CUPS_1_7 1
662 #endif
663
664 /*
665 * The httpAddrPort() function was only introduced in CUPS 1.7.x
666 */
667 #ifndef HAVE_CUPS_1_7
668 int /* O - Port number */
httpAddrPort(http_addr_t * addr)669 httpAddrPort(http_addr_t *addr) /* I - Address */
670 {
671 if (!addr)
672 return (-1);
673 #ifdef AF_INET6
674 else if (addr->addr.sa_family == AF_INET6)
675 return (ntohs(addr->ipv6.sin6_port));
676 #endif /* AF_INET6 */
677 else if (addr->addr.sa_family == AF_INET)
678 return (ntohs(addr->ipv4.sin_port));
679 else
680 return (0);
681 }
682 #endif
683
684
685 #if (CUPS_VERSION_MAJOR > 1)
686 #define HAVE_CUPS_2_0 1
687 #endif
688
689
690 void
start_debug_logging()691 start_debug_logging()
692 {
693 if (debug_log_file[0] == '\0')
694 return;
695 if (lfp == NULL)
696 lfp = fopen(debug_log_file, "a+");
697 if (lfp == NULL) {
698 fprintf(stderr, "cups-browsed: ERROR: Failed creating debug log file %s\n",
699 debug_log_file);
700 exit(1);
701 }
702 }
703
704 void
stop_debug_logging()705 stop_debug_logging()
706 {
707 debug_logfile = 0;
708 if (lfp)
709 fclose(lfp);
710 lfp = NULL;
711 }
712
713 // returns the size of debug log file
findLogFileSize()714 long int findLogFileSize()
715 {
716 FILE* fp = fopen(debug_log_file, "r");
717 if (fp == NULL) {
718 return -1;
719 }
720 fseek(fp, 0L, SEEK_END);
721 long int res = ftell(fp);
722 fclose(fp);
723 return res;
724 }
725
copyToFile(FILE ** fp1,FILE ** fp2)726 void copyToFile(FILE **fp1, FILE **fp2){
727 int buffer_size = 2048;
728 char *buf = (char*) malloc(sizeof(char)*buffer_size);
729 if(!buf){
730 fprintf(stderr,"Error creating buffer for debug logging\n");
731 return;
732 }
733 fseek(*fp1, 0, SEEK_SET);
734 size_t r;
735 do {
736 r = fread(buf, sizeof(char), buffer_size, *fp1);
737 fwrite(buf, sizeof(char), r, *fp2);
738 } while(r==buffer_size);
739
740 }
741
742 void
debug_printf(const char * format,...)743 debug_printf(const char *format, ...) {
744 if (debug_stderr || debug_logfile) {
745 time_t curtime = time(NULL);
746 char buf[64];
747 ctime_r(&curtime, buf);
748 while(isspace(buf[strlen(buf)-1])) buf[strlen(buf)-1] = '\0';
749 va_list arglist;
750 if (debug_stderr) {
751 va_start(arglist, format);
752 fprintf(stderr, "%s ", buf);
753 vfprintf(stderr, format, arglist);
754 fflush(stderr);
755 va_end(arglist);
756 }
757 if (debug_logfile && lfp) {
758 va_start(arglist, format);
759 fprintf(lfp, "%s ", buf);
760 vfprintf(lfp, format, arglist);
761 fflush(lfp);
762 va_end(arglist);
763 }
764
765 long int log_file_size = findLogFileSize();
766 if(DebugLogFileSize>0 && log_file_size>(long int)DebugLogFileSize*1024){
767 fclose(lfp);
768 FILE *fp1 = fopen(debug_log_file, "r");
769 FILE *fp2 = fopen(debug_log_file_bckp, "w");
770 copyToFile(&fp1,&fp2);
771 fclose(fp1);
772 fclose(fp2);
773 lfp = fopen(debug_log_file, "w");
774 }
775 }
776 }
777
778 void
debug_log_out(char * log)779 debug_log_out(char *log) {
780 if (debug_stderr || debug_logfile) {
781 time_t curtime = time(NULL);
782 char buf[64];
783 char *ptr1, *ptr2;
784 ctime_r(&curtime, buf);
785 while(isspace(buf[strlen(buf)-1])) buf[strlen(buf)-1] = '\0';
786 ptr1 = log;
787 while(ptr1) {
788 ptr2 = strchr(ptr1, '\n');
789 if (ptr2) *ptr2 = '\0';
790 if (debug_stderr)
791 fprintf(stderr, "%s %s\n", buf, ptr1);
792 if (debug_logfile && lfp)
793 fprintf(lfp, "%s %s\n", buf, ptr1);
794 if (ptr2) *ptr2 = '\n';
795 ptr1 = ptr2 ? (ptr2 + 1) : NULL;
796 }
797 }
798 }
799
800
801 /*
802 * 'create_media_size()' - Create a media-size value.
803 */
804
805 static ipp_t * /* O - media-col collection */
create_media_size(int width,int length)806 create_media_size(int width, /* I - x-dimension in 2540ths */
807 int length) /* I - y-dimension in 2540ths */
808 {
809 ipp_t *media_size = ippNew(); /* media-size value */
810
811 ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "x-dimension",
812 width);
813 ippAddInteger(media_size, IPP_TAG_PRINTER, IPP_TAG_INTEGER, "y-dimension",
814 length);
815
816 return (media_size);
817 }
818
819 /*
820 * 'create_media_range()' - Create a pagesize-range value.
821 */
822
823 static ipp_t *
create_media_range(int x_dim_min_width,int x_dim_max_width,int y_dim_min_height,int y_dim_max_height)824 create_media_range(int x_dim_min_width,
825 int x_dim_max_width,
826 int y_dim_min_height,
827 int y_dim_max_height)
828 {
829 ipp_t *media_size = ippNew();
830 ippAddRange(media_size, IPP_TAG_PRINTER, "x-dimension",
831 x_dim_min_width, x_dim_max_width);
832 ippAddRange(media_size, IPP_TAG_PRINTER, "y-dimension",
833 y_dim_min_height, y_dim_max_height);
834 return (media_size);
835 }
836
837 void *
copy_media_size(void * size,void * user_data)838 copy_media_size(void *size, void *user_data)
839 {
840 media_size_t *data = (media_size_t *)size;
841 media_size_t *copy;
842
843 copy = (media_size_t *)calloc(1, sizeof(media_size_t));
844 if (copy) {
845 copy->x = data->x;
846 copy->y = data->y;
847 }
848 return copy;
849 }
850
851 void*
copy_range_size(void * range,void * user_data)852 copy_range_size(void *range, void* user_data)
853 {
854 pagesize_range_t *data = (pagesize_range_t *)range;
855 pagesize_range_t *copy;
856
857 copy = (pagesize_range_t *)calloc(1,sizeof(pagesize_range_t));
858 if (copy) {
859 copy->x_dim_min = data->x_dim_min;
860 copy->x_dim_max = data->x_dim_max;
861 copy->y_dim_min = data->y_dim_min;
862 copy->y_dim_max = data->y_dim_max;
863 }
864 return copy;
865 }
866
867 void *
copy_media(void * media,void * user_data)868 copy_media(void *media, void *user_data)
869 {
870 media_col_t *data = (media_col_t *)media;
871 media_col_t *copy;
872
873 copy = (media_col_t *)calloc(1, sizeof(media_col_t));
874 if (copy) {
875 copy->x = data->x;
876 copy->y = data->y;
877 copy->left_margin=data->left_margin;
878 copy->right_margin=data->right_margin;
879 copy->top_margin=data->top_margin;
880 copy->bottom_margin=data->bottom_margin;
881 copy->media_source = NULL;
882 copy->media_type = NULL;
883 if (data->media_source != NULL) {
884 copy->media_source = (char *)malloc(sizeof(char)*32);
885 strcpy(copy->media_source,data->media_source);
886 }
887 if (data->media_type != NULL) {
888 copy->media_type = (char *)malloc(sizeof(char)*32);;
889 strcpy(copy->media_type,data->media_type);
890 }
891 }
892 return copy;
893 }
894
copy_media_count(void * media,void * user_data)895 void* copy_media_count(void *media, void* user_data)
896 {
897 mediacol_count_t *prev = (mediacol_count_t *)media;
898 mediacol_count_t *copy;
899
900 copy = (mediacol_count_t*)calloc(1,sizeof(mediacol_count_t));
901 if (copy) {
902 copy->data = copy_media(prev->data,NULL);
903 copy->count = prev->count;
904 }
905 return copy;
906 }
907
copy_pagesize_count(void * pagesize_count,void * user_data)908 void* copy_pagesize_count(void *pagesize_count, void* user_data)
909 {
910 pagesize_count_t *prev = (pagesize_count_t *)pagesize_count;
911 pagesize_count_t *copy;
912
913 copy = (pagesize_count_t*)calloc(1,sizeof(pagesize_count_t));
914 copy->pagesize = malloc(sizeof(char)*32);
915 if (copy) {
916 strcpy(copy->pagesize,prev->pagesize);
917 copy->count = prev->count;
918 }
919 return copy;
920 }
921
compare_pagesize_count(void * pagesize_a,void * pagesize_b,void * user_data)922 int compare_pagesize_count(void* pagesize_a, void* pagesize_b,void* user_data)
923 {
924 pagesize_count_t *a = (pagesize_count_t *) pagesize_a;
925 pagesize_count_t *b = (pagesize_count_t *) pagesize_b;
926
927 if (!strcmp(a->pagesize,b->pagesize))
928 return 0;
929 return 1;
930 }
931
932 /*
933 * 'create_media_col()' - Create a media-col value.
934 */
935
936 static ipp_t *
create_media_col(int width,int length,int left_margin,int right_margin,int top_margin,int bottom_margin,char * media_source,char * media_type)937 create_media_col(int width,
938 int length,
939 int left_margin,
940 int right_margin,
941 int top_margin,
942 int bottom_margin,
943 char *media_source,
944 char *media_type)
945 {
946 ipp_t *media_col = ippNew(), /* media-col value */
947 *media_size = create_media_size(width, length);
948
949 ippAddCollection(media_col, IPP_TAG_PRINTER, "media-size", media_size);
950 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
951 "media-bottom-margin",bottom_margin);
952 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
953 "media-left-margin", left_margin);
954 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
955 "media-right-margin",right_margin);
956 ippAddInteger(media_col, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
957 "media-top-margin", top_margin);
958 if (media_source != NULL)
959 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
960 "media-source", NULL,media_source);
961 if (media_type != NULL)
962 ippAddString(media_col, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
963 "media-type", NULL,media_type);
964 ippDelete(media_size);
965
966 return (media_col);
967 }
968
969 int
compare_mediasize(void * media_a,void * media_b,void * user_data)970 compare_mediasize(void *media_a, void *media_b,
971 void *user_data)
972 {
973 media_size_t *a=(media_size_t *)media_a;
974 media_size_t *b=(media_size_t *)media_b;
975
976 if (a->x < b->x)
977 return -1;
978 else if (a->x > b->x)
979 return 1;
980 else{
981 if (a->y == b->y)
982 return 0;
983 else if (a->y < b->y)
984 return -1;
985 return 1;
986 }
987 }
988
compare_int(int a,int b)989 int compare_int(int a, int b)
990 {
991 if (a < b)
992 return -1;
993 else if (a > b)
994 return 1;
995 return 0;
996 }
997
compare_rangesize(void * range_a,void * range_b,void * user_data)998 int compare_rangesize(void *range_a,void *range_b,
999 void *user_data)
1000 {
1001 pagesize_range_t *a = (pagesize_range_t *)range_a;
1002 pagesize_range_t *b = (pagesize_range_t *)range_b;
1003 int value;
1004
1005 if ((value = compare_int(a->x_dim_min, b->x_dim_min)) == 0) {
1006 if ((value = compare_int(a->x_dim_max, b->x_dim_max)) == 0) {
1007 if ((value = compare_int(a->y_dim_min, b->y_dim_min)) == 0) {
1008 if ((value = compare_int(a->y_dim_max, b->y_dim_max)) == 0) {
1009 return 0;
1010 }
1011 }
1012 }
1013 }
1014 return value;
1015 }
1016
compare_media(void * media_a,void * media_b,void * user_data)1017 int compare_media(void *media_a, void *media_b,
1018 void *user_data)
1019 {
1020 media_col_t *a=(media_col_t *)media_a;
1021 media_col_t *b=(media_col_t *)media_b;
1022 int value;
1023
1024 if ((value = compare_int(a->x, b->x)) == 0) {
1025 if ((value = compare_int(a->y, b->y)) == 0) {
1026 if ((value = compare_int(a->top_margin, b->top_margin)) == 0) {
1027 if ((value = compare_int(a->bottom_margin, b->bottom_margin)) == 0) {
1028 if ((value = compare_int(a->right_margin, b->right_margin)) == 0) {
1029 if ((value = compare_int(a->left_margin, b->left_margin)) == 0) {
1030 if (a->media_source == NULL && b->media_source == NULL) {
1031 if (a->media_type == NULL && b->media_type == NULL)
1032 return 0;
1033 if (a->media_type == NULL)
1034 return -1;
1035 if (b->media_type == NULL)
1036 return 1;
1037 return strcmp(a->media_type, b->media_type);
1038 }
1039 if (a->media_source == NULL)
1040 return -1;
1041 if (b->media_source == NULL)
1042 return 1;
1043 if (!strcmp(a->media_source, b->media_source)) {
1044 if (a->media_type==NULL && b->media_type == NULL)
1045 return 0;
1046 if (a->media_type==NULL)
1047 return -1;
1048 if (b->media_type==NULL)
1049 return 1;
1050 return strcmp(a->media_type, b->media_type);
1051 }
1052 else
1053 return strcmp(a->media_source, b->media_source);
1054 }
1055 }
1056 }
1057 }
1058 }
1059 }
1060 return value;
1061 }
1062
compare_media_count(void * media_a,void * media_b,void * user_data)1063 int compare_media_count(void* media_a, void* media_b,void* user_data)
1064 {
1065 mediacol_count_t *a = (mediacol_count_t*) media_a;
1066 mediacol_count_t *b = (mediacol_count_t*) media_b;
1067
1068 return (compare_media(a->data, b->data, NULL));
1069 }
1070
1071 void *
copy_default_str(void * data,void * user_data)1072 copy_default_str(void *data, void *user_data)
1073 {
1074 default_str_attribute_t *prev = (default_str_attribute_t *)data;
1075 default_str_attribute_t *copy;
1076
1077 copy = (default_str_attribute_t *)calloc(1, sizeof(default_str_attribute_t));
1078 if (copy) {
1079 copy->value = (char *)malloc(sizeof(char)*100);
1080 copy->value = strdup(prev->value);
1081 copy->count = prev->count;
1082 }
1083 return copy;
1084 }
1085
strstrip(char * s)1086 char *strstrip(char *s)
1087 {
1088 size_t size;
1089 char *end;
1090
1091 size = strlen(s);
1092
1093 if (!size)
1094 return s;
1095
1096 end = s + size - 1;
1097 while (end >= s && isspace(*end))
1098 end--;
1099 *(end + 1) = '\0';
1100
1101 while (*s && isspace(*s))
1102 s++;
1103
1104 return s;
1105 }
1106
1107 int
compare_default_str(void * defstr_a,void * defstr_b,void * user_data)1108 compare_default_str(void *defstr_a, void *defstr_b,
1109 void *user_data)
1110 {
1111 default_str_attribute_t *a=(default_str_attribute_t *)defstr_a;
1112 default_str_attribute_t *b=(default_str_attribute_t *)defstr_b;
1113
1114 return strcmp(a->value, b->value);
1115 }
1116
1117 void *
copy_counted_res(void * data,void * user_data)1118 copy_counted_res(void *data, void *user_data)
1119 {
1120 resolution_count_t *prev = (resolution_count_t *)data;
1121 resolution_count_t *copy;
1122
1123 copy = (resolution_count_t *)calloc(1, sizeof(resolution_count_t));
1124 if (copy) {
1125 copy->res = (res_t *)malloc(sizeof(res_t));
1126 copy->res->x = prev->res->x;
1127 copy->res->y = prev->res->y;
1128 copy->count = prev->count;
1129 }
1130 return copy;
1131 }
1132
1133
1134 int
compare_counted_res(void * defres_a,void * defres_b,void * user_data)1135 compare_counted_res(void *defres_a, void *defres_b,
1136 void *user_data)
1137 {
1138 resolution_count_t *a=(resolution_count_t *)defres_a;
1139 resolution_count_t *b=(resolution_count_t *)defres_b;
1140
1141 return compare_resolutions(a->res, b->res, NULL);
1142 }
1143
1144 /*
1145 * 'pwg_compare_sizes()' - Compare two media sizes...
1146 */
1147
1148 static int /* O - Result of comparison */
pwg_compare_sizes(cups_size_t * a,cups_size_t * b)1149 pwg_compare_sizes(cups_size_t *a, /* I - First media size */
1150 cups_size_t *b) /* I - Second media size */
1151 {
1152 return (strcmp(a->media, b->media));
1153 }
1154
1155
1156 /*
1157 * 'pwg_copy_size()' - Copy a media size.
1158 */
1159
1160 static cups_size_t * /* O - New media size */
pwg_copy_size(cups_size_t * size)1161 pwg_copy_size(cups_size_t *size) /* I - Media size to copy */
1162 {
1163 cups_size_t *newsize = (cups_size_t *)calloc(1, sizeof(cups_size_t));
1164 /* New media size */
1165
1166 if (newsize)
1167 memcpy(newsize, size, sizeof(cups_size_t));
1168
1169 return (newsize);
1170 }
1171
1172 /* Function returns number of jobs queued on printer*/
1173 int /* O - Number of jobs */
get_number_of_jobs(http_t * http,const char * uri,int myjobs,int whichjobs)1174 get_number_of_jobs(http_t *http, /* I - Connection to server */
1175 const char *uri, /* I - uri of printer */
1176 int myjobs, /* I - 0 = all users, 1 = mine */
1177 int whichjobs) /* I - CUPS_WHICHJOBS_ALL,
1178 CUPS_WHICHJOBS_ACTIVE, or
1179 CUPS_WHICHJOBS_COMPLETED */
1180 {
1181 int n; /* Number of jobs */
1182 ipp_t *request, /* IPP Request */
1183 *response; /* IPP Response */
1184 ipp_attribute_t *attr; /* Current attribute */
1185 int id; /* job-id */
1186 static const char * const attrs[] = /* Requested attributes */
1187 {
1188 "job-id"
1189 };
1190
1191 httpReconnect2(http, 30000, NULL);
1192
1193 /*
1194 * Build an IPP_GET_JOBS request, which requires the following
1195 * attributes:
1196 *
1197 * attributes-charset
1198 * attributes-natural-language
1199 * printer-uri
1200 * requesting-user-name
1201 * which-jobs
1202 * my-jobs
1203 * requested-attributes
1204 */
1205
1206
1207 /* Generating IPP Request */
1208 request = ippNewRequest(IPP_OP_GET_JOBS);
1209 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1210 "printer-uri", NULL, uri);
1211 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1212 "requesting-user-name", NULL, cupsUser());
1213 if (myjobs)
1214 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
1215 if (whichjobs == CUPS_WHICHJOBS_COMPLETED)
1216 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1217 "which-jobs", NULL, "completed");
1218 else if (whichjobs == CUPS_WHICHJOBS_ALL)
1219 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1220 "which-jobs", NULL, "all");
1221 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1222 "requested-attributes", sizeof(attrs) / sizeof(attrs[0]),
1223 NULL, attrs);
1224
1225 /* Do the request and get back a response... */
1226 n = 0;
1227 if ((response = cupsDoRequest(http, request, "/")) != NULL) {
1228 for (attr = ippFirstAttribute(response); attr;
1229 attr = ippNextAttribute(response)){
1230 /* Skip leading attributes until we hit a job... */
1231 while (attr && ippGetGroupTag(attr) != IPP_TAG_JOB)
1232 attr = ippNextAttribute(response);
1233
1234 if (!attr)
1235 break;
1236 /* Pull the needed attributes from this job */
1237 id = 0;
1238 while (attr && ippGetGroupTag(attr) == IPP_TAG_JOB) {
1239 if (!strcmp(ippGetName(attr), "job-id") &&
1240 ippGetValueTag(attr) == IPP_TAG_INTEGER)
1241 id = ippGetInteger(attr,0);
1242 attr = ippNextAttribute(response);
1243 }
1244
1245 /* See if we have everything needed */
1246 if (!id){
1247 if (!attr)
1248 break;
1249 else
1250 continue;
1251 }
1252 /* Incrementing number of jobs*/
1253 n ++;
1254 if (!attr)
1255 break;
1256 }
1257
1258 ippDelete(response);
1259 }
1260
1261 if (n == 0)
1262 return (-1);
1263 else
1264 return (n);
1265 }
1266
1267 static const char *
password_callback(const char * prompt,http_t * http,const char * method,const char * resource,void * user_data)1268 password_callback (const char *prompt,
1269 http_t *http,
1270 const char *method,
1271 const char *resource,
1272 void *user_data)
1273 {
1274 return NULL;
1275 }
1276
1277 http_t *
httpConnectEncryptShortTimeout(const char * host,int port,http_encryption_t encryption)1278 httpConnectEncryptShortTimeout(const char *host, int port,
1279 http_encryption_t encryption)
1280 {
1281 return (httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 3000,
1282 NULL));
1283 }
1284
1285 int
http_timeout_cb(http_t * http,void * user_data)1286 http_timeout_cb(http_t *http, void *user_data)
1287 {
1288 debug_printf("HTTP timeout! (consider increasing HttpLocalTimeout/HttpRemoteTimeout value)\n");
1289 timeout_reached = 1;
1290 return 0;
1291 }
1292
1293 static http_t *
http_connect_local(void)1294 http_connect_local (void)
1295 {
1296 const char *server = cupsServer();
1297 int port = ippPort();
1298
1299 if (!local_conn) {
1300 if (server[0] == '/')
1301 debug_printf("cups-browsed: Creating http connection to local CUPS daemon via domain socket: %s\n",
1302 server);
1303 else
1304 debug_printf("cups-browsed: Creating http connection to local CUPS daemon: %s:%d\n",
1305 server, port);
1306 local_conn = httpConnectEncryptShortTimeout(server, port,
1307 cupsEncryption());
1308 }
1309 if (local_conn)
1310 httpSetTimeout(local_conn, HttpLocalTimeout, http_timeout_cb, NULL);
1311 else {
1312 if (server[0] == '/')
1313 debug_printf("cups-browsed: Failed creating http connection to local CUPS daemon via domain socket: %s\n",
1314 server);
1315 else
1316 debug_printf("cups-browsed: Failed creating http connection to local CUPS daemon: %s:%d\n",
1317 server, port);
1318 }
1319
1320 return local_conn;
1321 }
1322
1323 static void
http_close_local(void)1324 http_close_local (void)
1325 {
1326 if (local_conn) {
1327 httpClose (local_conn);
1328 local_conn = NULL;
1329 }
1330 }
1331
1332 int /* O - 1 on match, 0 otherwise */
_cups_isalpha(int ch)1333 _cups_isalpha(int ch) /* I - Character to test */
1334 {
1335 return ((ch >= 'A' && ch <= 'Z') ||
1336 (ch >= 'a' && ch <= 'z'));
1337 }
1338
1339 int /* O - 1 on match, 0 otherwise */
_cups_islower(int ch)1340 _cups_islower(int ch) /* I - Character to test */
1341 {
1342 return (ch >= 'a' && ch <= 'z');
1343 }
1344
1345 int /* O - Converted character */
_cups_toupper(int ch)1346 _cups_toupper(int ch) /* I - Character to convert */
1347 {
1348 return (_cups_islower(ch) ? ch - 'a' + 'A' : ch);
1349 }
1350
1351 static void
pwg_ppdize_name(const char * ipp,char * name,size_t namesize)1352 pwg_ppdize_name(const char *ipp, /* I - IPP keyword */
1353 char *name, /* I - Name buffer */
1354 size_t namesize) /* I - Size of name buffer */
1355 {
1356 char *ptr, /* Pointer into name buffer */
1357 *end; /* End of name buffer */
1358
1359 *name = (char)toupper(*ipp++);
1360
1361 for (ptr = name + 1, end = name + namesize - 1; *ipp && ptr < end;)
1362 {
1363 if (*ipp == '-' && _cups_isalpha(ipp[1]))
1364 {
1365 ipp ++;
1366 *ptr++ = (char)toupper(*ipp++ & 255);
1367 }
1368 else
1369 *ptr++ = *ipp++;
1370 }
1371
1372 *ptr = '\0';
1373 }
1374
add_mimetype_attributes(char * cluster_name,ipp_t ** merged_attributes)1375 void add_mimetype_attributes(char* cluster_name, ipp_t **merged_attributes)
1376 {
1377 int count, i;
1378 remote_printer_t *p;
1379 const char *str;
1380 char *q;
1381 cups_array_t *list;
1382 ipp_attribute_t *attr;
1383 int num_value, attr_no;
1384 char* attributes[] = {
1385 "document-format-supported"
1386 };
1387
1388 for (attr_no = 0; attr_no < 1; attr_no++) {
1389 if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1390 (cups_acopy_func_t)strdup,
1391 (cups_afree_func_t)free)) == NULL)
1392 return ;
1393
1394 num_value = 0;
1395 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1396 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1397 if (strcmp(cluster_name,p->queue_name))
1398 continue;
1399 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1400 p->status == STATUS_TO_BE_RELEASED )
1401 continue;
1402 if ((attr = ippFindAttribute(p->prattrs,attributes[attr_no],
1403 IPP_TAG_MIMETYPE)) != NULL) {
1404 count = ippGetCount(attr);
1405 for (i = 0; i < count; i ++) {
1406 str = ippGetString(attr, i, NULL);
1407 if (!cupsArrayFind(list, (void *)str)){
1408 cupsArrayAdd(list, (void *)str);
1409 num_value++;
1410 }
1411 }
1412 }
1413 }
1414 if (num_value != 0) {
1415 char *values[num_value];
1416 for (q = (char *)cupsArrayFirst(list),i=0;
1417 q;
1418 q = (char *)cupsArrayNext(list),i++) {
1419 values[i]=malloc(sizeof(char) * (strlen(q) + 1));
1420 snprintf(values[i], strlen(q) + 1, "%s", q);
1421 }
1422 ippAddStrings(*merged_attributes, IPP_TAG_PRINTER,IPP_TAG_MIMETYPE,
1423 attributes[attr_no], num_value, NULL,
1424 (const char * const *)values);
1425
1426 for (int k = 0; k < i; k++) {
1427 free(values[k]);
1428 values[k] = NULL;
1429 }
1430 }
1431 cupsArrayDelete(list);
1432 list = NULL;
1433 }
1434 }
1435
1436 /*add_tagzero_attributes - Adds attribute to the merged_attribute variable for
1437 the cluster. This function adds attribute with value
1438 tag IPP_TAG_ZERO */
add_tagzero_attributes(char * cluster_name,ipp_t ** merged_attributes)1439 void add_tagzero_attributes(char* cluster_name, ipp_t **merged_attributes)
1440 {
1441 int count, i;
1442 remote_printer_t *p;
1443 const char *str;
1444 char *q;
1445 cups_array_t *list;
1446 ipp_attribute_t *attr;
1447 int num_value, attr_no;
1448 char* attributes[] = {
1449 "media-supported",
1450 "output-bin-supported",
1451 "print-content-optimize-supported",
1452 "print-rendering-intent-supported",
1453 "print-scaling-supported"
1454 };
1455
1456 for (attr_no = 0; attr_no < 5; attr_no++) {
1457 /* Cups Array to store the values for the attribute*/
1458 if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1459 (cups_acopy_func_t)strdup,
1460 (cups_afree_func_t)free)) == NULL)
1461 return ;
1462
1463 num_value = 0;
1464 /* Iterating over all the printers in the cluster*/
1465 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1466 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1467 if (strcmp(cluster_name, p->queue_name))
1468 continue;
1469 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1470 p->status == STATUS_TO_BE_RELEASED )
1471 continue;
1472 if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no],
1473 IPP_TAG_ZERO)) != NULL) {
1474 count = ippGetCount(attr);
1475 for(i = 0; i < count; i ++) {
1476 /* Pick next format from attribute */
1477 str = ippGetString(attr, i, NULL);
1478 /* Add format to list, skip duplicates */
1479 if (!cupsArrayFind(list, (void *)str)){
1480 cupsArrayAdd(list, (void *)str);
1481 num_value ++;
1482 }
1483 }
1484 }
1485 }
1486 if (num_value != 0) {
1487 char *values[num_value];
1488 /* Transferring attributes value from cups Array to char* array*/
1489 for (q = (char *)cupsArrayFirst(list), i = 0; q;
1490 q = (char *)cupsArrayNext(list), i ++) {
1491 values[i] = malloc(sizeof(char) * (strlen(q) + 1));
1492 snprintf(values[i], strlen(q) + 1, "%s", q);
1493 }
1494 ippAddStrings(*merged_attributes, IPP_TAG_PRINTER,
1495 IPP_TAG_KEYWORD, attributes[attr_no],
1496 num_value, NULL,
1497 (const char * const *)values);
1498
1499 for (int k = 0; k < i; k++) {
1500 free(values[k]);
1501 values[k] = NULL;
1502 }
1503 }
1504 cupsArrayDelete(list);
1505 list = NULL;
1506 }
1507 }
1508
1509 /*add_keyword_attributes - Adds attributes to the merged_attribute variable for
1510 the cluster. This function adds attributes with
1511 value tag IPP_TAG_KEYWORD*/
add_keyword_attributes(char * cluster_name,ipp_t ** merged_attributes)1512 void add_keyword_attributes(char* cluster_name, ipp_t **merged_attributes)
1513 {
1514 int count, i;
1515 remote_printer_t *p;
1516 const char *str;
1517 char *q;
1518 cups_array_t *list;
1519 ipp_attribute_t *attr;
1520 int num_value, attr_no;
1521 char* attributes[] = {
1522 "output-mode-supported",
1523 "urf-supported",
1524 "pwg-raster-document-type-supported",
1525 "media-source-supported",
1526 "media-type-supported",
1527 "print-color-mode-supported",
1528 "sides-supported"
1529 };
1530
1531 for (attr_no = 0; attr_no < 7; attr_no ++) {
1532 if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1533 (cups_acopy_func_t)strdup,
1534 (cups_afree_func_t)free)) == NULL)
1535 return;
1536
1537 num_value = 0;
1538 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1539 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1540 if (strcmp(cluster_name, p->queue_name))
1541 continue;
1542 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1543 p->status == STATUS_TO_BE_RELEASED )
1544 continue;
1545 if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no],
1546 IPP_TAG_KEYWORD)) != NULL) {
1547 count = ippGetCount(attr);
1548 for (i = 0; i < count; i++) {
1549 str = ippGetString(attr, i, NULL);
1550 if (!cupsArrayFind(list, (void *)str)){
1551 cupsArrayAdd(list, (void *)str);
1552 num_value ++;
1553 }
1554 }
1555 }
1556 }
1557 if (num_value != 0) {
1558 char *values[num_value];
1559 for (q = (char *)cupsArrayFirst(list), i=0;
1560 q;
1561 q = (char *)cupsArrayNext(list), i ++) {
1562 values[i] = malloc(sizeof(char) * (strlen(q) + 1));
1563 snprintf(values[i], strlen(q) + 1, "%s", q);
1564 }
1565 ippAddStrings(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1566 attributes[attr_no], num_value, NULL,
1567 (const char * const *)values);
1568
1569 for (int k = 0; k < i; k++) {
1570 free(values[k]);
1571 values[k] = NULL;
1572 }
1573 }
1574 cupsArrayDelete(list);
1575 list = NULL;
1576 }
1577 }
1578
1579 /*add_enum_attributes - Adds attributes to the merged_attribute variable for
1580 the cluster. This function adds attributes with value
1581 tag IPP_TAG_BEGIN_ENUM*/
add_enum_attributes(char * cluster_name,ipp_t ** merged_attributes)1582 void add_enum_attributes(char* cluster_name, ipp_t **merged_attributes)
1583 {
1584 int count, i, value;
1585 remote_printer_t *p;
1586 char *str = NULL;
1587 char *q;
1588 cups_array_t *list;
1589 ipp_attribute_t *attr;
1590 int num_value, attr_no;
1591 char* attributes[] = {
1592 "finishings-supported",
1593 "print-quality-supported",
1594 "finishing-template",
1595 "finishings-col-database"
1596 };
1597
1598 for (attr_no = 0; attr_no < 4; attr_no ++) {
1599 if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1600 (cups_acopy_func_t)strdup,
1601 (cups_afree_func_t)free)) == NULL)
1602 return ;
1603 str = malloc(sizeof(char) * 10);
1604 num_value = 0;
1605 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1606 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1607 if (strcmp(cluster_name,p->queue_name))
1608 continue;
1609 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1610 p->status == STATUS_TO_BE_RELEASED )
1611 continue;
1612 if ((attr = ippFindAttribute(p->prattrs,attributes[attr_no], IPP_TAG_ENUM)) != NULL) {
1613 count = ippGetCount(attr);
1614 for (i = 0; i < count; i ++) {
1615 value = ippGetInteger(attr, i);
1616 sprintf(str,"%d",value);
1617 if (!cupsArrayFind(list, (void *)str)){
1618 cupsArrayAdd(list, (void *)str);
1619 num_value++;
1620 }
1621 }
1622 }
1623 }
1624
1625 if (num_value != 0){
1626 int values[num_value];
1627 for (q = (char *)cupsArrayFirst(list), i = 0;q;
1628 q = (char *)cupsArrayNext(list), i++) {
1629 values[i] = atoi(q);
1630 }
1631 ippAddIntegers(*merged_attributes, IPP_TAG_PRINTER,IPP_TAG_ENUM,
1632 attributes[attr_no], num_value,values);
1633 }
1634
1635 if (str != NULL) {
1636 free(str);
1637 str = NULL;
1638 }
1639 cupsArrayDelete(list);
1640 list = NULL;
1641 }
1642 }
1643
1644 /*add_margin_attribute - Adds margin attributes to the merged_attribute variable for the cluster.*/
add_margin_attributes(char * cluster_name,ipp_t ** merged_attributes)1645 void add_margin_attributes(char* cluster_name, ipp_t **merged_attributes)
1646 {
1647 int count, i, value;
1648 remote_printer_t *p;
1649 char *str;
1650 char *q;
1651 cups_array_t *list;
1652 ipp_attribute_t *attr;
1653 int num_value, attr_no;
1654 char* attributes[] = {
1655 "media-bottom-margin-supported",
1656 "media-left-margin-supported",
1657 "media-top-margin-supported",
1658 "media-right-margin-supported"
1659 };
1660
1661 for (attr_no = 0; attr_no < 4; attr_no++) {
1662 if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1663 (cups_acopy_func_t)strdup,
1664 (cups_afree_func_t)free)) == NULL)
1665 return ;
1666 str = malloc(sizeof(char)*10);
1667 num_value = 0;
1668 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1669 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1670 if (strcmp(cluster_name,p->queue_name))
1671 continue;
1672 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1673 p->status == STATUS_TO_BE_RELEASED )
1674 continue;
1675 if ((attr = ippFindAttribute(p->prattrs,attributes[attr_no], IPP_TAG_INTEGER)) != NULL) {
1676 count = ippGetCount(attr);
1677 for (i = 0; i < count; i++) {
1678 value = ippGetInteger(attr, i);
1679 sprintf(str,"%d",value);
1680 if (!cupsArrayFind(list, (void *)str)){
1681 cupsArrayAdd(list, (void *)str);
1682 num_value++;
1683 }
1684 }
1685 }
1686 }
1687
1688 if (num_value != 0){
1689 int values[num_value];
1690 for (q = (char *)cupsArrayFirst(list),i=0;q;
1691 q = (char *)cupsArrayNext(list),i++) {
1692 values[i] = atoi(q);
1693 }
1694 ippAddIntegers(*merged_attributes, IPP_TAG_PRINTER,IPP_TAG_INTEGER,
1695 attributes[attr_no], num_value,values);
1696 }
1697
1698 if (str != NULL) {
1699 free(str);
1700 str = NULL;
1701 }
1702 cupsArrayDelete(list);
1703 list = NULL;
1704 }
1705 }
1706
1707 /*add_resolution_attributes - Adds resolution attributes to the merged_attribute
1708 for the cluster*/
add_resolution_attributes(char * cluster_name,ipp_t ** merged_attributes)1709 void add_resolution_attributes(char* cluster_name, ipp_t **merged_attributes)
1710 {
1711 int count, i;
1712 remote_printer_t *p;
1713 ipp_attribute_t *attr;
1714 int num_resolution, attr_no;
1715 cups_array_t *res_array;
1716 res_t *res, *resolution;
1717 char* attributes[] = {
1718 "printer-resolution-supported",
1719 "pwg-raster-document-resolution-supported",
1720 "pclm-source-resolution-supported"
1721 };
1722
1723 for (attr_no = 0; attr_no < 3; attr_no ++) {
1724 res_array = NULL;
1725 res_array = resolutionArrayNew();
1726 num_resolution = 0;
1727 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1728 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1729 if (strcmp(cluster_name, p->queue_name))
1730 continue;
1731 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1732 p->status == STATUS_TO_BE_RELEASED )
1733 continue;
1734 if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no],
1735 IPP_TAG_RESOLUTION)) != NULL) {
1736 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1737 if ((res = ippResolutionToRes(attr, i)) != NULL) {
1738 if (cupsArrayFind(res_array, res) == NULL) {
1739 cupsArrayAdd(res_array, res);
1740 num_resolution ++;
1741 }
1742 free_resolution(res, NULL);
1743 }
1744 }
1745 }
1746 }
1747 if (num_resolution) {
1748 int xres[num_resolution], yres[num_resolution];
1749 for (i = 0, resolution=cupsArrayFirst(res_array); resolution;
1750 i ++, resolution = cupsArrayNext(res_array)) {
1751 xres[i] = resolution->x;
1752 yres[i] = resolution->y;
1753 }
1754 ippAddResolutions(*merged_attributes, IPP_TAG_PRINTER,
1755 attributes[attr_no], num_resolution,
1756 IPP_RES_PER_INCH, xres, yres);
1757 }
1758 cupsArrayDelete(res_array);
1759 res_array = NULL;
1760 }
1761 }
1762
1763 /*add_mediasize_attribute - Adds media sizes to the merged_attribute for the
1764 printer*/
add_mediasize_attributes(char * cluster_name,ipp_t ** merged_attributes)1765 void add_mediasize_attributes(char* cluster_name, ipp_t **merged_attributes)
1766 {
1767 int count, i = 0;
1768 remote_printer_t *p;
1769 ipp_attribute_t *attr, *media_size_supported, *x_dim, *y_dim;
1770 int num_sizes, attr_no,num_ranges;
1771 ipp_t *media_size;
1772 cups_array_t *sizes, *size_ranges;
1773 media_size_t *temp, *media_s;
1774 pagesize_range_t *temp_range = NULL, *range = NULL;
1775 char* attributes[] = {
1776 "media-size-supported",
1777 };
1778
1779 sizes = cupsArrayNew3((cups_array_func_t)compare_mediasize, NULL, NULL, 0,
1780 (cups_acopy_func_t)copy_media_size,
1781 (cups_afree_func_t)free);
1782 size_ranges = cupsArrayNew3((cups_array_func_t)compare_rangesize, NULL, NULL,
1783 0,
1784 (cups_acopy_func_t)copy_range_size,
1785 (cups_afree_func_t)free);
1786 temp = (media_size_t *)malloc(sizeof(media_size_t));
1787 temp_range = (pagesize_range_t *)malloc(sizeof(pagesize_range_t));
1788 for (attr_no = 0; attr_no < 1; attr_no ++) {
1789 num_sizes = 0;
1790 num_ranges = 0;
1791 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1792 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1793 if (strcmp(cluster_name,p->queue_name))
1794 continue;
1795 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1796 p->status == STATUS_TO_BE_RELEASED )
1797 continue;
1798 if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no],
1799 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
1800 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1801 media_size = ippGetCollection(attr, i);
1802 x_dim = ippFindAttribute(media_size, "x-dimension", IPP_TAG_ZERO);
1803 y_dim = ippFindAttribute(media_size, "y-dimension", IPP_TAG_ZERO);
1804 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE ||
1805 ippGetValueTag(y_dim) == IPP_TAG_RANGE) {
1806 if (ippGetValueTag(x_dim) == IPP_TAG_RANGE)
1807 temp_range->x_dim_min = ippGetRange(x_dim, 0,
1808 &temp_range->x_dim_max);
1809 else
1810 temp_range->x_dim_min = temp_range->x_dim_max =
1811 ippGetInteger(x_dim, 0);
1812
1813 if (ippGetValueTag(y_dim) == IPP_TAG_RANGE)
1814 temp_range->y_dim_min = ippGetRange(y_dim, 0,
1815 &temp_range->y_dim_max);
1816 else
1817 temp_range->y_dim_min = temp_range->y_dim_max =
1818 ippGetInteger(y_dim, 0);
1819 if (!cupsArrayFind(size_ranges,temp_range)) {
1820 cupsArrayAdd(size_ranges, temp_range);
1821 num_ranges++;
1822 }
1823 } else {
1824 temp->x = ippGetInteger(x_dim,0);
1825 temp->y = ippGetInteger(y_dim,0);
1826 if (!cupsArrayFind(sizes, temp)){
1827 cupsArrayAdd(sizes, temp);
1828 num_sizes++;
1829 }
1830 }
1831 }
1832 }
1833 }
1834 media_size_supported =
1835 ippAddCollections(*merged_attributes,
1836 IPP_TAG_PRINTER, attributes[attr_no],
1837 num_sizes+num_ranges, NULL);
1838 if (num_sizes) {
1839 for (i = 0, media_s = cupsArrayFirst(sizes);
1840 media_s; i ++, media_s = cupsArrayNext(sizes)) {
1841 ipp_t *size = create_media_size(media_s->x, media_s->y);
1842 ippSetCollection(*merged_attributes, &media_size_supported, i, size);
1843 ippDelete(size);
1844 }
1845 }
1846 if (num_ranges) {
1847 for (range = cupsArrayFirst(size_ranges); range;
1848 i++, range = cupsArrayNext(size_ranges)) {
1849 ipp_t *size_range = create_media_range(range->x_dim_min,
1850 range->x_dim_max,
1851 range->y_dim_min,
1852 range->y_dim_max);
1853 ippSetCollection(*merged_attributes, &media_size_supported, i,
1854 size_range);
1855 ippDelete(size_range);
1856 }
1857 }
1858 }
1859
1860 free(temp);
1861 free(temp_range);
1862 cupsArrayDelete(sizes);
1863 cupsArrayDelete(size_ranges);
1864 }
1865
1866 /*add_mediadatabase_attribute - Adds media-col-database attributes for the
1867 cluster*/
1868 void
add_mediadatabase_attributes(char * cluster_name,ipp_t ** merged_attributes)1869 add_mediadatabase_attributes(char* cluster_name, ipp_t **merged_attributes)
1870 {
1871 int count, i;
1872 remote_printer_t *p;
1873 ipp_attribute_t *attr, *media_attr;
1874 int num_database, attr_no;
1875 cups_array_t *media_database;
1876 media_col_t *temp, *media_data;
1877 ipp_t *media_col,
1878 *media_size, *current_media;
1879 ipp_attribute_t *media_col_database;
1880 char media_source[32], media_type[32];
1881 char* attributes[] = {
1882 "media-col-database",
1883 };
1884 temp = (media_col_t *)malloc(sizeof(media_col_t));
1885 media_database = cupsArrayNew3((cups_array_func_t)compare_media,
1886 NULL, NULL, 0,
1887 (cups_acopy_func_t)copy_media,
1888 (cups_afree_func_t)free);
1889 for (attr_no = 0; attr_no < 1; attr_no ++) {
1890 num_database = 0;
1891 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
1892 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
1893 if (strcmp(cluster_name, p->queue_name))
1894 continue;
1895 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
1896 p->status == STATUS_TO_BE_RELEASED )
1897 continue;
1898 if ((attr = ippFindAttribute(p->prattrs, attributes[attr_no],
1899 IPP_TAG_BEGIN_COLLECTION)) != NULL){
1900 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
1901 media_col = ippGetCollection(attr, i);
1902 media_size =
1903 ippGetCollection(ippFindAttribute(media_col,
1904 "media-size",
1905 IPP_TAG_BEGIN_COLLECTION), 0);
1906 temp->x = ippGetInteger(ippFindAttribute(media_size, "x-dimension",
1907 IPP_TAG_ZERO),0);
1908 temp->y = ippGetInteger(ippFindAttribute(media_size, "y-dimension",
1909 IPP_TAG_ZERO),0);
1910 temp->top_margin =
1911 ippGetInteger(ippFindAttribute(media_col,
1912 "media-top-margin",
1913 IPP_TAG_INTEGER),
1914 0);
1915 temp->bottom_margin =
1916 ippGetInteger(ippFindAttribute(media_col,
1917 "media-bottom-margin",
1918 IPP_TAG_INTEGER),
1919 0);
1920 temp->left_margin =
1921 ippGetInteger(ippFindAttribute(media_col,
1922 "media-left-margin",
1923 IPP_TAG_INTEGER),
1924 0);
1925 temp->right_margin =
1926 ippGetInteger(ippFindAttribute(media_col,
1927 "media-right-margin",
1928 IPP_TAG_INTEGER),
1929 0);
1930 media_type[0] = '\0';
1931 media_source[0] = '\0';
1932 temp->media_source = NULL;
1933 temp->media_type = NULL;
1934 if ((media_attr = ippFindAttribute(media_col,
1935 "media-type",
1936 IPP_TAG_KEYWORD)) != NULL)
1937 pwg_ppdize_name(ippGetString(media_attr, 0, NULL), media_type,
1938 sizeof(media_type));
1939 if (strlen(media_type) > 1) {
1940 temp->media_type = (char*)malloc(sizeof(char) * 32);
1941 strcpy(temp->media_type, media_type);
1942 }
1943 if ((media_attr = ippFindAttribute(media_col, "media-source",
1944 IPP_TAG_KEYWORD)) != NULL) {
1945 pwg_ppdize_name(ippGetString(media_attr, 0, NULL), media_source,
1946 sizeof(media_source));
1947 }
1948 if(strlen(media_source) > 1) {
1949 temp->media_source = (char*)malloc(sizeof(char) * 32);
1950 strcpy(temp->media_source, media_source);
1951 }
1952
1953 if (!cupsArrayFind(media_database, temp)) {
1954 cupsArrayAdd(media_database, temp);
1955 num_database ++;
1956 }
1957 }
1958 }
1959 }
1960
1961 if (num_database != 0) {
1962 media_col_database = ippAddCollections(*merged_attributes,
1963 IPP_TAG_PRINTER,
1964 attributes[attr_no],
1965 num_database, NULL);
1966 for (i = 0, media_data = cupsArrayFirst(media_database); media_data;
1967 i ++, media_data = cupsArrayNext(media_database)) {
1968 current_media = create_media_col(media_data->x, media_data->y,
1969 media_data->left_margin,
1970 media_data->right_margin,
1971 media_data->top_margin,
1972 media_data->bottom_margin,
1973 media_data->media_source,
1974 media_data->media_type);
1975 ippSetCollection(*merged_attributes, &media_col_database, i,
1976 current_media);
1977 ippDelete(current_media);
1978 }
1979 }
1980 }
1981
1982 free(temp);
1983 cupsArrayDelete(media_database);
1984 }
1985
1986 /*add_jobpresets_attribute - Adds presets attributes for the cluster*/
add_jobpresets_attribute(char * cluster_name,ipp_t ** merged_attributes)1987 void add_jobpresets_attribute(char* cluster_name, ipp_t ** merged_attributes)
1988 {
1989 int count, i, num_preset = 0, preset_no = 0;
1990 remote_printer_t *p;
1991 cups_array_t *list, *added_presets;
1992 ipp_t *preset;
1993 ipp_attribute_t *attr;
1994 const char *preset_name;
1995 ipp_attribute_t *preset_attribute;
1996
1997 if ((list = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
1998 (cups_acopy_func_t)strdup,
1999 (cups_afree_func_t)free)) == NULL)
2000 return;
2001
2002 if ((added_presets = cupsArrayNew3((cups_array_func_t)strcasecmp,
2003 NULL, NULL, 0,
2004 (cups_acopy_func_t)strdup,
2005 (cups_afree_func_t)free)) == NULL)
2006 return;
2007
2008 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2009 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2010 if (strcmp(cluster_name, p->queue_name))
2011 continue;
2012 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
2013 p->status == STATUS_TO_BE_RELEASED )
2014 continue;
2015 if ((attr = ippFindAttribute(p->prattrs, "job-presets-supported",
2016 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
2017 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2018 preset = ippGetCollection(attr, i);
2019 preset_name = ippGetString(ippFindAttribute(preset, "preset-name",
2020 IPP_TAG_ZERO), 0, NULL);
2021 if (!cupsArrayFind(list, (void *)preset_name)) {
2022 cupsArrayAdd(list, (void *)preset_name);
2023 num_preset++;
2024 }
2025 }
2026 }
2027 }
2028
2029 if (num_preset == 0) {
2030 cupsArrayDelete(list);
2031 cupsArrayDelete(added_presets);
2032 return;
2033 }
2034
2035 preset_attribute = ippAddCollections(*merged_attributes, IPP_TAG_PRINTER,
2036 "job-presets-supported", num_preset,
2037 NULL);
2038
2039 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2040 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2041 if ((attr = ippFindAttribute(p->prattrs, "job-presets-supported",
2042 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
2043 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2044 preset = ippGetCollection(attr, i);
2045 preset_name = ippGetString(ippFindAttribute(preset, "preset-name",
2046 IPP_TAG_ZERO), 0, NULL);
2047 if (!cupsArrayFind(added_presets, (void *)preset_name)) {
2048 cupsArrayAdd(added_presets, (void *)preset_name);
2049 ippSetCollection(*merged_attributes, &preset_attribute, i, preset);
2050 preset_no++;
2051 } else
2052 continue;
2053 }
2054 }
2055 }
2056
2057 cupsArrayDelete(list);
2058 cupsArrayDelete(added_presets);
2059 }
2060
2061 /* get_pagesize: Function returns the standard/custom page size using
2062 generate_sizes function from ppdgenerator.c*/
get_pagesize(ipp_t * printer_attributes)2063 static cups_array_t* get_pagesize(ipp_t *printer_attributes)
2064 {
2065 cups_array_t *sizes, *page_media;
2066 cups_size_t *size;
2067 ipp_attribute_t *defattr;
2068 char *ppdsizename, *temp;
2069 int min_length = INT_MAX, min_width = INT_MAX,
2070 max_length = 0, max_width = 0,
2071 bottom, left, right, top;
2072 char ppdname[41];
2073
2074 ppdsizename = (char *)malloc(sizeof(char) * 128);
2075 sizes = generate_sizes(printer_attributes, &defattr, &min_length, &min_width,
2076 &max_length, &max_width,
2077 &bottom, &left, &right, &top,ppdname);
2078 if ((page_media = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2079 (cups_acopy_func_t)strdup,
2080 (cups_afree_func_t)free)) == NULL)
2081 return NULL;
2082 for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
2083 size = (cups_size_t *)cupsArrayNext(sizes)) {
2084 strcpy(ppdsizename, size->media);
2085 if (( temp = strchr(ppdsizename, ' ')) != NULL)
2086 *temp = '\0';
2087 cupsArrayAdd(page_media, ppdsizename);
2088 }
2089 free(ppdsizename);
2090 cupsArrayDelete(sizes);
2091
2092 return page_media;
2093 }
2094
2095 /*get_mediadata - This function extracts the MediaType, InputSlot and OutputBin
2096 supported, using IPP Response message of the printer*/
get_mediadata(ipp_t * printer_attributes,char * requested_attr)2097 cups_array_t* get_mediadata(ipp_t *printer_attributes, char* requested_attr)
2098 {
2099 ipp_attribute_t *attr;
2100 int count, i;
2101 cups_array_t *media_data;
2102 const char *keyword; /* Keyword value */
2103 char ppdname[41];
2104 char requested_option[30];
2105
2106 if (!strcmp(requested_attr, "MediaType"))
2107 strcpy(requested_option, "media-type-supported");
2108 else if (!strcmp(requested_attr, "InputSlot"))
2109 strcpy(requested_option, "media-source-supported");
2110 else if (!strcmp(requested_attr, "OutputBin"))
2111 strcpy(requested_option, "output-bin-supported");
2112 else
2113 return NULL;
2114
2115 if ((media_data = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2116 (cups_acopy_func_t)strdup,
2117 (cups_afree_func_t)free)) == NULL)
2118 return NULL;
2119 if ((attr = ippFindAttribute(printer_attributes, requested_option,
2120 IPP_TAG_ZERO)) != NULL
2121 && (count = ippGetCount(attr)) > 1) {
2122 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2123 keyword = ippGetString(attr, i, NULL);
2124 pwg_ppdize_name(keyword, ppdname, sizeof(ppdname));
2125 cupsArrayAdd(media_data, ppdname);
2126 }
2127 }
2128 return media_data;
2129 }
2130
2131 /*get_mimetype_attribute - Adds attributes to the merged_attribute variable for
2132 the cluster. This function adds attribute with value
2133 tag IPP_TAG_MIMETYPE*/
get_mimetype_attributes(ipp_t * printer_attributes)2134 cups_array_t* get_mimetype_attributes(ipp_t *printer_attributes)
2135 {
2136 int count, i;
2137 const char *str;
2138 cups_array_t *document_formats;
2139 ipp_attribute_t *attr;
2140
2141 if ((document_formats = cupsArrayNew3((cups_array_func_t)strcasecmp,
2142 NULL, NULL, 0,
2143 (cups_acopy_func_t)strdup,
2144 (cups_afree_func_t)free)) == NULL)
2145 return NULL;
2146
2147 if ((attr = ippFindAttribute(printer_attributes, "document-format-supported",
2148 IPP_TAG_MIMETYPE)) != NULL) {
2149 count = ippGetCount(attr);
2150 for (i = 0; i < count; i ++) {
2151 str = ippGetString(attr, i, NULL);
2152 if (!cupsArrayFind(document_formats, (void *)str))
2153 cupsArrayAdd(document_formats, (void *)str);
2154 }
2155 }
2156 return document_formats;
2157 }
2158
2159 /*get_staplelocation: This function returns the supported staple locations of
2160 the printer */
get_staplelocation(ipp_t * printer_attributes)2161 cups_array_t* get_staplelocation(ipp_t *printer_attributes)
2162 {
2163 ipp_attribute_t *attr;
2164 int count, value, i;
2165 const char *name;
2166 cups_array_t *staplelocation;
2167
2168 if ((staplelocation = cupsArrayNew3((cups_array_func_t)strcasecmp,
2169 NULL, NULL, 0,
2170 (cups_acopy_func_t)strdup,
2171 (cups_afree_func_t)free)) == NULL)
2172 return NULL;
2173 if ((attr = ippFindAttribute(printer_attributes, "finishings-supported",
2174 IPP_TAG_ENUM)) != NULL) {
2175 count = ippGetCount(attr);
2176 for (i = 0; i < count; i ++) {
2177 value = ippGetInteger(attr, i);
2178 name = ippEnumString("finishings", value);
2179 if (!strncmp(name, "staple-", 7) || !strncmp(name, "bind-", 5) ||
2180 !strncmp(name, "edge-stitch-", 12) || !strcmp(name, "saddle-stitch"))
2181 if (!cupsArrayFind(staplelocation,(void*)name))
2182 cupsArrayAdd(staplelocation,(void*)name);
2183 }
2184 }
2185 return staplelocation;
2186 }
2187
2188 /*get_foldtype - Function returns the supported foldtype for the printer*/
get_foldtype(ipp_t * printer_attributes)2189 cups_array_t* get_foldtype(ipp_t *printer_attributes)
2190 {
2191 ipp_attribute_t *attr;
2192 int count, value, i;
2193 const char *name;
2194 cups_array_t *foldtype;
2195
2196 if ((foldtype = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2197 (cups_acopy_func_t)strdup,
2198 (cups_afree_func_t)free)) == NULL)
2199 return NULL;
2200 if ((attr = ippFindAttribute(printer_attributes, "finishings-supported",
2201 IPP_TAG_ENUM)) != NULL) {
2202 count = ippGetCount(attr);
2203 for (i = 0; i < count; i ++) {
2204 value = ippGetInteger(attr, i);
2205 name = ippEnumString("finishings", value);
2206 if (!strncmp(name, "fold-", 5))
2207 if (!cupsArrayFind(foldtype, (void*)name))
2208 cupsArrayAdd(foldtype, (void*)name);
2209 }
2210 }
2211 return foldtype;
2212 }
2213
2214 /*get_finishings - Function returns the supported finishings for the printer*/
get_finishings(ipp_t * printer_attributes)2215 cups_array_t* get_finishings(ipp_t *printer_attributes)
2216 {
2217 ipp_attribute_t *attr;
2218 int count, value, i;
2219 const char *name;
2220 cups_array_t *finishings;
2221
2222 if ((finishings = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2223 (cups_acopy_func_t)strdup,
2224 (cups_afree_func_t)free)) == NULL)
2225 return NULL;
2226 if ((attr = ippFindAttribute(printer_attributes, "finishings-supported",
2227 IPP_TAG_ENUM)) != NULL) {
2228 count = ippGetCount(attr);
2229 for (i = 0; i < count; i ++) {
2230 value = ippGetInteger(attr, i);
2231 name = ippEnumString("finishings", value);
2232 if (!cupsArrayFind(finishings, (void*)name))
2233 cupsArrayAdd(finishings, (void*)name);
2234 }
2235 }
2236 return finishings;
2237 }
2238
2239
2240 /*get_punchmedia - Returns the puchmedia supported by the printer*/
get_punchmedia(ipp_t * printer_attributes)2241 cups_array_t* get_punchmedia(ipp_t *printer_attributes)
2242 {
2243 ipp_attribute_t *attr;
2244 int count, value, i;
2245 const char *name;
2246 cups_array_t *punchmedia;
2247
2248 if ((punchmedia = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2249 (cups_acopy_func_t)strdup,
2250 (cups_afree_func_t)free)) == NULL)
2251 return NULL;
2252 if ((attr = ippFindAttribute(printer_attributes, "finishings-supported",
2253 IPP_TAG_ENUM)) != NULL) {
2254 count = ippGetCount(attr);
2255 for (i = 0; i < count; i ++) {
2256 value = ippGetInteger(attr, i);
2257 name = ippEnumString("finishings", value);
2258 if (!strncmp(name, "punch-", 6))
2259 if (!cupsArrayFind(punchmedia, (void*)name))
2260 cupsArrayAdd(punchmedia, (void*)name);
2261 }
2262 }
2263 return punchmedia;
2264 }
2265
2266 /*get_duplex - Function returns whether the printer support Duplex,
2267 DuplexTumble, DuplexNoTumble using attributes returned by the
2268 IPP Request*/
get_duplex(ipp_t * printer_attributes)2269 cups_array_t* get_duplex(ipp_t *printer_attributes)
2270 {
2271 ipp_attribute_t *attr;
2272 int count, i;
2273 cups_array_t *duplex_options;
2274 const char *str;
2275
2276 if ((duplex_options = cupsArrayNew3((cups_array_func_t)strcasecmp,
2277 NULL, NULL, 0,
2278 (cups_acopy_func_t)strdup,
2279 (cups_afree_func_t)free)) == NULL)
2280 return NULL;
2281 if ((attr = ippFindAttribute(printer_attributes, "sides-supported",
2282 IPP_TAG_KEYWORD)) != NULL) {
2283 count = ippGetCount(attr);
2284 for (i = 0; i < count; i ++) {
2285 str = ippGetString(attr, i, NULL);
2286 if (!strcmp(str, "one-sided"))
2287 cupsArrayAdd(duplex_options, "None");
2288 else if (!strcmp(str, "two-sided-long-edge"))
2289 cupsArrayAdd(duplex_options, "DuplexNoTumble");
2290 else if (!strcmp(str, "two-sided-short-edge"))
2291 cupsArrayAdd(duplex_options, "DuplexTumble");
2292 }
2293 }
2294 return duplex_options;
2295 }
2296
2297 /* get_colormodel - Returns the colormodel supported by the printer*/
get_colormodel(ipp_t * printer_attributes)2298 cups_array_t* get_colormodel(ipp_t *printer_attributes)
2299 {
2300 ipp_attribute_t *attr;
2301 int count, i;
2302 cups_array_t *colormodel;
2303 const char *keyword;
2304 int have_bi_level = 0,
2305 have_mono = 0;
2306
2307 if ((colormodel = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2308 (cups_acopy_func_t)strdup,
2309 (cups_afree_func_t)free)) == NULL)
2310 return NULL;
2311 if ((attr = ippFindAttribute(printer_attributes, "urf-supported",
2312 IPP_TAG_KEYWORD)) == NULL)
2313 if ((attr = ippFindAttribute(printer_attributes,
2314 "pwg-raster-document-type-supported",
2315 IPP_TAG_KEYWORD)) == NULL)
2316 if ((attr = ippFindAttribute(printer_attributes,
2317 "print-color-mode-supported",
2318 IPP_TAG_KEYWORD)) == NULL)
2319 attr = ippFindAttribute(printer_attributes, "output-mode-supported",
2320 IPP_TAG_KEYWORD);
2321
2322 if (attr && ippGetCount(attr) > 0) {
2323 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2324 keyword = ippGetString(attr, i, NULL);
2325 if (!have_bi_level && (!strcasecmp(keyword, "black_1") ||
2326 !strcmp(keyword, "bi-level") ||
2327 !strcmp(keyword, "process-bi-level"))) {
2328 cupsArrayAdd(colormodel,"FastGray");
2329 have_bi_level = 1;
2330 } else if (!have_mono &&(!strcasecmp(keyword, "sgray_8") ||
2331 !strncmp(keyword, "W8", 2) ||
2332 !strcmp(keyword, "monochrome") ||
2333 !strcmp(keyword, "process-monochrome"))) {
2334 have_mono = 1;
2335 cupsArrayAdd(colormodel,"Gray");
2336 } else if (!strcasecmp(keyword, "sgray_16") ||
2337 !strncmp(keyword, "W8-16", 5) || !strncmp(keyword, "W16", 3))
2338 cupsArrayAdd(colormodel,"Gray16");
2339 else if (!strcasecmp(keyword, "srgb_8") ||
2340 !strncmp(keyword, "SRGB24", 6) || !strcmp(keyword, "color"))
2341 cupsArrayAdd(colormodel,"RGB");
2342 else if ((!strcasecmp(keyword, "srgb_16") ||
2343 !strncmp(keyword, "SRGB48", 6)) &&
2344 !ippContainsString(attr, "srgb_8"))
2345 cupsArrayAdd(colormodel,"RGB");
2346 else if (!strcasecmp(keyword, "adobe-rgb_16") ||
2347 !strncmp(keyword, "ADOBERGB48", 10) ||
2348 !strncmp(keyword, "ADOBERGB24-48", 13))
2349 cupsArrayAdd(colormodel,"AdobeRGB");
2350 else if ((!strcasecmp(keyword, "adobe-rgb_8") ||
2351 !strcmp(keyword, "ADOBERGB24")) &&
2352 !ippContainsString(attr, "adobe-rgb_16"))
2353 cupsArrayAdd(colormodel,"AdobeRGB");
2354 else if ((!strcasecmp(keyword, "black_8") &&
2355 !ippContainsString(attr, "black_16")) ||
2356 !strcmp(keyword, "DEVW8"))
2357 cupsArrayAdd(colormodel,"DeviceGray");
2358 else if (!strcasecmp(keyword, "black_16") ||
2359 !strcmp(keyword, "DEVW16") || !strcmp(keyword, "DEVW8-16"))
2360 cupsArrayAdd(colormodel,"DeviceGray");
2361 else if ((!strcasecmp(keyword, "cmyk_8") &&
2362 !ippContainsString(attr, "cmyk_16")) ||
2363 !strcmp(keyword, "DEVCMYK32"))
2364 cupsArrayAdd(colormodel,"CMYK");
2365 else if (!strcasecmp(keyword, "cmyk_16") ||
2366 !strcmp(keyword, "DEVCMYK32-64") ||
2367 !strcmp(keyword, "DEVCMYK64"))
2368 cupsArrayAdd(colormodel,"CMYK");
2369 else if ((!strcasecmp(keyword, "rgb_8") &&
2370 !ippContainsString(attr, "rgb_16")) ||
2371 !strcmp(keyword, "DEVRGB24"))
2372 cupsArrayAdd(colormodel,"DeviceRGB");
2373 else if (!strcasecmp(keyword, "rgb_16") ||
2374 !strcmp(keyword, "DEVRGB24-48") ||
2375 !strcmp(keyword, "DEVRGB48"))
2376 cupsArrayAdd(colormodel,"DeviceRGB");
2377 }
2378 }
2379 return colormodel;
2380 }
2381
2382 /* get_printquality - Returns the print qualities supported by the printer*/
get_printquality(ipp_t * printer_attributes)2383 cups_array_t* get_printquality(ipp_t *printer_attributes)
2384 {
2385 ipp_attribute_t *quality;
2386 cups_array_t *print_qualities;
2387
2388 if ((print_qualities = cupsArrayNew3((cups_array_func_t)strcasecmp,
2389 NULL, NULL, 0,
2390 (cups_acopy_func_t)strdup,
2391 (cups_afree_func_t)free)) == NULL)
2392 return NULL;
2393 if ((quality=ippFindAttribute(printer_attributes, "print-quality-supported",
2394 IPP_TAG_ENUM)) != NULL) {
2395 if (ippContainsInteger(quality, IPP_QUALITY_DRAFT))
2396 cupsArrayAdd(print_qualities, "3");
2397 if (ippContainsInteger(quality, IPP_QUALITY_HIGH))
2398 cupsArrayAdd(print_qualities, "5");
2399 cupsArrayAdd(print_qualities, "4");
2400 }
2401 return print_qualities;
2402 }
2403
2404 /* get_job_data - Returns the job_sheets,multiple-document-handling supported
2405 by the printer*/
get_job_data(ipp_t * printer_attributes,char * requested_attr)2406 cups_array_t* get_job_data(ipp_t *printer_attributes, char* requested_attr)
2407 {
2408 ipp_attribute_t *attr;
2409 cups_array_t *job_data;
2410 int i, count;
2411 const char* str;
2412
2413 if ((job_data = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2414 (cups_acopy_func_t)strdup,
2415 (cups_afree_func_t)free)) == NULL)
2416 return NULL;
2417 if ((attr = ippFindAttribute(printer_attributes, requested_attr,
2418 IPP_TAG_KEYWORD)) != NULL) {
2419 for(i = 0, count = ippGetCount(attr); i < count; i ++) {
2420 str = ippGetString(attr, i, NULL);
2421 if (!cupsArrayFind(job_data, (void *)str))
2422 cupsArrayAdd(job_data, (void*)str);
2423 }
2424 }
2425 return job_data;
2426 }
2427
2428 /* get_finishingtemplate - Returns the Finishing Templates supported by the
2429 printer*/
get_finishingtemplate(ipp_t * printer_attributes)2430 cups_array_t* get_finishingtemplate(ipp_t *printer_attributes)
2431 {
2432 ipp_attribute_t *attr;
2433 cups_array_t *finishing_templates;
2434 ipp_t *finishing_col; /* Current finishing collection */
2435 int count, i;
2436 const char *keyword;
2437
2438 if ((finishing_templates = cupsArrayNew3((cups_array_func_t)strcasecmp,
2439 NULL, NULL, 0,
2440 (cups_acopy_func_t)strdup,
2441 (cups_afree_func_t)free)) == NULL)
2442 return NULL;
2443 if ((attr = ippFindAttribute(printer_attributes, "finishings-col-database",
2444 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
2445 count = ippGetCount(attr);
2446 for (i = 0; i < count; i ++) {
2447 finishing_col = ippGetCollection(attr, i);
2448 keyword = ippGetString(ippFindAttribute(finishing_col,
2449 "finishing-template",
2450 IPP_TAG_ZERO), 0, NULL);
2451 if (!keyword || cupsArrayFind(finishing_templates, (void *)keyword))
2452 continue;
2453 if (strncmp(keyword, "fold-", 5) && (strstr(keyword, "-bottom") ||
2454 strstr(keyword, "-left") ||
2455 strstr(keyword, "-right") ||
2456 strstr(keyword, "-top")))
2457 continue;
2458 cupsArrayAdd(finishing_templates, (void*)keyword);
2459 }
2460 }
2461 return finishing_templates;
2462 }
2463
2464 /* get_printing_data - Returns the print-content-optimize,print-rendering-intent
2465 and print-scaling attributes for the printer*/
get_printing_data(ipp_t * printer_attributes,char * requested_attr)2466 cups_array_t* get_printing_data(ipp_t *printer_attributes,char* requested_attr)
2467 {
2468 ipp_attribute_t *attr;
2469 int count, i;
2470 cups_array_t *printing_support;
2471 const char *keyword;
2472 char requested_option[40];
2473
2474 if(!strcmp(requested_attr, "print-content-optimize"))
2475 strcpy(requested_option, "print-content-optimize-supported");
2476 else if (!strcmp(requested_attr, "print-rendering-intent"))
2477 strcpy(requested_option, "print-rendering-intent-supported");
2478 else if(!strcmp(requested_attr, "print-scaling"))
2479 strcpy(requested_option, "print-scaling-supported");
2480 else if (!strcmp(requested_attr, "job-sheets-supported"))
2481 strcpy(requested_option, "job-sheets-supported");
2482 else
2483 return NULL;
2484
2485 if ((printing_support = cupsArrayNew3((cups_array_func_t)strcasecmp,
2486 NULL, NULL, 0,
2487 (cups_acopy_func_t)strdup,
2488 (cups_afree_func_t)free)) == NULL)
2489 return NULL;
2490 if ((attr = ippFindAttribute(printer_attributes, requested_option,
2491 IPP_TAG_ZERO)) != NULL &&
2492 (count = ippGetCount(attr)) > 1) {
2493 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2494 keyword = ippGetString(attr, i, NULL);
2495 cupsArrayAdd(printing_support, (void *)keyword);
2496 }
2497 }
2498 return printing_support;
2499 }
2500
2501 /*get_presets - Returns a list of presets name supported by the printer*/
get_presets(ipp_t * printer_attributes)2502 cups_array_t* get_presets(ipp_t *printer_attributes)
2503 {
2504 ipp_attribute_t *attr;
2505 int count, i;
2506 cups_array_t *presets;
2507 ipp_t *preset;
2508 const char *preset_name;
2509
2510 if ((presets = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2511 (cups_acopy_func_t)strdup,
2512 (cups_afree_func_t)free)) == NULL)
2513 return NULL;
2514 if ((attr = ippFindAttribute(printer_attributes, "job-presets-supported",
2515 IPP_TAG_BEGIN_COLLECTION)) != NULL &&
2516 (count = ippGetCount(attr)) > 1) {
2517 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
2518 preset = ippGetCollection(attr, i);
2519 preset_name = ippGetString(ippFindAttribute(preset, "preset-name",
2520 IPP_TAG_ZERO), 0, NULL);
2521 if(!cupsArrayFind(presets, (void*)preset_name))
2522 cupsArrayAdd(presets, (void *)preset_name);
2523 }
2524 }
2525 return presets;
2526 }
2527
2528 /* get_booklet - Returns True if booklet is supported */
get_booklet(ipp_t * printer_attributes)2529 cups_array_t* get_booklet(ipp_t *printer_attributes)
2530 {
2531 ipp_attribute_t *attr;
2532 cups_array_t *booklet;
2533
2534 if ((booklet = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2535 (cups_acopy_func_t)strdup,
2536 (cups_afree_func_t)free)) == NULL)
2537 return NULL;
2538 if ((attr = ippFindAttribute(printer_attributes, "finishings-supported",
2539 IPP_TAG_ENUM)) != NULL) {
2540 if (ippContainsInteger(attr, IPP_FINISHINGS_BOOKLET_MAKER)) {
2541 /* Assuming that the printer which supports Booklet also supports
2542 printing without Booklet, so for this printer we will return
2543 both "True" and "False" */
2544 cupsArrayAdd(booklet, "True");
2545 }
2546 }
2547 cupsArrayAdd(booklet, "False");
2548 return booklet;
2549 }
2550
2551 /* get_supported_options - Function returns various attributes supported by the
2552 printer, such as PageSize,ColorModel etc.*/
get_supported_options(ipp_t * printer_attributes,char * option)2553 cups_array_t* get_supported_options(ipp_t *printer_attributes, char* option)
2554 {
2555 if (!strcmp(option, "PageSize") || !strcmp(option, "PageRegion"))
2556 return get_pagesize(printer_attributes);
2557 else if (!strcmp(option, "MediaType") || !strcmp(option, "InputSlot") ||
2558 !strcmp(option, "OutputBin"))
2559 return get_mediadata(printer_attributes, option);
2560 else if (!strcmp(option, "StapleLocation"))
2561 return get_staplelocation(printer_attributes);
2562 else if (!strcmp(option, "FoldType"))
2563 return get_foldtype(printer_attributes);
2564 else if (!strcmp(option, "PunchMedia"))
2565 return get_punchmedia(printer_attributes);
2566 else if (!strcmp(option, "cupsFinishingTemplate"))
2567 return get_finishingtemplate(printer_attributes);
2568 else if (!strcmp(option, "cupsPrintQuality"))
2569 return get_printquality(printer_attributes);
2570 else if (!strcmp(option, "job-sheets-supported") ||
2571 !strcmp(option, "print-content-optimize") ||
2572 !strcmp(option, "print-rendering-intent") ||
2573 !strcmp(option, "print-scaling"))
2574 return get_printing_data(printer_attributes, option);
2575 else if (!strcmp(option, "APPrinterPreset"))
2576 return get_presets(printer_attributes);
2577 else if(!strcmp(option, "Booklet"))
2578 return get_booklet(printer_attributes);
2579 else if(!strcmp(option, "ColorModel"))
2580 return get_colormodel(printer_attributes);
2581 else if (!strcmp(option, "Duplex"))
2582 return get_duplex(printer_attributes);
2583 else if (!strcmp(option, "multiple-document-handling-supported") ||
2584 !strcmp(option, "cover-back-supported") ||
2585 !strcmp(option, "cover-front-supported") ||
2586 !strcmp(option, "cover-type-supported") ||
2587 !strcmp(option, "media-type-supported"))
2588 return get_job_data(printer_attributes, option);
2589 else if (!strcmp(option,"finishings-supported"))
2590 return get_finishings(printer_attributes);
2591 return NULL;
2592 }
2593
2594 /*check_printer_with_options - Checks whether a printer in an cluster supports
2595 option1 for keyword at value idx_option1 in
2596 ppd_keywords[] and option2 for keyword at value
2597 idx_option2*/
check_printer_with_options(char * cluster_name,int idx_option1,char * option1,int idx_option2,char * option2)2598 int check_printer_with_options(char* cluster_name, int idx_option1,
2599 char* option1, int idx_option2, char* option2)
2600 {
2601 remote_printer_t *p;
2602 cups_array_t *first_attributes_value;
2603 cups_array_t *second_attributes_value;
2604 char *borderless_pagesize = NULL;
2605 int option1_is_size = 0, option2_is_size = 0;
2606 unsigned long int max_length = 0, option1_len = 0, option2_len = 0, t_len = 0;
2607 char t[] = ".Borderless";
2608
2609 t_len = strlen(t);
2610 if (option1)
2611 option1_len = strlen(option1);
2612 if (option2)
2613 option2_len = strlen(option2);
2614
2615 /* Seems to be possible to have both options...*/
2616 max_length = option1_len + option2_len + (2 * t_len) + 1;
2617
2618 borderless_pagesize = (char *)malloc(sizeof(char) * max_length);
2619 if (borderless_pagesize == NULL)
2620 {
2621 debug_printf("check_printer_with_options: Run out of memory.\n");
2622 return 0;
2623 }
2624 memset(borderless_pagesize, 0, max_length);
2625
2626 if (!strcmp(ppd_keywords[idx_option1], "PageSize") ||
2627 !strcmp(ppd_keywords[idx_option1], "PageRegion")) {
2628 /* Check that we are generating .Borderless for the correct size, i.e We
2629 are generating 4x5.Borderless for 4x5 and not generating
2630 4x5.Borderless.Borderless for 4x5.Borderless */
2631 if (option1_len >= 11 &&
2632 !strcmp(&option1[option1_len - t_len], t))
2633 ;
2634 else {
2635 strcat(borderless_pagesize, option1);
2636 strcat(borderless_pagesize, t);
2637 option1_is_size = 1;
2638 }
2639 }
2640 if (!strcmp(ppd_keywords[idx_option2], "PageSize") ||
2641 !strcmp(ppd_keywords[idx_option2], "PageRegion")) {
2642 if(option2_len >=11 &&
2643 !strcmp(&option2[option2_len - t_len], t))
2644 ;
2645 else {
2646 strcat(borderless_pagesize, option2);
2647 strcat(borderless_pagesize, t);
2648 option2_is_size = 1;
2649 }
2650 }
2651 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2652 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2653 if(strcmp(cluster_name, p->queue_name))
2654 continue;
2655 first_attributes_value = get_supported_options(p->prattrs,
2656 ppd_keywords[idx_option1]);
2657 if(cupsArrayFind(first_attributes_value, (void*)option1) ||
2658 (option1_is_size && cupsArrayFind(first_attributes_value,
2659 (void*)borderless_pagesize))) {
2660 second_attributes_value =
2661 get_supported_options(p->prattrs,
2662 ppd_keywords[idx_option2]);
2663 if (cupsArrayFind(second_attributes_value,(void*)option2) ||
2664 (option2_is_size && cupsArrayFind(second_attributes_value,
2665 (void*)borderless_pagesize)))
2666 {
2667 free(borderless_pagesize);
2668 return 1;
2669 }
2670 }
2671 }
2672 free(borderless_pagesize);
2673 return 0;
2674 }
2675
2676 /* The function returns a array containint the sizes supported by the cluster*/
get_cluster_sizes(char * cluster_name)2677 cups_array_t* get_cluster_sizes(char* cluster_name)
2678 {
2679 cups_array_t *sizes = NULL;
2680 cups_array_t *cluster_sizes = NULL,
2681 *sizes_ppdname;
2682 cups_size_t *size;
2683 remote_printer_t *p;
2684 ipp_attribute_t *defattr;
2685 char ppdname[41], pagesize[128];
2686 char* first_space;
2687 int min_length, min_width, max_length, max_width,
2688 bottom, left, right, top;
2689
2690 cluster_sizes = cupsArrayNew3((cups_array_func_t)pwg_compare_sizes,
2691 NULL, NULL, 0,
2692 (cups_acopy_func_t)pwg_copy_size,
2693 (cups_afree_func_t)free);
2694 sizes_ppdname = cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2695 (cups_acopy_func_t)strdup,
2696 (cups_afree_func_t)free);
2697 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2698 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2699 if (!strcmp(p->queue_name, cluster_name)) {
2700 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
2701 p->status == STATUS_TO_BE_RELEASED )
2702 continue;
2703 defattr = NULL;
2704 min_length = INT_MAX;
2705 min_width = INT_MAX;
2706 max_length = 0;
2707 max_width = 0;
2708 bottom = 0;
2709 left = 0;
2710 right = 0;
2711 top = 0;
2712 sizes = generate_sizes(p->prattrs, &defattr, &min_length, &min_width,
2713 &max_length, &max_width,
2714 &bottom, &left, &right, &top, ppdname);
2715 for (size = (cups_size_t *)cupsArrayFirst(sizes);
2716 size; size = (cups_size_t *)cupsArrayNext(sizes)) {
2717 if (!cupsArrayFind(cluster_sizes, size)) {
2718 strcpy(pagesize, size->media);
2719 if ((first_space = strchr(pagesize, ' ')) != NULL) {
2720 *first_space = '\0';
2721 }
2722 if (!cupsArrayFind(sizes_ppdname, pagesize)) {
2723 cupsArrayAdd(cluster_sizes, size);
2724 cupsArrayAdd(sizes_ppdname, pagesize);
2725 }
2726 }
2727 }
2728
2729 cupsArrayDelete(sizes);
2730 sizes = NULL;
2731 }
2732 }
2733
2734 cupsArrayDelete(sizes_ppdname);
2735
2736 return cluster_sizes;
2737 }
2738
2739 /* generate_cluster_conflicts - Function generates conflicts for the cluster*/
generate_cluster_conflicts(char * cluster_name,ipp_t * merged_attributes)2740 cups_array_t* generate_cluster_conflicts(char* cluster_name,
2741 ipp_t *merged_attributes)
2742 {
2743 remote_printer_t *p;
2744 cups_array_t *conflict_pairs = NULL;
2745 int i, k, j, no_of_printers = 0, no_of_ppd_keywords;
2746 cups_array_t *printer_first_options = NULL,
2747 *printer_second_options = NULL;
2748 char *opt1, *opt2, constraint[100], *ppdsizename, *temp;
2749 cups_array_t *sizes = NULL, *pagesizes;
2750 cups_size_t *size;
2751
2752 /* Cups Array to store the conflicts*/
2753 ppdsizename = (char *)malloc(sizeof(char) * 128);
2754 if ((conflict_pairs = cupsArrayNew3((cups_array_func_t)strcasecmp,
2755 NULL, NULL, 0,
2756 (cups_acopy_func_t)strdup,
2757 (cups_afree_func_t)free)) == NULL)
2758 return NULL;
2759
2760 /* Storing all the values supported by the cluster in cluster_options*/
2761 no_of_ppd_keywords = sizeof(ppd_keywords) / sizeof(ppd_keywords[0]);
2762 cups_array_t *cluster_options[no_of_ppd_keywords];
2763 for(i = 0; i < no_of_ppd_keywords; i ++) {
2764 if (strcmp(ppd_keywords[i], "PageSize") &&
2765 strcmp(ppd_keywords[i], "PageRegion"))
2766 cluster_options[i] =
2767 get_supported_options(merged_attributes,ppd_keywords[i]);
2768 else {
2769 sizes = get_cluster_sizes(cluster_name);
2770 if ((pagesizes =
2771 cupsArrayNew3((cups_array_func_t)strcasecmp, NULL, NULL, 0,
2772 (cups_acopy_func_t)strdup,
2773 (cups_afree_func_t)free)) == NULL)
2774 return NULL;
2775 for (size = (cups_size_t *)cupsArrayFirst(sizes); size;
2776 size = (cups_size_t *)cupsArrayNext(sizes)) {
2777 strcpy(ppdsizename, size->media);
2778 if ((temp = strchr(ppdsizename, ' ')) != NULL)
2779 *temp = '\0';
2780 cupsArrayAdd(pagesizes, ppdsizename);
2781 }
2782 cluster_options[i] = pagesizes;
2783
2784 cupsArrayDelete(sizes);
2785 sizes = NULL;
2786 }
2787 }
2788
2789 /* Algorithm to find constraints: We iterate over printer, if we
2790 find a value for a keyword which is supported by the cluster but
2791 not by the printer, that value can be part of the conflict. With
2792 this value v and a new value (for an different keyword, at index
2793 more than the index of first keyword), we generate a pair (v,u)
2794 and then we check whether some printer satisfy this pair, if no
2795 such printer exists then the pair is a conflict, we add it to
2796 conflict_pairs array */
2797
2798 no_of_printers = cupsArrayCount(remote_printers);
2799 for (j = 0; j < no_of_printers; j ++) {
2800 p = (remote_printer_t *)cupsArrayIndex(remote_printers, j);
2801 if (strcmp(cluster_name, p->queue_name))
2802 continue;
2803 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
2804 p->status == STATUS_TO_BE_RELEASED )
2805 continue;
2806 for (i = 0; i < no_of_ppd_keywords; i ++) {
2807 printer_first_options =
2808 get_supported_options(p->prattrs, ppd_keywords[i]);
2809 if (i == 0)
2810 for (opt1 = cupsArrayFirst(cluster_options[i]); opt1;
2811 opt1 = cupsArrayNext(cluster_options[i])) {
2812 if (cupsArrayFind(printer_first_options, opt1))
2813 continue;
2814 for (k = i + 1; k < no_of_ppd_keywords; k++) {
2815 if (!strcmp(ppd_keywords[i], "PageSize") &&
2816 !strcmp(ppd_keywords[k], "PageRegion"))
2817 continue;
2818 printer_second_options = get_supported_options(p->prattrs,
2819 ppd_keywords[k]);
2820 for(opt2 = cupsArrayFirst(printer_second_options); opt2;
2821 opt2 = cupsArrayNext(printer_second_options)) {
2822 if (check_printer_with_options(cluster_name, i, opt1, k, opt2))
2823 continue;
2824 if (!strcasecmp(opt1, AUTO_OPTION) ||
2825 !strcasecmp(opt2, AUTO_OPTION))
2826 continue;
2827 if (!strcmp(opt1, "Gray") || !strcmp(opt2, "Gray"))
2828 continue;
2829 sprintf(constraint, "*UIConstraints: *%s %s *%s %s\n",
2830 ppd_keywords[i],
2831 opt1,ppd_keywords[k], opt2);
2832 if (!cupsArrayFind(conflict_pairs, constraint)) {
2833 cupsArrayAdd(conflict_pairs, constraint);
2834 }
2835 sprintf(constraint, "*UIConstraints: *%s %s *%s %s\n",
2836 ppd_keywords[k],
2837 opt2, ppd_keywords[i], opt1);
2838 if (!cupsArrayFind(conflict_pairs, constraint)) {
2839 cupsArrayAdd(conflict_pairs, constraint);
2840 }
2841 }
2842
2843 cupsArrayDelete(printer_second_options);
2844 printer_second_options = NULL;
2845 }
2846 }
2847
2848 cupsArrayDelete(printer_first_options);
2849 printer_first_options = NULL;
2850 }
2851 }
2852
2853 for(i = 0; i < no_of_ppd_keywords; i ++) {
2854 cupsArrayDelete(cluster_options[i]);
2855 }
2856
2857 free(ppdsizename);
2858 return conflict_pairs;
2859 }
2860
2861 /*get_cluster_attributes - Returns ipp_t* containing the options supplied by
2862 all the printers in the cluster, which can be sent
2863 to ppdCreateFromIPP2() to generate the ppd file */
get_cluster_attributes(char * cluster_name)2864 ipp_t* get_cluster_attributes(char* cluster_name)
2865 {
2866 remote_printer_t *p;
2867 ipp_t *merged_attributes = NULL;
2868 char printer_make_and_model[256];
2869 ipp_attribute_t *attr;
2870 int color_supported = 0, make_model_done = 0, i;
2871 char valuebuffer[65536];
2872 merged_attributes = ippNew();
2873 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2874 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2875 if (strcmp(cluster_name, p->queue_name))
2876 continue;
2877 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
2878 p->status == STATUS_TO_BE_RELEASED )
2879 continue;
2880 if (!make_model_done) {
2881 strcpy(printer_make_and_model, "Cluster ");
2882 strcat(printer_make_and_model, cluster_name);
2883 make_model_done = 1;
2884 }
2885 if (((attr = ippFindAttribute(p->prattrs, "color-supported",
2886 IPP_TAG_BOOLEAN)) != NULL &&
2887 ippGetBoolean(attr, 0)))
2888 color_supported = 1;
2889 }
2890
2891 ippAddString(merged_attributes, IPP_TAG_PRINTER, IPP_TAG_TEXT,
2892 "printer-make-and-model",
2893 NULL, printer_make_and_model);
2894 ippAddBoolean(merged_attributes, IPP_TAG_PRINTER, "color-supported",
2895 color_supported);
2896
2897 add_keyword_attributes(cluster_name, &merged_attributes);
2898 add_mimetype_attributes(cluster_name, &merged_attributes);
2899 add_tagzero_attributes(cluster_name, &merged_attributes);
2900 add_enum_attributes(cluster_name, &merged_attributes);
2901 add_resolution_attributes(cluster_name, &merged_attributes);
2902 add_margin_attributes(cluster_name, &merged_attributes);
2903 add_mediasize_attributes(cluster_name, &merged_attributes);
2904 add_mediadatabase_attributes(cluster_name, &merged_attributes);
2905 add_jobpresets_attribute(cluster_name, &merged_attributes);
2906 attr = ippFirstAttribute(merged_attributes);
2907 /* Printing merged attributes*/
2908 debug_printf("Merged attributes for the cluster %s : \n", cluster_name);
2909 while (attr) {
2910 debug_printf(" Attr: %s\n",
2911 ippGetName(attr));
2912 ippAttributeString(attr, valuebuffer, sizeof(valuebuffer));
2913 debug_printf(" Value: %s\n", valuebuffer);
2914 const char *kw;
2915 for (i = 0; i < ippGetCount(attr); i ++)
2916 if ((kw = ippGetString(attr, i, NULL)) != NULL)
2917 debug_printf(" Keyword: %s\n", kw);
2918 attr = ippNextAttribute(merged_attributes);
2919 }
2920 return merged_attributes;
2921 }
2922
cluster_supports_given_attribute(char * cluster_name,ipp_tag_t tag,const char * attribute)2923 int cluster_supports_given_attribute(char* cluster_name, ipp_tag_t tag,
2924 const char* attribute)
2925 {
2926 remote_printer_t *p;
2927 ipp_attribute_t *attr;
2928 int count;
2929
2930 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2931 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2932 if (strcmp(cluster_name, p->queue_name))
2933 continue;
2934 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
2935 p->status == STATUS_TO_BE_RELEASED )
2936 continue;
2937 if ((attr = ippFindAttribute(p->prattrs, attribute, tag)) != NULL &&
2938 (count = ippGetCount(attr)) > 1)
2939 return 1;
2940 }
2941 return 0;
2942 }
2943
2944 /* Generating the default values for the cluster*/
get_cluster_default_attributes(ipp_t ** merged_attributes,char * cluster_name,char * default_pagesize,const char ** default_color)2945 void get_cluster_default_attributes(ipp_t** merged_attributes,
2946 char* cluster_name,
2947 char* default_pagesize,
2948 const char **default_color)
2949 {
2950 int max_pages_per_min = 0, pages_per_min;
2951 remote_printer_t *p, *def_printer = NULL;
2952 int i, count;
2953 ipp_attribute_t *attr, *media_attr, *media_col_default, *defattr;
2954 ipp_t *media_col,
2955 *media_size, *current_media=NULL;
2956 char media_source[32], media_type[32];
2957 const char *str;
2958 media_col_t *temp;
2959 const char *keyword;
2960 res_t *res;
2961 int xres, yres;
2962 int min_length = INT_MAX, min_width = INT_MAX,
2963 max_length = 0, max_width = 0,
2964 bottom, left, right, top;
2965 char ppdname[41];
2966 cups_array_t *sizes;
2967
2968 /*The printer with the maximum Throughtput(pages_per_min) is selected as
2969 the default printer*/
2970 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2971 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2972 if (strcmp(p->queue_name, cluster_name))
2973 continue;
2974 if(p->status == STATUS_DISAPPEARED || p->status == STATUS_UNCONFIRMED ||
2975 p->status == STATUS_TO_BE_RELEASED )
2976 continue;
2977 if ((attr = ippFindAttribute (p->prattrs, "pages-per-minute",
2978 IPP_TAG_INTEGER)) != NULL) {
2979 pages_per_min = ippGetInteger (attr, 0);
2980 if (pages_per_min > max_pages_per_min) {
2981 max_pages_per_min = pages_per_min;
2982 def_printer = p;
2983 }
2984 }
2985 }
2986
2987 /* If none of the printer in the cluster has "pages-per-minute" in the ipp
2988 response message, then select the first printer in the cluster */
2989 if (!def_printer) {
2990 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
2991 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
2992 if (strcmp(p->queue_name, cluster_name))
2993 continue;
2994 else {
2995 def_printer = p;
2996 break;
2997 }
2998 }
2999 }
3000
3001 debug_printf("Selecting printer (%s) as the default for the cluster %s\n",
3002 def_printer->uri, cluster_name);
3003 debug_printf("Default Attributes of the cluster %s are : \n", cluster_name);
3004
3005 /* Generating the default pagesize for the cluster*/
3006 sizes = generate_sizes(def_printer->prattrs, &defattr, &min_length,
3007 &min_width, &max_length, &max_width,
3008 &bottom, &left, &right, &top, ppdname);
3009 strcpy(default_pagesize, ppdname);
3010 debug_printf("Default PageSize : %s\n", default_pagesize);
3011
3012 /* Generating the default media-col for the cluster*/
3013 if ((attr = ippFindAttribute(def_printer->prattrs, "media-col-default",
3014 IPP_TAG_BEGIN_COLLECTION)) != NULL) {
3015 media_col = ippGetCollection(attr, 0);
3016 media_size = ippGetCollection(ippFindAttribute(media_col, "media-size",
3017 IPP_TAG_BEGIN_COLLECTION),
3018 0);
3019 temp = (media_col_t *)malloc(sizeof(media_col_t));
3020 temp->x = ippGetInteger(ippFindAttribute(media_size, "x-dimension",
3021 IPP_TAG_ZERO), 0);
3022 temp->y = ippGetInteger(ippFindAttribute(media_size, "y-dimension",
3023 IPP_TAG_ZERO), 0);
3024 temp->top_margin = ippGetInteger(ippFindAttribute(media_col,
3025 "media-top-margin",
3026 IPP_TAG_INTEGER), 0);
3027 temp->bottom_margin = ippGetInteger(ippFindAttribute(media_col,
3028 "media-bottom-margin",
3029 IPP_TAG_INTEGER), 0);
3030 temp->left_margin = ippGetInteger(ippFindAttribute(media_col,
3031 "media-left-margin",
3032 IPP_TAG_INTEGER), 0);
3033 temp->right_margin = ippGetInteger(ippFindAttribute(media_col,
3034 "media-right-margin",
3035 IPP_TAG_INTEGER), 0);
3036 media_type[0] = '\0';
3037 media_source[0] = '\0';
3038 temp->media_source = NULL;
3039 temp->media_type = NULL;
3040
3041 if ((media_attr = ippFindAttribute(media_col, "media-type",
3042 IPP_TAG_KEYWORD)) != NULL)
3043 pwg_ppdize_name(ippGetString(media_attr, 0, NULL), media_type,
3044 sizeof(media_type));
3045
3046 if (strlen(media_type) > 1) {
3047 temp->media_type = (char*)malloc(sizeof(char)*32);
3048 strcpy(temp->media_type, media_type);
3049 debug_printf("Default MediaType: %s\n", media_type);
3050 }
3051
3052 if (temp->media_type == NULL) {
3053 if (cluster_supports_given_attribute(cluster_name, IPP_TAG_KEYWORD,
3054 "media-type-supported")) {
3055 temp->media_type = (char*)malloc(sizeof(char)*32);
3056 strcpy(temp->media_type, AUTO_OPTION);
3057 debug_printf("Default MediaType: " AUTO_OPTION "\n");
3058 }
3059 }
3060
3061 if ((media_attr = ippFindAttribute(media_col, "media-source",
3062 IPP_TAG_KEYWORD)) != NULL) {
3063 pwg_ppdize_name(ippGetString(media_attr, 0, NULL), media_source,
3064 sizeof(media_source));
3065 }
3066
3067 if (strlen(media_source) > 1) {
3068 temp->media_source = (char*)malloc(sizeof(char)*32);
3069 strcpy(temp->media_source, media_source);
3070 debug_printf("Default MediaSource: %s\n", media_source);
3071 }
3072
3073 if (temp->media_source == NULL) {
3074 if (cluster_supports_given_attribute(cluster_name, IPP_TAG_KEYWORD,
3075 "media-source-supported")) {
3076 temp->media_source = (char*)malloc(sizeof(char) * 32);
3077 strcpy(temp->media_source, AUTO_OPTION);
3078 debug_printf("Default MediaSource: " AUTO_OPTION "\n");
3079 }
3080 }
3081
3082 media_col_default = ippAddCollection(*merged_attributes, IPP_TAG_PRINTER,
3083 "media-col-default", NULL);
3084 current_media = create_media_col(temp->x, temp->y, temp->left_margin,
3085 temp->right_margin, temp->top_margin,
3086 temp->bottom_margin,
3087 temp->media_source, temp->media_type);
3088 ippSetCollection(*merged_attributes, &media_col_default, 0, current_media);
3089
3090 free(temp->media_source);
3091 free(temp->media_type);
3092 free(temp);
3093 ippDelete(current_media);
3094 }
3095
3096 /*Finding the default colormodel for the cluster*/
3097 if ((attr = ippFindAttribute(def_printer->prattrs, "urf-supported",
3098 IPP_TAG_KEYWORD)) == NULL)
3099 if ((attr = ippFindAttribute(def_printer->prattrs,
3100 "pwg-raster-document-type-supported",
3101 IPP_TAG_KEYWORD)) == NULL)
3102 if ((attr = ippFindAttribute(def_printer->prattrs,
3103 "print-color-mode-supported",
3104 IPP_TAG_KEYWORD)) == NULL)
3105 attr = ippFindAttribute(def_printer->prattrs, "output-mode-supported",
3106 IPP_TAG_KEYWORD);
3107
3108 if (attr && ippGetCount(attr) > 0) {
3109 *default_color = NULL;
3110 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
3111 keyword = ippGetString(attr, i, NULL);
3112 if ((!strcasecmp(keyword, "black_1") ||
3113 !strcmp(keyword, "bi-level") ||
3114 !strcmp(keyword, "process-bi-level"))) {
3115 if (!*default_color)
3116 *default_color = "FastGray";
3117 } else if ((!strcasecmp(keyword, "sgray_8") ||
3118 !strncmp(keyword, "W8", 2) ||
3119 !strcmp(keyword, "monochrome") ||
3120 !strcmp(keyword, "process-monochrome"))) {
3121 if (!*default_color || !strcmp(*default_color, "FastGray"))
3122 *default_color = "Gray";
3123 } else if (!strcasecmp(keyword, "sgray_16") ||
3124 !strncmp(keyword, "W8-16", 5) ||
3125 !strncmp(keyword, "W16", 3)) {
3126 if (!*default_color || !strcmp(*default_color, "FastGray"))
3127 *default_color = "Gray16";
3128 } else if (!strcasecmp(keyword, "srgb_8") ||
3129 !strncmp(keyword, "SRGB24", 6) ||
3130 !strcmp(keyword, "color")) {
3131 *default_color = "RGB";
3132 } else if ((!strcasecmp(keyword, "srgb_16") ||
3133 !strncmp(keyword, "SRGB48", 6)) &&
3134 !ippContainsString(attr, "srgb_8")) {
3135 *default_color = "RGB";
3136 } else if (!strcasecmp(keyword, "adobe-rgb_16") ||
3137 !strncmp(keyword, "ADOBERGB48", 10) ||
3138 !strncmp(keyword, "ADOBERGB24-48", 13)) {
3139 if (!*default_color)
3140 *default_color = "AdobeRGB";
3141 } else if ((!strcasecmp(keyword, "adobe-rgb_8") ||
3142 !strcmp(keyword, "ADOBERGB24")) &&
3143 !ippContainsString(attr, "adobe-rgb_16")) {
3144 if (!*default_color)
3145 *default_color = "AdobeRGB";
3146 }
3147 }
3148 if (*default_color)
3149 debug_printf("Default ColorModel : %s\n", *default_color);
3150 }
3151
3152 if ((attr = ippFindAttribute(def_printer->prattrs, "output-bin-default",
3153 IPP_TAG_ZERO)) != NULL) {
3154 str = ippGetString(attr, 0, NULL);
3155 ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3156 "output-bin-default", NULL, str);
3157 debug_printf("Default OutputBin: %s\n", str);
3158 } else {
3159 if (cluster_supports_given_attribute(cluster_name,IPP_TAG_ZERO,
3160 "output-bin-supported")) {
3161 ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3162 "output-bin-default", NULL, AUTO_OPTION);
3163 debug_printf("Default OutputBin: %s\n", AUTO_OPTION);
3164 }
3165 }
3166
3167 if ((attr = ippFindAttribute(def_printer->prattrs,
3168 "print-content-optimize-default",
3169 IPP_TAG_ZERO)) != NULL) {
3170 str = ippGetString(attr, 0, NULL);
3171 ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3172 "print-content-optimize-default", NULL, str);
3173 debug_printf("Default print-content-optimize: %s\n", str);
3174 } else {
3175 if (cluster_supports_given_attribute(cluster_name, IPP_TAG_ZERO,
3176 "print-content-optimize-default")) {
3177 ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3178 "print-content-optimize-default", NULL, AUTO_OPTION);
3179 debug_printf("Default print-content-optimize: %s\n", AUTO_OPTION);
3180 }
3181 }
3182
3183 if ((attr = ippFindAttribute(def_printer->prattrs,
3184 "print-rendering-intent-default",
3185 IPP_TAG_ZERO)) != NULL) {
3186 str = ippGetString(attr, 0, NULL);
3187 ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3188 "print-rendering-intent-default", NULL, str);
3189 debug_printf("Default print-rendering-intent: %s\n", str);
3190 } else {
3191 if (cluster_supports_given_attribute(cluster_name, IPP_TAG_ZERO,
3192 "print-rendering-intent-default")) {
3193 ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3194 "print-rendering-intent-default", NULL, AUTO_OPTION);
3195 debug_printf("Default print-rendering-intent: %s\n", AUTO_OPTION);
3196 }
3197 }
3198
3199 if ((attr = ippFindAttribute(def_printer->prattrs, "print-scaling-default",
3200 IPP_TAG_ZERO)) != NULL) {
3201 str = ippGetString(attr, 0, NULL);
3202 ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3203 "print-scaling-default", NULL, str);
3204 debug_printf("Default print-scaling: %s\n",str);
3205 } else {
3206 if (cluster_supports_given_attribute(cluster_name, IPP_TAG_ZERO,
3207 "print-scaling-default")) {
3208 ippAddString(*merged_attributes, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
3209 "print-scaling-default", NULL, AUTO_OPTION);
3210 debug_printf("Default print-scaling: %s\n", AUTO_OPTION);
3211 }
3212 }
3213
3214 if ((attr = ippFindAttribute(def_printer->prattrs,
3215 "printer-resolution-default",
3216 IPP_TAG_ZERO)) != NULL) {
3217 if ((res = ippResolutionToRes(attr, 0)) != NULL) {
3218 xres = res->x;
3219 yres = res->y;
3220 ippAddResolution(*merged_attributes, IPP_TAG_PRINTER,
3221 "printer-resolution-default",
3222 IPP_RES_PER_INCH, xres, yres);
3223 debug_printf("Default Resolution : %dx%d\n", xres, yres);
3224 free_resolution(res, NULL);
3225 }
3226 }
3227
3228 cupsArrayDelete(sizes);
3229 }
3230
3231 /* Function to see which printer in the cluster supports the
3232 requested job attributes*/
supports_job_attributes_requested(const gchar * printer,int printer_index,int job_id,int * print_quality)3233 int supports_job_attributes_requested(const gchar* printer, int printer_index,
3234 int job_id, int *print_quality)
3235 {
3236 char uri[1024];
3237 http_t *http = NULL;
3238 ipp_attribute_t *attr, *attr1;
3239 ipp_t *request, *response = NULL;
3240 const char *str, *side, *resource;
3241 cups_array_t *job_sheet_supported = NULL,
3242 *multiple_doc_supported = NULL, *print_qualities = NULL,
3243 *media_type_supported = NULL, *staplelocation_supported = NULL,
3244 *foldtype_supported = NULL, *punchmedia_supported = NULL,
3245 *color_supported = NULL;
3246 remote_printer_t *p;
3247 int i, count, side_found, orien_req, orien,
3248 orien_found;
3249 cups_array_t *sizes = NULL;
3250 int ret = 1;
3251
3252 p = (remote_printer_t *)cupsArrayIndex(remote_printers, printer_index);
3253 static const char * const jattrs[] = /* Job attributes we want */
3254 {
3255 "all"
3256 };
3257
3258 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3259 "localhost", 0, "/printers/%s", printer);
3260
3261 /* Getting the resource */
3262 resource = uri + (strlen(uri) - strlen(printer) - 10);
3263
3264 http = http_connect_local();
3265 request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES);
3266 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
3267 uri);
3268 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
3269 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
3270 "requesting-user-name", NULL, cupsUser());
3271 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3272 "requested-attributes",
3273 (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
3274
3275 response = cupsDoRequest(http, request,resource);
3276 attr = ippFirstAttribute(response);
3277
3278 /* Document Format */
3279 /*
3280 if ((attr = ippFindAttribute(response, "document-format-detected",
3281 IPP_TAG_MIMETYPE)) != NULL &&
3282 ippGetCount(attr) > 0) {
3283 str = ippGetString(attr, 0, NULL);
3284 debug_printf("The job-document is of the format %s\n.",str);
3285 formats_supported = get_mimetype_attributes(p->prattrs);
3286 if(!cupsArrayFind(formats_supported, (void *)str)){
3287 debug_printf("Printer %s doesn't support the document format %s\n",
3288 printer, str);
3289 return 0;
3290 }
3291 }
3292 */
3293
3294 /* Job Sheets */
3295 if ((attr = ippFindAttribute(response, "job-sheets",
3296 IPP_TAG_ZERO)) != NULL &&
3297 ippGetCount(attr) > 0) {
3298 str = ippGetString(attr, 0, NULL);
3299 debug_printf("The job-sheets %s is requested for the job\n", str);
3300 job_sheet_supported = get_supported_options(p->prattrs,
3301 "job-sheets-supported");
3302 if (str) {
3303 if (!cupsArrayFind(job_sheet_supported, (void *)str) &&
3304 strcasecmp(str,"none")) {
3305 debug_printf("Printer %s doesn't support the job-sheet %s\n", printer,
3306 str);
3307 ret = 0;
3308 goto cleanup;
3309 }
3310 }
3311 }
3312
3313 /* Multiple document handling */
3314 /* Can't get multiple-document-handling data from job templates */
3315 if ((attr = ippFindAttribute(response, "multiple-document-handling",
3316 IPP_TAG_ZERO)) != NULL && ippGetCount(attr)>0) {
3317 str = ippGetString(attr, 0, NULL);
3318 debug_printf("The multiple-document-handling type %s is requested\n", str);
3319 if (str) {
3320 multiple_doc_supported =
3321 get_supported_options(p->prattrs,
3322 "multiple-document-handling-supported");
3323 if (!cupsArrayFind(multiple_doc_supported, (void *)str)) {
3324 debug_printf("Printer %s doesn't support the multiple document handling option %s\n",
3325 printer, str);
3326 ret = 0;
3327 goto cleanup;
3328 }
3329 }
3330 }
3331
3332 /* Media Type */
3333 if ((attr = ippFindAttribute(response, "MediaType",
3334 IPP_TAG_ZERO)) != NULL &&
3335 ippGetCount(attr) > 0) {
3336 str = ippGetString(attr, 0, NULL);
3337 debug_printf("The mediatype %s is requested for the job\n", str);
3338 if (str != NULL) {
3339 media_type_supported = get_supported_options(p->prattrs,
3340 "media-type-supported");
3341 if (!cupsArrayFind(media_type_supported, (void *)str) &&
3342 strcasecmp(str, AUTO_OPTION)) {
3343 debug_printf("Printer %s doesn't support the media-type %s\n",
3344 printer, str);
3345 ret = 0;
3346 goto cleanup;
3347 }
3348 }
3349 }
3350
3351 /* Staple Location*/
3352 if ((attr = ippFindAttribute(response, "StapleLocation",
3353 IPP_TAG_ZERO)) != NULL &&
3354 ippGetCount(attr) > 0) {
3355 str = ippGetString(attr, 0, NULL);
3356 debug_printf("The staple location %s is requested for the job\n", str);
3357 if (str != NULL) {
3358 staplelocation_supported =
3359 get_supported_options(p->prattrs, "StapleLocation");
3360 if (!cupsArrayFind(staplelocation_supported, (void *)str) &&
3361 strcasecmp(str, "None")) {
3362 debug_printf("Printer %s doesn't support the staple location %s\n",
3363 printer, str);
3364 ret = 0;
3365 goto cleanup;
3366 }
3367 }
3368 }
3369
3370 /* FoldType */
3371 if ((attr = ippFindAttribute(response, "FoldType",
3372 IPP_TAG_ZERO)) != NULL &&
3373 ippGetCount(attr) > 0) {
3374 str = ippGetString(attr, 0, NULL);
3375 debug_printf("The FoldType %s is requested for the job\n", str);
3376 if (str != NULL) {
3377 foldtype_supported = get_supported_options(p->prattrs, "FoldType");
3378 if (!cupsArrayFind(foldtype_supported, (void *)str) &&
3379 strcasecmp(str, "None")) {
3380 debug_printf("Printer %s doesn't support the FoldType %s\n",
3381 printer, str);
3382 ret = 0;
3383 goto cleanup;
3384 }
3385 }
3386 }
3387
3388 /* PunchMedia */
3389 if ((attr = ippFindAttribute(response, "PunchMedia",
3390 IPP_TAG_ZERO)) != NULL &&
3391 ippGetCount(attr) > 0) {
3392 str = ippGetString(attr, 0, NULL);
3393 debug_printf("The PunchMedia %s is requested for the job\n", str);
3394 if (str != NULL) {
3395 punchmedia_supported = get_supported_options(p->prattrs, "PunchMedia");
3396 if (!cupsArrayFind(punchmedia_supported, (void *)str) &&
3397 strcasecmp(str, "none")) {
3398 debug_printf("Printer %s doesn't support the PunchMedia %s\n",
3399 printer, str);
3400 ret = 0;
3401 goto cleanup;
3402 }
3403 }
3404 }
3405
3406 /* ColorModel */
3407 if ((attr = ippFindAttribute(response, "ColorModel",
3408 IPP_TAG_ZERO)) != NULL &&
3409 ippGetCount(attr) > 0) {
3410 str = ippGetString(attr, 0, NULL);
3411 debug_printf("The ColorModel %s is requested for the job\n", str);
3412 if (str != NULL) {
3413 color_supported = get_supported_options(p->prattrs, "ColorModel");
3414 if (!cupsArrayFind(color_supported, (void *)str) &&
3415 strcasecmp(str,"Gray")) {
3416 debug_printf("Printer %s doesn't support the ColorModel %s\n",
3417 printer, str);
3418 ret = 0;
3419 goto cleanup;
3420 }
3421 }
3422 }
3423
3424 /* Sides supported */
3425 if ((attr = ippFindAttribute(response, "Duplex",
3426 IPP_TAG_ZERO)) != NULL) {
3427 side_found = 0;
3428 str = ippGetString(attr, 0, NULL);
3429 if (str) {
3430 if ((attr1 = ippFindAttribute(p->prattrs, "sides-supported",
3431 IPP_TAG_KEYWORD)) != NULL) {
3432 for (i = 0, count = ippGetCount(attr1); i < count; i++) {
3433 side = ippGetString(attr1, i, NULL);
3434 debug_printf("The duplex option %s is requested\n", side);
3435 if (!strcasecmp(str, "None") && !strcmp(side, "one-sided")) {
3436 side_found = 1;
3437 break;
3438 } else if (!strcmp(str, "DuplexNoTumble") &&
3439 !strcmp(side, "two-sided-long-edge")) {
3440 side_found = 1;
3441 break;
3442 } else if (!strcmp(str, "DuplexTumble") &&
3443 !strcmp(side, "two-sided-short-edge")) {
3444 side_found = 1;
3445 break;
3446 }
3447 }
3448 if (!side_found) {
3449 debug_printf("Printer %s doesn't support the required duplex options\n",
3450 printer);
3451 ret = 0;
3452 goto cleanup;
3453 }
3454 }
3455 }
3456 }
3457
3458 /* Orientation Requested */
3459 if ((attr = ippFindAttribute(response, "orientation-requested",
3460 IPP_TAG_ENUM)) != NULL) {
3461 orien_found = 0;
3462 orien_req = ippGetInteger(attr, 0);
3463 if ((attr1 = ippFindAttribute(p->prattrs,
3464 "orientation-requested-supported",
3465 IPP_TAG_ENUM)) != NULL) {
3466 for (i = 0, count = ippGetCount(attr1); i < count; i ++) {
3467 orien = ippGetInteger(attr1, i);
3468 if (orien == orien_req) {
3469 orien_found = 1;
3470 break;
3471 }
3472 }
3473 if (!orien_found) {
3474 debug_printf("Printer %s doesn't support the requested orientation\n",
3475 printer);
3476 ret = 0;
3477 goto cleanup;
3478 }
3479 }
3480 }
3481
3482 /* Page Size */
3483 if ((attr = ippFindAttribute(response, "PageSize",
3484 IPP_TAG_ZERO)) != NULL &&
3485 ippGetCount(attr) > 0) {
3486 str = ippGetString(attr, 0, NULL);
3487 if (str) {
3488 sizes = get_pagesize(p->prattrs);
3489 if (!cupsArrayFind(sizes, (void*)str)) {
3490 debug_printf("Printer %s doesn't support %s PageSize\n", p->uri, str);
3491 ret = 0;
3492 goto cleanup;
3493 }
3494 }
3495 }
3496
3497 /* Print Quality */
3498 *print_quality = 4;
3499 if ((attr = ippFindAttribute(response, "cupsPrintQuality",
3500 IPP_TAG_ZERO)) != NULL &&
3501 ippGetCount(attr) > 0) {
3502 print_qualities = get_supported_options(p->prattrs, "cupsPrintQuality");
3503 str = ippGetString(attr, 0, NULL);
3504 debug_printf("%s\n", str);
3505 if (str && !cupsArrayFind(print_qualities, (void*)str)) {
3506 debug_printf("In\n");
3507 if(!strcmp(str, "5"))
3508 *print_quality = 5;
3509 else if (!strcmp(str, "3"))
3510 *print_quality = 3;
3511 debug_printf("Printer doesn't support %s print quality\n",
3512 !strcmp(str, "5") ? "HIGH": "DRAFT");
3513 ret = 0;
3514 goto cleanup;
3515 }
3516 }
3517
3518 cleanup:
3519 if (response != NULL)
3520 ippDelete(response);
3521 if (job_sheet_supported != NULL)
3522 cupsArrayDelete(job_sheet_supported);
3523 if (multiple_doc_supported)
3524 cupsArrayDelete(multiple_doc_supported);
3525 if (media_type_supported != NULL)
3526 cupsArrayDelete(media_type_supported);
3527 if (staplelocation_supported != NULL)
3528 cupsArrayDelete(staplelocation_supported);
3529 if (foldtype_supported != NULL)
3530 cupsArrayDelete(foldtype_supported);
3531 if (punchmedia_supported != NULL)
3532 cupsArrayDelete(punchmedia_supported);
3533 if (color_supported != NULL)
3534 cupsArrayDelete(color_supported);
3535 if (print_qualities != NULL)
3536 cupsArrayDelete(print_qualities);
3537 if (sizes != NULL)
3538 cupsArrayDelete(sizes);
3539
3540 return ret;
3541 }
3542
3543
3544 /*
3545 * Remove all illegal characters and replace each group of such characters
3546 * by a single separator character (dash or underscore), return a free()-able
3547 * string.
3548 *
3549 * mode = 0: Only allow letters, numbers, dashes, and underscores for
3550 * turning make/model info into a valid print queue name or
3551 * into a string which can be supplied as option value in a
3552 * filter command line without need of quoting. Replace all
3553 * groups of illegal characters by single dashes and remove
3554 * leading and trailing dashes.
3555 * mode = 1: Allow also '/', '.', ',' for cleaning up MIME type
3556 * strings (here available Page Description Languages, PDLs) to
3557 * supply them on a filter command line without quoting.
3558 * Replace all groups of illegal characters by single dashes
3559 * and remove leading and trailing dashes.
3560 * mode = 2: Keep all locale-free alphanumeric characters (a-z, A-Z, 0-9)
3561 * and replace everything else by underscores. Replace all
3562 * groups of illegal characters by single underscores. This is
3563 * for generating print queue names from DNS-SD service names
3564 * to do it exactly as CUPS 2.2.x (or newer) does, so that CUPS
3565 * does not create its own temporary queues in addition.
3566 *
3567 * Especially this prevents from arbitrary code execution by interface scripts
3568 * generated for print queues to native IPP printers when a malicious IPP
3569 * print service with forged PDL and/or make/model info gets broadcasted into
3570 * the local network.
3571 */
3572
3573 char * /* O - Cleaned string */
remove_bad_chars(const char * str_orig,int mode)3574 remove_bad_chars(const char *str_orig, /* I - Original string */
3575 int mode) /* I - 0: Make/Model, queue name */
3576 /* 1: MIME types/PDLs */
3577 /* 2: Queue name from DNS-SD */
3578 /* service name */
3579 {
3580 int i, j;
3581 int havesep = 0;
3582 char sep, *str;
3583
3584 if (str_orig == NULL)
3585 return NULL;
3586
3587 str = strdup(str_orig);
3588
3589 /* for later str[strlen(str)-1] access */
3590 if (strlen(str) < 1)
3591 return str;
3592
3593 /* Select separator character */
3594 if (mode == 2)
3595 sep = '_';
3596 else
3597 sep = '-';
3598
3599 for (i = 0, j = 0; i < strlen(str); i++, j++) {
3600 if (((str[i] >= 'A') && (str[i] <= 'Z')) ||
3601 ((str[i] >= 'a') && (str[i] <= 'z')) ||
3602 ((str[i] >= '0') && (str[i] <= '9')) ||
3603 (mode != 2 && (str[i] == '_' ||
3604 str[i] == '.')) ||
3605 (mode == 1 && (str[i] == '/' ||
3606 str[i] == ','))) {
3607 /* Allowed character, keep it */
3608 havesep = 0;
3609 str[j] = str[i];
3610 } else {
3611 /* Replace all other characters by a single separator */
3612 if (havesep == 1)
3613 j --;
3614 else {
3615 havesep = 1;
3616 str[j] = sep;
3617 }
3618 }
3619 }
3620 /* Add terminating zero */
3621 str[j] = '\0';
3622
3623 i = 0;
3624 if (mode != 2) {
3625 /* Cut off trailing separators */
3626 while (strlen(str) > 0 && str[strlen(str)-1] == sep)
3627 str[strlen(str)-1] = '\0';
3628
3629 /* Cut off leading separators */
3630 while (str[i] == sep)
3631 ++i;
3632 }
3633
3634 /* keep a free()-able string. +1 for trailing \0 */
3635 return memmove(str, str + i, strlen(str) - i + 1);
3636 }
3637
3638
3639 static local_printer_t *
new_local_printer(const char * device_uri,const char * uuid,gboolean cups_browsed_controlled)3640 new_local_printer (const char *device_uri,
3641 const char *uuid,
3642 gboolean cups_browsed_controlled)
3643 {
3644 local_printer_t *printer = g_malloc (sizeof (local_printer_t));
3645 printer->device_uri = strdup (device_uri);
3646 printer->uuid = (char*)uuid;
3647 printer->cups_browsed_controlled = cups_browsed_controlled;
3648 return printer;
3649 }
3650
3651 static void
free_local_printer(gpointer data)3652 free_local_printer (gpointer data)
3653 {
3654 local_printer_t *printer = data;
3655 debug_printf("free_local_printer() in THREAD %ld\n", pthread_self());
3656 free (printer->device_uri);
3657 if (printer->uuid) free (printer->uuid);
3658 free (printer);
3659 }
3660
3661 static gboolean
local_printer_is_same_device(gpointer key,gpointer value,gpointer user_data)3662 local_printer_is_same_device (gpointer key,
3663 gpointer value,
3664 gpointer user_data)
3665 {
3666 local_printer_t *lprinter = value;
3667 remote_printer_t *p = user_data;
3668 char lhost[HTTP_MAX_URI], /* Local printer: Hostname */
3669 lresource[HTTP_MAX_URI], /* Local printer: Resource path */
3670 lscheme[32], /* Local printer: URI's scheme */
3671 lusername[64], /* Local printer: URI's username */
3672 *ltype = NULL, /* Local printer: If URI DNS-SD-based */
3673 *ldomain = NULL; /* pointers into lhost for components*/
3674 int lport = 0; /* Local printer: URI's port number */
3675
3676 debug_printf("local_printer_is_same_device() in THREAD %ld\n",
3677 pthread_self());
3678 if (!lprinter || !lprinter->device_uri || !p)
3679 return (0);
3680 /* Separate the local printer's URI into their components */
3681 memset(lscheme, 0, sizeof(lscheme));
3682 memset(lusername, 0, sizeof(lusername));
3683 memset(lhost, 0, sizeof(lhost));
3684 memset(lresource, 0, sizeof(lresource));
3685 httpSeparateURI(HTTP_URI_CODING_ALL, lprinter->device_uri,
3686 lscheme, sizeof(lscheme) - 1,
3687 lusername, sizeof(lusername) - 1,
3688 lhost, sizeof(lhost) - 1,
3689 &lport,
3690 lresource, sizeof(lresource) - 1);
3691 if ((ltype = strstr(lhost, "._ipp._tcp.")) != NULL ||
3692 (ltype = strstr(lhost, "._ipps._tcp.")) != NULL) {
3693 *ltype = '\0';
3694 ltype ++;
3695 ldomain = strchr(ltype + 9, '.');
3696 *ldomain = '\0';
3697 ldomain ++;
3698 if (*ldomain && ldomain[strlen(ldomain) - 1] == '.')
3699 ldomain[strlen(ldomain) - 1] = '\0';
3700 }
3701 /* Consider not only absolutely equal URIs as equal
3702 but alo URIs which differ only by use of IPP or
3703 IPPS and/or have the IPP standard port 631
3704 replaced by the HTTPS standard port 443, as this
3705 is common on network printers */
3706 return ((ltype && p->service_name && p->domain &&
3707 g_str_equal(lhost, p->service_name) &&
3708 !strncmp(ldomain, p->domain, strlen(ldomain))) ||
3709 (!ltype && p->host && p->resource &&
3710 (g_str_equal(lscheme, "ipp") || g_str_equal(lscheme, "ipps")) &&
3711 !lusername[0] &&
3712 g_str_equal(lhost, p->host) &&
3713 ((!p->port && (lport == 631 || lport == 443)) ||
3714 lport == p->port ||
3715 (lport == 631 && p->port == 443) ||
3716 (lport == 443 && p->port == 631)) &&
3717 g_str_equal(lresource, p->resource)));
3718 }
3719
3720 static gboolean
local_printer_has_uuid(gpointer key,gpointer value,gpointer user_data)3721 local_printer_has_uuid (gpointer key,
3722 gpointer value,
3723 gpointer user_data)
3724 {
3725 local_printer_t *printer = value;
3726 char *uuid = user_data;
3727
3728 debug_printf("local_printer_has_uuid() in THREAD %ld\n", pthread_self());
3729 return (printer != NULL && printer->uuid != NULL && uuid != NULL &&
3730 g_str_equal(printer->uuid, uuid));
3731 }
3732
3733 static gboolean
local_printer_service_name_matches(gpointer key,gpointer value,gpointer user_data)3734 local_printer_service_name_matches (gpointer key,
3735 gpointer value,
3736 gpointer user_data)
3737 {
3738 char *queue_name = key;
3739 char *service_name = user_data;
3740 char *p;
3741 debug_printf("local_printer_service_name_matches() in THREAD %ld\n",
3742 pthread_self());
3743 p = remove_bad_chars(service_name, 2);
3744 if (p && strncasecmp(p, queue_name, 63) == 0) {
3745 free(p);
3746 return TRUE;
3747 }
3748 if (p) free(p);
3749 return FALSE;
3750 }
3751
3752 static void
local_printers_create_subscription(http_t * conn)3753 local_printers_create_subscription (http_t *conn)
3754 {
3755 char temp[1024];
3756 if (!local_printers_context) {
3757 local_printers_context = g_malloc0 (sizeof (browsepoll_t));
3758 /* The httpGetAddr() function was introduced in CUPS 2.0.0 */
3759 #ifdef HAVE_CUPS_2_0
3760 local_printers_context->server =
3761 strdup(httpAddrString(httpGetAddress(conn),
3762 temp, sizeof(temp)));
3763 local_printers_context->port = httpAddrPort(httpGetAddress(conn));
3764 #else
3765 local_printers_context->server = cupsServer();
3766 local_printers_context->port = ippPort();
3767 #endif
3768 local_printers_context->can_subscribe = TRUE;
3769 }
3770
3771 browse_poll_create_subscription (local_printers_context, conn);
3772 }
3773
3774 int
add_dest_cb(dest_list_t * user_data,unsigned flags,cups_dest_t * dest)3775 add_dest_cb(dest_list_t *user_data, unsigned flags, cups_dest_t *dest)
3776 {
3777 if (flags & CUPS_DEST_FLAGS_REMOVED)
3778 /* Remove destination from array */
3779 user_data->num_dests =
3780 cupsRemoveDest(dest->name, dest->instance, user_data->num_dests,
3781 &(user_data->dests));
3782 else
3783 /* Add destination to array... */
3784 user_data->num_dests =
3785 cupsCopyDest(dest, user_data->num_dests,
3786 &(user_data->dests));
3787 return (1);
3788 }
3789
3790
3791 const char *
get_printer_uuid(http_t * http_printer,const char * raw_uri)3792 get_printer_uuid(http_t *http_printer,
3793 const char* raw_uri)
3794 {
3795 ipp_t *response = NULL;
3796 ipp_attribute_t *attr = NULL;
3797 const char * uuid = NULL;
3798
3799 const char * const pattrs[] = {
3800 "printer-uuid",
3801 };
3802 const char * const req_attrs[] = {
3803 "printer-uuid",
3804 };
3805
3806 if (http_printer == NULL)
3807 debug_printf ("HTTP connection for printer with URI %s not set!\n",
3808 raw_uri);
3809
3810 if ((response =
3811 get_printer_attributes2(http_printer, raw_uri,
3812 pattrs, 1, req_attrs, 1, 0)) == NULL) {
3813 debug_printf ("Printer with URI %s has no \"printer-uuid\" IPP attribute!\n",
3814 raw_uri);
3815 return NULL;
3816 }
3817
3818 attr = ippFindAttribute(response, "printer-uuid", IPP_TAG_URI);
3819
3820
3821 if (attr)
3822 uuid = strdup(ippGetString(attr, 0, NULL) + 9);
3823 else {
3824 debug_printf ("Printer with URI %s: Cannot read \"printer-uuid\" IPP attribute!\n",
3825 raw_uri);
3826 }
3827
3828 ippDelete(response);
3829
3830 return uuid;
3831 }
3832
3833 static void
get_local_printers(void)3834 get_local_printers (void)
3835 {
3836 dest_list_t dest_list = {0, NULL};
3837 http_t *conn = NULL;
3838
3839 conn = http_connect_local ();
3840
3841 /* We only want to have a list of actually existing CUPS queues, not of
3842 DNS-SD-discovered printers for which CUPS can auto-setup a driverless
3843 print queue */
3844 if (OnlyUnsupportedByCUPS)
3845 cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, 0, 0,
3846 (cups_dest_cb_t)add_dest_cb, &dest_list);
3847 else
3848 cupsEnumDests(CUPS_DEST_FLAGS_NONE, 1000, NULL, CUPS_PRINTER_LOCAL,
3849 CUPS_PRINTER_DISCOVERED, (cups_dest_cb_t)add_dest_cb,
3850 &dest_list);
3851 debug_printf ("cups-browsed (%s): cupsEnumDests\n", local_server_str);
3852 g_hash_table_remove_all (local_printers);
3853 if (OnlyUnsupportedByCUPS)
3854 g_hash_table_remove_all (cups_supported_remote_printers);
3855 int num_dests = dest_list.num_dests;
3856 cups_dest_t *dests = dest_list.dests;
3857 for (int i = 0; i < num_dests; i++) {
3858 const char *val;
3859 cups_dest_t *dest = &dests[i];
3860 local_printer_t *printer;
3861 gboolean cups_browsed_controlled;
3862 gboolean is_temporary;
3863 gboolean is_cups_supported_remote;
3864 char uri[HTTP_MAX_URI];
3865
3866 const char *device_uri = cupsGetOption ("device-uri",
3867 dest->num_options,
3868 dest->options);
3869 if (device_uri == NULL)
3870 device_uri = "";
3871
3872 /* Temporary CUPS queue? */
3873 val = cupsGetOption ("printer-is-temporary",
3874 dest->num_options,
3875 dest->options);
3876 is_temporary = (val && (!strcasecmp (val, "yes") ||
3877 !strcasecmp (val, "on") ||
3878 !strcasecmp (val, "true")));
3879
3880 if (OnlyUnsupportedByCUPS) {
3881 /* Printer discovered by DNS-SD and supported by CUPS' temporary
3882 queues? */
3883 val = cupsGetOption ("printer-uri-supported",
3884 dest->num_options,
3885 dest->options);
3886 /* Printer has no local CUPS queue but CUPS would create a
3887 temporary queue on-demand */
3888 is_cups_supported_remote = (val == NULL || is_temporary);
3889 } else {
3890 is_cups_supported_remote = 0;
3891 if (is_temporary)
3892 continue;
3893 }
3894
3895 val = cupsGetOption (CUPS_BROWSED_MARK,
3896 dest->num_options,
3897 dest->options);
3898 cups_browsed_controlled = val && (!strcasecmp (val, "yes") ||
3899 !strcasecmp (val, "on") ||
3900 !strcasecmp (val, "true"));
3901 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
3902 "localhost", 0, "/printers/%s", dest->name);
3903 printer = new_local_printer (device_uri, get_printer_uuid(conn, uri),
3904 cups_browsed_controlled);
3905 debug_printf ("Printer %s: %s, %s%s%s\n",
3906 dest->name, device_uri, printer->uuid,
3907 cups_browsed_controlled ? ", cups_browsed" : "",
3908 is_cups_supported_remote ? ", temporary" : "");
3909
3910 if (is_cups_supported_remote)
3911 g_hash_table_insert (cups_supported_remote_printers,
3912 g_ascii_strdown (dest->name, -1),
3913 printer);
3914 else
3915 g_hash_table_insert (local_printers,
3916 g_ascii_strdown (dest->name, -1),
3917 printer);
3918 }
3919
3920 cupsFreeDests (num_dests, dests);
3921 }
3922
3923 static browse_data_t *
new_browse_data(int type,int state,const gchar * uri,const gchar * location,const gchar * info,const gchar * make_model,const gchar * browse_options)3924 new_browse_data (int type, int state, const gchar *uri,
3925 const gchar *location, const gchar *info,
3926 const gchar *make_model, const gchar *browse_options)
3927 {
3928 browse_data_t *data = g_malloc (sizeof (browse_data_t));
3929 data->type = type;
3930 data->state = state;
3931 data->uri = g_strdup (uri);
3932 data->location = g_strdup (location);
3933 data->info = g_strdup (info);
3934 data->make_model = g_strdup (make_model);
3935 data->browse_options = g_strdup (browse_options);
3936 return data;
3937 }
3938
3939 static void
browse_data_free(gpointer data)3940 browse_data_free (gpointer data)
3941 {
3942 browse_data_t *bdata = data;
3943 debug_printf("browse_data_free() in THREAD %ld\n", pthread_self());
3944 g_free (bdata->uri);
3945 g_free (bdata->location);
3946 g_free (bdata->info);
3947 g_free (bdata->make_model);
3948 g_free (bdata->browse_options);
3949 g_free (bdata);
3950 }
3951
3952 static void
prepare_browse_data(void)3953 prepare_browse_data (void)
3954 {
3955 static const char * const rattrs[] = { "printer-type",
3956 "printer-state",
3957 "printer-uri-supported",
3958 "printer-info",
3959 "printer-location",
3960 "printer-make-and-model",
3961 "auth-info-required",
3962 "printer-uuid",
3963 "job-template" };
3964 ipp_t *request, *response = NULL;
3965 ipp_attribute_t *attr;
3966 http_t *conn = NULL;
3967
3968 conn = http_connect_local ();
3969
3970 if (conn == NULL) {
3971 debug_printf("Browse send failed to connect to localhost\n");
3972 goto fail;
3973 }
3974
3975 request = ippNewRequest(CUPS_GET_PRINTERS);
3976 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
3977 "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]),
3978 NULL, rattrs);
3979 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
3980 "requesting-user-name", NULL, cupsUser ());
3981
3982 debug_printf("preparing browse data\n");
3983 response = cupsDoRequest (conn, request, "/");
3984 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
3985 debug_printf("browse send failed for localhost: %s\n",
3986 cupsLastErrorString ());
3987 goto fail;
3988 }
3989
3990 g_list_free_full (browse_data, browse_data_free);
3991 browse_data = NULL;
3992 for (attr = ippFirstAttribute(response); attr;
3993 attr = ippNextAttribute(response)) {
3994 int type = -1, state = -1;
3995 const char *uri = NULL;
3996 gchar *location = NULL;
3997 gchar *info = NULL;
3998 gchar *make_model = NULL;
3999 GString *browse_options = g_string_new ("");
4000
4001 /* Skip any non-printer attributes */
4002 while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
4003 attr = ippNextAttribute(response);
4004
4005 if (!attr)
4006 break;
4007
4008 while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
4009 const char *attrname = ippGetName(attr);
4010 int value_tag = ippGetValueTag(attr);
4011
4012 if (!strcasecmp(attrname, "printer-type") &&
4013 value_tag == IPP_TAG_ENUM) {
4014 type = ippGetInteger(attr, 0);
4015 if (type & CUPS_PRINTER_NOT_SHARED) {
4016 /* Skip CUPS queues not marked as shared */
4017 state = -1;
4018 type = -1;
4019 break;
4020 }
4021 } else if (!strcasecmp(attrname, "printer-state") &&
4022 value_tag == IPP_TAG_ENUM)
4023 state = ippGetInteger(attr, 0);
4024 else if (!strcasecmp(attrname, "printer-uri-supported") &&
4025 value_tag == IPP_TAG_URI)
4026 uri = ippGetString(attr, 0, NULL);
4027 else if (!strcasecmp(attrname, "printer-location") &&
4028 value_tag == IPP_TAG_TEXT) {
4029 /* Remove quotes */
4030 gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
4031 location = g_strjoinv ("", tokens);
4032 g_strfreev (tokens);
4033 } else if (!strcasecmp(attrname, "printer-info") &&
4034 value_tag == IPP_TAG_TEXT) {
4035 /* Remove quotes */
4036 gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
4037 info = g_strjoinv ("", tokens);
4038 g_strfreev (tokens);
4039 } else if (!strcasecmp(attrname, "printer-make-and-model") &&
4040 value_tag == IPP_TAG_TEXT) {
4041 /* Remove quotes */
4042 gchar **tokens = g_strsplit (ippGetString(attr, 0, NULL), "\"", -1);
4043 make_model = g_strjoinv ("", tokens);
4044 g_strfreev (tokens);
4045 } else if (!strcasecmp(attrname, "auth-info-required") &&
4046 value_tag == IPP_TAG_KEYWORD) {
4047 if (strcasecmp (ippGetString(attr, 0, NULL), "none"))
4048 g_string_append_printf (browse_options, "auth-info-required=%s ",
4049 ippGetString(attr, 0, NULL));
4050 } else if (!strcasecmp(attrname, "printer-uuid") &&
4051 value_tag == IPP_TAG_URI)
4052 g_string_append_printf (browse_options, "uuid=%s ",
4053 ippGetString(attr, 0, NULL));
4054 else if (!strcasecmp(attrname, "job-sheets-default") &&
4055 value_tag == IPP_TAG_NAME &&
4056 ippGetCount(attr) == 2)
4057 g_string_append_printf (browse_options, "job-sheets=%s,%s ",
4058 ippGetString(attr, 0, NULL),
4059 ippGetString(attr, 1, NULL));
4060 else if (strstr(attrname, "-default")) {
4061 gchar *name = g_strdup (attrname);
4062 gchar *value = NULL;
4063 *strstr (name, "-default") = '\0';
4064
4065 switch (value_tag) {
4066 gchar **tokens;
4067
4068 case IPP_TAG_KEYWORD:
4069 case IPP_TAG_STRING:
4070 case IPP_TAG_NAME:
4071 /* Escape value */
4072 tokens = g_strsplit_set (ippGetString(attr, 0, NULL),
4073 " \"\'\\", -1);
4074 value = g_strjoinv ("\\", tokens);
4075 g_strfreev (tokens);
4076 break;
4077
4078 default:
4079 /* other values aren't needed? */
4080 debug_printf("skipping %s (%d)\n", name, value_tag);
4081 break;
4082 }
4083
4084 if (value) {
4085 g_string_append_printf (browse_options, "%s=%s ", name, value);
4086 g_free (value);
4087 }
4088
4089 g_free (name);
4090 }
4091
4092 attr = ippNextAttribute(response);
4093 }
4094
4095 if (type != -1 && state != -1 && uri && location && info && make_model) {
4096 gchar *browse_options_str = g_string_free (browse_options, FALSE);
4097 browse_data_t *data;
4098 browse_options = NULL;
4099 g_strchomp (browse_options_str);
4100 data = new_browse_data (type, state, uri, location,
4101 info, make_model, browse_options_str);
4102 browse_data = g_list_insert (browse_data, data, 0);
4103 g_free (browse_options_str);
4104 }
4105
4106 if (make_model)
4107 g_free (make_model);
4108
4109 if (info)
4110 g_free (info);
4111
4112 if (location)
4113 g_free (location);
4114
4115 if (browse_options)
4116 g_string_free (browse_options, TRUE);
4117
4118 if (!attr)
4119 break;
4120 }
4121
4122 fail:
4123 if (response)
4124 ippDelete(response);
4125 }
4126
4127 static void
update_local_printers(void)4128 update_local_printers (void)
4129 {
4130 gboolean get_printers = FALSE;
4131 http_t *conn;
4132
4133 if (inhibit_local_printers_update)
4134 return;
4135
4136 conn = http_connect_local ();
4137 if (conn &&
4138 (!local_printers_context || local_printers_context->can_subscribe)) {
4139 if (!local_printers_context ||
4140 local_printers_context->subscription_id == -1) {
4141 /* No subscription yet. First, create the subscription. */
4142 local_printers_create_subscription (conn);
4143 get_printers = TRUE;
4144 } else
4145 /* We already have a subscription, so use it. */
4146
4147 /* Note: for the moment, browse_poll_get_notifications() just
4148 * tells us whether we should re-fetch the printer list, so it
4149 * is safe to use here. */
4150 get_printers = browse_poll_get_notifications (local_printers_context,
4151 conn);
4152 } else
4153 get_printers = TRUE;
4154
4155 if (get_printers) {
4156 get_local_printers ();
4157
4158 if (BrowseLocalProtocols & BROWSE_CUPS)
4159 prepare_browse_data ();
4160 }
4161 }
4162
4163 int
check_jobs()4164 check_jobs () {
4165 int num_jobs = 0;
4166 cups_job_t *jobs = NULL;
4167 remote_printer_t *p;
4168 http_t *conn = NULL;
4169
4170 conn = http_connect_local ();
4171 if (conn == NULL) {
4172 debug_printf("Cannot connect to local CUPS to check whether there are still jobs.\n");
4173 return 0;
4174 }
4175
4176 if (cupsArrayCount(remote_printers) > 0)
4177 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
4178 p;
4179 p = (remote_printer_t *)cupsArrayNext(remote_printers))
4180 if (!p->slave_of) {
4181 num_jobs = cupsGetJobs2(conn, &jobs, p->queue_name, 0,
4182 CUPS_WHICHJOBS_ACTIVE);
4183 if (num_jobs > 0) {
4184 debug_printf("Queue %s still has jobs!\n", p->queue_name);
4185 cupsFreeJobs(num_jobs, jobs);
4186 return 1;
4187 }
4188 }
4189
4190 debug_printf("All our remote printers are without jobs.\n");
4191 return 0;
4192 }
4193
4194 gboolean
autoshutdown_execute(gpointer data)4195 autoshutdown_execute (gpointer data)
4196 {
4197 debug_printf("autoshutdown_execute() in THREAD %ld\n", pthread_self());
4198 /* Are we still in auto shutdown mode and are we still without queues or
4199 jobs*/
4200 if (autoshutdown &&
4201 (cupsArrayCount(remote_printers) == 0 ||
4202 (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
4203 debug_printf("Automatic shutdown as there are no print queues maintained by us or no jobs on them for %d sec.\n",
4204 autoshutdown_timeout);
4205 g_main_loop_quit(gmainloop);
4206 g_main_context_wakeup(NULL);
4207 }
4208
4209 /* Stop this timeout handler, we needed it only once */
4210 return FALSE;
4211 }
4212
4213 int
color_space_score(const char * color_space)4214 color_space_score(const char *color_space)
4215 {
4216 int score = 0;
4217 const char *p = color_space;
4218
4219 if (!p) return -1;
4220 if (!strncasecmp(p, "s", 1)) {
4221 p += 1;
4222 score += 2;
4223 } else if (!strncasecmp(p, "adobe", 5)) {
4224 p += 5;
4225 score += 1;
4226 } else
4227 score += 3;
4228 if (!strncasecmp(p, "black", 5)) {
4229 p += 5;
4230 score += 1000;
4231 } else if (!strncasecmp(p, "gray", 4)) {
4232 p += 4;
4233 score += 2000;
4234 } else if (!strncasecmp(p, "cmyk", 4)) {
4235 p += 4;
4236 score += 4000;
4237 } else if (!strncasecmp(p, "cmy", 3)) {
4238 p += 3;
4239 score += 3000;
4240 } else if (!strncasecmp(p, "rgb", 3)) {
4241 p += 3;
4242 score += 5000;
4243 }
4244 if (!strncasecmp(p, "-", 1) || !strncasecmp(p, "_", 1)) {
4245 p += 1;
4246 }
4247 score += strtol(p, (char **)&p, 10) * 10;
4248 debug_printf("Score for color space %s: %d\n", color_space, score);
4249 return score;
4250 }
4251
4252
4253 #ifdef HAVE_LDAP_REBIND_PROC
4254 # if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
4255 /*
4256 * 'ldap_rebind_proc()' - Callback function for LDAP rebind
4257 */
4258
4259 static int /* O - Result code */
ldap_rebind_proc(LDAP * RebindLDAPHandle,LDAP_CONST char * refsp,ber_tag_t request,ber_int_t msgid,void * params)4260 ldap_rebind_proc(LDAP *RebindLDAPHandle, /* I - LDAP handle */
4261 LDAP_CONST char *refsp, /* I - ??? */
4262 ber_tag_t request, /* I - ??? */
4263 ber_int_t msgid, /* I - ??? */
4264 void *params) /* I - ??? */
4265 {
4266 int rc; /* Result code */
4267 # if LDAP_API_VERSION > 3000
4268 struct berval bval; /* Bind value */
4269 # endif /* LDAP_API_VERSION > 3000 */
4270 (void)request;
4271 (void)msgid;
4272 (void)params;
4273
4274 /*
4275 * Bind to new LDAP server...
4276 */
4277
4278 debug_printf("ldap_rebind_proc: Rebind to %s\n", refsp);
4279
4280 # if LDAP_API_VERSION > 3000
4281 bval.bv_val = BrowseLDAPPassword;
4282 bval.bv_len = (BrowseLDAPPassword == NULL) ? 0 : strlen(BrowseLDAPPassword);
4283
4284 rc = ldap_sasl_bind_s(RebindLDAPHandle, BrowseLDAPBindDN, LDAP_SASL_SIMPLE,
4285 &bval, NULL, NULL, NULL);
4286 # else
4287 rc = ldap_bind_s(RebindLDAPHandle, BrowseLDAPBindDN, BrowseLDAPPassword,
4288 LDAP_AUTH_SIMPLE);
4289 # endif /* LDAP_API_VERSION > 3000 */
4290
4291 return (rc);
4292 }
4293
4294
4295 # else /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
4296 /*
4297 * 'ldap_rebind_proc()' - Callback function for LDAP rebind
4298 */
4299
4300 static int /* O - Result code */
ldap_rebind_proc(LDAP * RebindLDAPHandle,char ** dnp,char ** passwdp,int * authmethodp,int freeit,void * arg)4301 ldap_rebind_proc(LDAP *RebindLDAPHandle, /* I - LDAP handle */
4302 char **dnp, /* I - ??? */
4303 char **passwdp, /* I - ??? */
4304 int *authmethodp, /* I - ??? */
4305 int freeit, /* I - ??? */
4306 void *arg) /* I - ??? */
4307 {
4308 switch (freeit) {
4309 case 1:
4310 /*
4311 * Free current values...
4312 */
4313
4314 debug_printf("ldap_rebind_proc: Free values...\n");
4315
4316 if (dnp && *dnp)
4317 free(*dnp);
4318
4319 if (passwdp && *passwdp)
4320 free(*passwdp);
4321 break;
4322
4323 case 0:
4324 /*
4325 * Return credentials for LDAP referal...
4326 */
4327
4328 debug_printf("ldap_rebind_proc: Return necessary values...\n");
4329
4330 *dnp = strdup(BrowseLDAPBindDN);
4331 *passwdp = strdup(BrowseLDAPPassword);
4332 *authmethodp = LDAP_AUTH_SIMPLE;
4333 break;
4334
4335 default:
4336 /*
4337 * Should never happen...
4338 */
4339
4340 debug_printf("LDAP rebind has been called with wrong freeit value!\n");
4341 break;
4342 }
4343
4344 return (LDAP_SUCCESS);
4345 }
4346 # endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
4347 #endif /* HAVE_LDAP_REBIND_PROC */
4348
4349
4350 #ifdef HAVE_LDAP
4351 /*
4352 * 'ldap_new_connection()' - Start new LDAP connection
4353 */
4354
4355 static LDAP * /* O - LDAP handle */
ldap_new_connection(void)4356 ldap_new_connection(void)
4357 {
4358 int rc; /* LDAP API status */
4359 int version = 3; /* LDAP version */
4360 struct berval bv = {0, ""}; /* SASL bind value */
4361 LDAP *TempBrowseLDAPHandle=NULL;
4362 /* Temporary LDAP Handle */
4363 # if defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP)
4364 int ldap_ssl = 0; /* LDAP SSL indicator */
4365 int ssl_err = 0; /* LDAP SSL error value */
4366 # endif /* defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) */
4367
4368
4369 # ifdef HAVE_OPENLDAP
4370 # ifdef HAVE_LDAP_SSL
4371 /*
4372 * Set the certificate file to use for encrypted LDAP sessions...
4373 */
4374
4375 if (BrowseLDAPCACertFile) {
4376 debug_printf("ldap_new_connection: Setting CA certificate file \"%s\"\n",
4377 BrowseLDAPCACertFile);
4378
4379 if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
4380 (void *)BrowseLDAPCACertFile)) != LDAP_SUCCESS)
4381 debug_printf("Unable to set CA certificate file for LDAP "
4382 "connections: %d - %s\n", rc, ldap_err2string(rc));
4383 }
4384 # endif /* HAVE_LDAP_SSL */
4385
4386 /*
4387 * Initialize OPENLDAP connection...
4388 * LDAP stuff currently only supports ldapi EXTERNAL SASL binds...
4389 */
4390
4391 if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost"))
4392 rc = ldap_initialize(&TempBrowseLDAPHandle, "ldapi:///");
4393 else
4394 rc = ldap_initialize(&TempBrowseLDAPHandle, BrowseLDAPServer);
4395
4396 # else /* HAVE_OPENLDAP */
4397
4398 int ldap_port = 0; /* LDAP port */
4399 char ldap_protocol[11], /* LDAP protocol */
4400 ldap_host[255]; /* LDAP host */
4401
4402 /*
4403 * Split LDAP URI into its components...
4404 */
4405
4406 if (!BrowseLDAPServer) {
4407 debug_printf("BrowseLDAPServer not configured!\n");
4408 debug_printf("Disabling LDAP browsing!\n");
4409 /*BrowseLocalProtocols &= ~BROWSE_LDAP;*/
4410 BrowseRemoteProtocols &= ~BROWSE_LDAP;
4411 return (NULL);
4412 }
4413
4414 sscanf(BrowseLDAPServer, "%10[^:]://%254[^:/]:%d", ldap_protocol, ldap_host,
4415 &ldap_port);
4416
4417 if (!strcmp(ldap_protocol, "ldap"))
4418 ldap_ssl = 0;
4419 else if (!strcmp(ldap_protocol, "ldaps"))
4420 ldap_ssl = 1;
4421 else {
4422 debug_printf("Unrecognized LDAP protocol (%s)!\n",
4423 ldap_protocol);
4424 debug_printf("Disabling LDAP browsing!\n");
4425 /*BrowseLocalProtocols &= ~BROWSE_LDAP;*/
4426 BrowseRemoteProtocols &= ~BROWSE_LDAP;
4427 return (NULL);
4428 }
4429
4430 if (ldap_port == 0) {
4431 if (ldap_ssl)
4432 ldap_port = LDAPS_PORT;
4433 else
4434 ldap_port = LDAP_PORT;
4435 }
4436
4437 debug_printf("ldap_new_connection: PROT:%s HOST:%s PORT:%d\n",
4438 ldap_protocol, ldap_host, ldap_port);
4439
4440 /*
4441 * Initialize LDAP connection...
4442 */
4443
4444 if (!ldap_ssl) {
4445 if ((TempBrowseLDAPHandle = ldap_init(ldap_host, ldap_port)) == NULL)
4446 rc = LDAP_OPERATIONS_ERROR;
4447 else
4448 rc = LDAP_SUCCESS;
4449
4450 # ifdef HAVE_LDAP_SSL
4451 } else {
4452 /*
4453 * Initialize SSL LDAP connection...
4454 */
4455
4456 if (BrowseLDAPCACertFile) {
4457 rc = ldapssl_client_init(BrowseLDAPCACertFile, (void *)NULL);
4458 if (rc != LDAP_SUCCESS) {
4459 debug_printf("Failed to initialize LDAP SSL client!\n");
4460 rc = LDAP_OPERATIONS_ERROR;
4461 } else {
4462 if ((TempBrowseLDAPHandle = ldapssl_init(ldap_host, ldap_port,
4463 1)) == NULL)
4464 rc = LDAP_OPERATIONS_ERROR;
4465 else
4466 rc = LDAP_SUCCESS;
4467 }
4468 } else {
4469 debug_printf("LDAP SSL certificate file/database not configured!\n");
4470 rc = LDAP_OPERATIONS_ERROR;
4471 }
4472
4473 # else /* HAVE_LDAP_SSL */
4474
4475 /*
4476 * Return error, because client libraries doesn't support SSL
4477 */
4478
4479 debug_printf("LDAP client libraries do not support SSL\n");
4480 rc = LDAP_OPERATIONS_ERROR;
4481
4482 # endif /* HAVE_LDAP_SSL */
4483 }
4484 # endif /* HAVE_OPENLDAP */
4485
4486 /*
4487 * Check return code from LDAP initialize...
4488 */
4489
4490 if (rc != LDAP_SUCCESS) {
4491 debug_printf("Unable to initialize LDAP!\n");
4492
4493 if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)
4494 debug_printf("Temporarily disabling LDAP browsing...\n");
4495 else {
4496 debug_printf("Disabling LDAP browsing!\n");
4497
4498 /*BrowseLocalProtocols &= ~BROWSE_LDAP;*/
4499 BrowseRemoteProtocols &= ~BROWSE_LDAP;
4500 }
4501
4502 ldap_disconnect(TempBrowseLDAPHandle);
4503
4504 return (NULL);
4505 }
4506
4507 /*
4508 * Upgrade LDAP version...
4509 */
4510
4511 if (ldap_set_option(TempBrowseLDAPHandle, LDAP_OPT_PROTOCOL_VERSION,
4512 (const void *)&version) != LDAP_SUCCESS) {
4513 debug_printf("Unable to set LDAP protocol version %d!\n",
4514 version);
4515 debug_printf("Disabling LDAP browsing!\n");
4516
4517 /*BrowseLocalProtocols &= ~BROWSE_LDAP;*/
4518 BrowseRemoteProtocols &= ~BROWSE_LDAP;
4519 ldap_disconnect(TempBrowseLDAPHandle);
4520
4521 return (NULL);
4522 }
4523
4524 /*
4525 * Register LDAP rebind procedure...
4526 */
4527
4528 # ifdef HAVE_LDAP_REBIND_PROC
4529 # if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
4530
4531 rc = ldap_set_rebind_proc(TempBrowseLDAPHandle, &ldap_rebind_proc,
4532 (void *)NULL);
4533 if (rc != LDAP_SUCCESS)
4534 debug_printf("Setting LDAP rebind function failed with status %d: %s\n",
4535 rc, ldap_err2string(rc));
4536
4537 # else
4538
4539 ldap_set_rebind_proc(TempBrowseLDAPHandle, &ldap_rebind_proc, (void *)NULL);
4540
4541 # endif /* defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) */
4542 # endif /* HAVE_LDAP_REBIND_PROC */
4543
4544 /*
4545 * Start LDAP bind...
4546 */
4547
4548 # if LDAP_API_VERSION > 3000
4549 struct berval bval;
4550 bval.bv_val = BrowseLDAPPassword;
4551 bval.bv_len = (BrowseLDAPPassword == NULL) ? 0 : strlen(BrowseLDAPPassword);
4552
4553 if (!BrowseLDAPServer || !strcasecmp(BrowseLDAPServer, "localhost"))
4554 rc = ldap_sasl_bind_s(TempBrowseLDAPHandle, NULL, "EXTERNAL", &bv, NULL,
4555 NULL, NULL);
4556 else
4557 rc = ldap_sasl_bind_s(TempBrowseLDAPHandle, BrowseLDAPBindDN,
4558 LDAP_SASL_SIMPLE, &bval, NULL, NULL, NULL);
4559
4560 # else
4561 rc = ldap_bind_s(TempBrowseLDAPHandle, BrowseLDAPBindDN,
4562 BrowseLDAPPassword, LDAP_AUTH_SIMPLE);
4563 # endif /* LDAP_API_VERSION > 3000 */
4564
4565 if (rc != LDAP_SUCCESS) {
4566 debug_printf("LDAP bind failed with error %d: %s\n",
4567 rc, ldap_err2string(rc));
4568
4569 # if defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP)
4570 if (ldap_ssl && (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR)) {
4571 ssl_err = PORT_GetError();
4572 if (ssl_err != 0)
4573 debug_printf("LDAP SSL error %d: %s\n", ssl_err,
4574 ldapssl_err2string(ssl_err));
4575 }
4576 # endif /* defined(HAVE_LDAP_SSL) && defined (HAVE_MOZILLA_LDAP) */
4577
4578 ldap_disconnect(TempBrowseLDAPHandle);
4579
4580 return (NULL);
4581 }
4582
4583 debug_printf("LDAP connection established\n");
4584
4585 return (TempBrowseLDAPHandle);
4586 }
4587
4588
4589 /*
4590 * 'ldap_reconnect()' - Reconnect to LDAP Server
4591 */
4592
4593 static LDAP * /* O - New LDAP handle */
ldap_reconnect(void)4594 ldap_reconnect(void)
4595 {
4596 LDAP *TempBrowseLDAPHandle = NULL; /* Temp Handle to LDAP server */
4597
4598 /*
4599 * Get a new LDAP Handle and replace the global Handle
4600 * if the new connection was successful.
4601 */
4602
4603 debug_printf("Try LDAP reconnect...\n");
4604
4605 TempBrowseLDAPHandle = ldap_new_connection();
4606
4607 if (TempBrowseLDAPHandle != NULL) {
4608 if (BrowseLDAPHandle != NULL)
4609 ldap_disconnect(BrowseLDAPHandle);
4610
4611 BrowseLDAPHandle = TempBrowseLDAPHandle;
4612 }
4613
4614 return (BrowseLDAPHandle);
4615 }
4616
4617
4618 /*
4619 * 'ldap_disconnect()' - Disconnect from LDAP Server
4620 */
4621
4622 static void
ldap_disconnect(LDAP * ld)4623 ldap_disconnect(LDAP *ld) /* I - LDAP handle */
4624 {
4625 int rc; /* Return code */
4626
4627 /*
4628 * Close LDAP handle...
4629 */
4630
4631 # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4632 rc = ldap_unbind_ext_s(ld, NULL, NULL);
4633 # else
4634 rc = ldap_unbind_s(ld);
4635 # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4636
4637 if (rc != LDAP_SUCCESS)
4638 debug_printf("Unbind from LDAP server failed with status %d: %s\n",
4639 rc, ldap_err2string(rc));
4640 }
4641
4642 /*
4643 * 'cupsdUpdateLDAPBrowse()' - Scan for new printers via LDAP...
4644 */
4645
4646 void
cupsdUpdateLDAPBrowse(void)4647 cupsdUpdateLDAPBrowse(void)
4648 {
4649 char uri[HTTP_MAX_URI], /* Printer URI */
4650 host[HTTP_MAX_URI], /* Hostname */
4651 resource[HTTP_MAX_URI], /* Resource path */
4652 local_resource[HTTP_MAX_URI], /* Resource path */
4653 service_name[4096],
4654 location[1024], /* Printer location */
4655 info[1024], /* Printer information */
4656 make_model[1024], /* Printer make and model */
4657 type_num[30], /* Printer type number */
4658 scheme[32], /* URI's scheme */
4659 username[64]; /* URI's username */
4660 int port; /* URI's port number */
4661 char *c;
4662 int hl;
4663 int rc; /* LDAP status */
4664 int limit; /* Size limit */
4665 LDAPMessage *res, /* LDAP search results */
4666 *e; /* Current entry from search */
4667
4668 debug_printf("UpdateLDAPBrowse\n");
4669
4670 /*
4671 * Reconnect if LDAP Handle is invalid...
4672 */
4673
4674 if (! BrowseLDAPHandle) {
4675 ldap_reconnect();
4676 return;
4677 }
4678
4679 /*
4680 * Search for cups printers in LDAP directory...
4681 */
4682
4683 rc = ldap_search_rec(BrowseLDAPHandle, BrowseLDAPDN, LDAP_SCOPE_SUBTREE,
4684 BrowseLDAPFilter, (char **)ldap_attrs, 0, &res);
4685
4686 /*
4687 * If ldap search was successfull then exit function
4688 * and temporary disable LDAP updates...
4689 */
4690
4691 if (rc != LDAP_SUCCESS) {
4692 if (BrowseLDAPUpdate && ((rc == LDAP_SERVER_DOWN) ||
4693 (rc == LDAP_CONNECT_ERROR))) {
4694 BrowseLDAPUpdate = FALSE;
4695 debug_printf("LDAP update temporary disabled\n");
4696 }
4697 return;
4698 }
4699
4700 /*
4701 * If LDAP updates were disabled, we will reenable them...
4702 */
4703
4704 if (!BrowseLDAPUpdate) {
4705 BrowseLDAPUpdate = TRUE;
4706 debug_printf("LDAP update enabled\n");
4707 }
4708
4709 /*
4710 * Count LDAP entries and return if no entry exist...
4711 */
4712
4713 limit = ldap_count_entries(BrowseLDAPHandle, res);
4714 debug_printf("LDAP search returned %d entries\n", limit);
4715 if (limit < 1) {
4716 ldap_freeres(res);
4717 return;
4718 }
4719
4720 /*
4721 * Loop through the available printers...
4722 */
4723
4724 for (e = ldap_first_entry(BrowseLDAPHandle, res);
4725 e;
4726 e = ldap_next_entry(BrowseLDAPHandle, e)) {
4727 /*
4728 * Get the required values from this entry...
4729 */
4730
4731 if (ldap_getval_firststring(BrowseLDAPHandle, e,
4732 "printerDescription", info, sizeof(info)) == -1)
4733 continue;
4734
4735 if (ldap_getval_firststring(BrowseLDAPHandle, e,
4736 "printerLocation", location,
4737 sizeof(location)) == -1)
4738 continue;
4739
4740 if (ldap_getval_firststring(BrowseLDAPHandle, e,
4741 "printerMakeAndModel", make_model,
4742 sizeof(make_model)) == -1)
4743 continue;
4744
4745 if (ldap_getval_firststring(BrowseLDAPHandle, e,
4746 "printerType", type_num,
4747 sizeof(type_num)) == -1)
4748 continue;
4749
4750 if (ldap_getval_firststring(BrowseLDAPHandle, e,
4751 "printerURI", uri, sizeof(uri)) == -1)
4752 continue;
4753
4754 /*
4755 * Process the entry...
4756 */
4757
4758 memset(scheme, 0, sizeof(scheme));
4759 memset(username, 0, sizeof(username));
4760 memset(host, 0, sizeof(host));
4761 memset(resource, 0, sizeof(resource));
4762 memset(local_resource, 0, sizeof(local_resource));
4763
4764 httpSeparateURI (HTTP_URI_CODING_ALL, uri,
4765 scheme, sizeof(scheme) - 1,
4766 username, sizeof(username) - 1,
4767 host, sizeof(host) - 1,
4768 &port,
4769 resource, sizeof(resource)- 1);
4770
4771 if (strncasecmp (resource, "/printers/", 10) &&
4772 strncasecmp (resource, "/classes/", 9)) {
4773 debug_printf("don't understand URI: %s\n", uri);
4774 return;
4775 }
4776
4777 strncpy (local_resource, resource + 1, sizeof (local_resource) - 1);
4778 local_resource[sizeof (local_resource) - 1] = '\0';
4779 c = strchr (local_resource, '?');
4780 if (c)
4781 *c = '\0';
4782
4783 /* Build the DNS-SD service name which CUPS would give to this printer
4784 when DNS-SD-broadcasting it */
4785 snprintf(service_name, sizeof (service_name), "%s @ %s",
4786 (strlen(info) > 0 ? info : strchr(local_resource, '/') + 1), host);
4787 /* Cut off trailing ".local" of host name */
4788 hl = strlen(service_name);
4789 if (hl > 6 && !strcasecmp(service_name + hl - 6, ".local"))
4790 service_name[hl - 6] = '\0';
4791 if (hl > 7 && !strcasecmp(service_name + hl - 7, ".local."))
4792 service_name[hl - 7] = '\0';
4793 /* DNS-SD service name has max. 63 characters */
4794 service_name[63] = '\0';
4795
4796 debug_printf("LDAP: Remote host: %s; Port: %d; Remote queue name: %s; Service Name: %s\n",
4797 host, port, strchr(local_resource, '/') + 1, service_name);
4798
4799 examine_discovered_printer_record(host, NULL, port, local_resource,
4800 service_name, location, info, "", "",
4801 "", 0, NULL);
4802
4803 }
4804
4805 ldap_freeres(res);
4806 }
4807
4808 /*
4809 * 'ldap_search_rec()' - LDAP Search with reconnect
4810 */
4811
4812 static int /* O - Return code */
ldap_search_rec(LDAP * ld,char * base,int scope,char * filter,char * attrs[],int attrsonly,LDAPMessage ** res)4813 ldap_search_rec(LDAP *ld, /* I - LDAP handler */
4814 char *base, /* I - Base dn */
4815 int scope, /* I - LDAP search scope */
4816 char *filter, /* I - Filter string */
4817 char *attrs[], /* I - Requested attributes */
4818 int attrsonly, /* I - Return only attributes? */
4819 LDAPMessage **res) /* I - LDAP handler */
4820 {
4821 int rc; /* Return code */
4822 LDAP *ldr; /* LDAP handler after reconnect */
4823
4824
4825 # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4826 rc = ldap_search_ext_s(ld, base, scope, filter, attrs, attrsonly, NULL, NULL,
4827 NULL, LDAP_NO_LIMIT, res);
4828 # else
4829 rc = ldap_search_s(ld, base, scope, filter, attrs, attrsonly, res);
4830 # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4831
4832 /*
4833 * If we have a connection problem try again...
4834 */
4835
4836 if (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR) {
4837 debug_printf("LDAP search failed with status %d: %s\n",
4838 rc, ldap_err2string(rc));
4839 debug_printf("We try the LDAP search once again after reconnecting to "
4840 "the server\n");
4841 ldap_freeres(*res);
4842 ldr = ldap_reconnect();
4843
4844 # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4845 rc = ldap_search_ext_s(ldr, base, scope, filter, attrs, attrsonly, NULL,
4846 NULL, NULL, LDAP_NO_LIMIT, res);
4847 # else
4848 rc = ldap_search_s(ldr, base, scope, filter, attrs, attrsonly, res);
4849 # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4850 }
4851
4852 if (rc == LDAP_NO_SUCH_OBJECT)
4853 debug_printf("ldap_search_rec: LDAP entry/object not found\n");
4854 else if (rc != LDAP_SUCCESS)
4855 debug_printf("ldap_search_rec: LDAP search failed with status %d: %s\n",
4856 rc, ldap_err2string(rc));
4857
4858 if (rc != LDAP_SUCCESS)
4859 ldap_freeres(*res);
4860
4861 return (rc);
4862 }
4863
4864
4865 /*
4866 * 'ldap_freeres()' - Free LDAPMessage
4867 */
4868
4869 static void
ldap_freeres(LDAPMessage * entry)4870 ldap_freeres(LDAPMessage *entry) /* I - LDAP handler */
4871 {
4872 int rc; /* Return value */
4873
4874 rc = ldap_msgfree(entry);
4875 if (rc == -1)
4876 debug_printf("Can't free LDAPMessage!\n");
4877 else if (rc == 0)
4878 debug_printf("Freeing LDAPMessage was unnecessary\n");
4879 }
4880
4881
4882 /*
4883 * 'ldap_getval_char()' - Get first LDAP value and convert to string
4884 */
4885
4886 static int /* O - Return code */
ldap_getval_firststring(LDAP * ld,LDAPMessage * entry,char * attr,char * retval,unsigned long maxsize)4887 ldap_getval_firststring(LDAP *ld, /* I - LDAP handler */
4888 LDAPMessage *entry, /* I - LDAP message or search
4889 result */
4890 char *attr, /* I - the wanted attribute */
4891 char *retval, /* O - String to return */
4892 unsigned long maxsize) /* I - Max string size */
4893 {
4894 char *dn; /* LDAP DN */
4895 int rc = 0; /* Return code */
4896 # if defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000
4897 struct berval **bval; /* LDAP value array */
4898 unsigned long size; /* String size */
4899
4900
4901 /*
4902 * Get value from LDAPMessage...
4903 */
4904
4905 if ((bval = ldap_get_values_len(ld, entry, attr)) == NULL) {
4906 rc = -1;
4907 dn = ldap_get_dn(ld, entry);
4908 debug_printf("Failed to get LDAP value %s for %s!\n",
4909 attr, dn);
4910 ldap_memfree(dn);
4911 } else {
4912 /*
4913 * Check size and copy value into our string...
4914 */
4915
4916 size = maxsize;
4917 if (size < (bval[0]->bv_len + 1)) {
4918 rc = -1;
4919 dn = ldap_get_dn(ld, entry);
4920 debug_printf("Attribute %s is too big! (dn: %s)\n",
4921 attr, dn);
4922 ldap_memfree(dn);
4923 } else
4924 size = bval[0]->bv_len + 1;
4925
4926 strncpy(retval, bval[0]->bv_val, size);
4927 if (size > 0)
4928 retval[size - 1] = '\0';
4929 ldap_value_free_len(bval);
4930 }
4931 # else
4932 char **value; /* LDAP value */
4933
4934 /*
4935 * Get value from LDAPMessage...
4936 */
4937
4938 if ((value = (char **)ldap_get_values(ld, entry, attr)) == NULL) {
4939 rc = -1;
4940 dn = ldap_get_dn(ld, entry);
4941 debug_printf("Failed to get LDAP value %s for %s!\n",
4942 attr, dn);
4943 ldap_memfree(dn);
4944 } else {
4945 strncpy(retval, *value, maxsize);
4946 if (maxsize > 0)
4947 retval[maxsize - 1] = '\0';
4948 ldap_value_free(value);
4949 }
4950 # endif /* defined(HAVE_OPENLDAP) && LDAP_API_VERSION > 3000 */
4951
4952 return (rc);
4953 }
4954
4955 #endif /* HAVE_LDAP */
4956
4957
4958 static int
create_subscription()4959 create_subscription ()
4960 {
4961 ipp_t *req;
4962 ipp_t *resp;
4963 ipp_attribute_t *attr;
4964 int id = 0;
4965 http_t *conn = NULL;
4966
4967 conn = http_connect_local ();
4968 if (conn == NULL) {
4969 debug_printf("Cannot connect to local CUPS to subscribe to notifications.\n");
4970 return 0;
4971 }
4972
4973 req = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION);
4974 ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
4975 "printer-uri", NULL, "/");
4976 ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
4977 "notify-events", NULL, "all");
4978 ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
4979 "notify-recipient-uri", NULL, "dbus://");
4980 ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
4981 "notify-lease-duration", notify_lease_duration);
4982
4983 resp = cupsDoRequest (conn, req, "/");
4984 if (!resp || cupsLastError() != IPP_STATUS_OK) {
4985 debug_printf ("Error subscribing to CUPS notifications: %s\n",
4986 cupsLastErrorString ());
4987 return 0;
4988 }
4989
4990 attr = ippFindAttribute (resp, "notify-subscription-id", IPP_TAG_INTEGER);
4991 if (attr)
4992 id = ippGetInteger (attr, 0);
4993 else
4994 debug_printf (""
4995 "ipp-create-printer-subscription response doesn't contain "
4996 "subscription id.\n");
4997
4998 ippDelete (resp);
4999 return id;
5000 }
5001
5002
5003 static gboolean
renew_subscription(int id)5004 renew_subscription (int id)
5005 {
5006 ipp_t *req;
5007 ipp_t *resp;
5008 http_t *conn = NULL;
5009
5010 conn = http_connect_local ();
5011 if (conn == NULL) {
5012 debug_printf("Cannot connect to local CUPS to renew subscriptions.\n");
5013 return FALSE;
5014 }
5015
5016 req = ippNewRequest (IPP_RENEW_SUBSCRIPTION);
5017 ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
5018 "notify-subscription-id", id);
5019 ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
5020 "printer-uri", NULL, "/");
5021 ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
5022 "notify-recipient-uri", NULL, "dbus://");
5023 ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
5024 "notify-lease-duration", notify_lease_duration);
5025
5026 resp = cupsDoRequest (conn, req, "/");
5027 if (!resp || cupsLastError() != IPP_STATUS_OK) {
5028 debug_printf ("Error renewing CUPS subscription %d: %s\n",
5029 id, cupsLastErrorString ());
5030 return FALSE;
5031 }
5032
5033 ippDelete (resp);
5034 return TRUE;
5035 }
5036
5037
5038 static gboolean
renew_subscription_timeout(gpointer userdata)5039 renew_subscription_timeout (gpointer userdata)
5040 {
5041 int *subscription_id = userdata;
5042
5043 debug_printf("renew_subscription_timeout() in THREAD %ld\n", pthread_self());
5044
5045 if (*subscription_id <= 0 || !renew_subscription (*subscription_id))
5046 *subscription_id = create_subscription ();
5047
5048 return TRUE;
5049 }
5050
5051
5052 void
cancel_subscription(int id)5053 cancel_subscription (int id)
5054 {
5055 ipp_t *req;
5056 ipp_t *resp;
5057 http_t *conn = NULL;
5058
5059 conn = http_connect_local ();
5060 if (conn == NULL) {
5061 debug_printf("Cannot connect to local CUPS to cancel subscriptions.\n");
5062 return;
5063 }
5064
5065 if (id <= 0)
5066 return;
5067
5068 req = ippNewRequest (IPP_CANCEL_SUBSCRIPTION);
5069 ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
5070 "printer-uri", NULL, "/");
5071 ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
5072 "notify-subscription-id", id);
5073
5074 resp = cupsDoRequest (conn, req, "/");
5075 if (!resp || cupsLastError() != IPP_STATUS_OK) {
5076 debug_printf ("Error subscribing to CUPS notifications: %s\n",
5077 cupsLastErrorString ());
5078 return;
5079 }
5080
5081 ippDelete (resp);
5082 }
5083
5084 int
is_created_by_cups_browsed(const char * printer)5085 is_created_by_cups_browsed (const char *printer) {
5086 remote_printer_t *p;
5087
5088 if (printer == NULL)
5089 return 0;
5090 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
5091 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
5092 if (!p->slave_of && !strcasecmp(printer, p->queue_name))
5093 return 1;
5094
5095 return 0;
5096 }
5097
5098 remote_printer_t *
printer_record(const char * printer)5099 printer_record (const char *printer) {
5100 remote_printer_t *p;
5101
5102 if (printer == NULL)
5103 return NULL;
5104 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
5105 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
5106 if (!p->slave_of && !strcasecmp(printer, p->queue_name))
5107 return p;
5108
5109 return NULL;
5110 }
5111
5112 void
log_cluster(remote_printer_t * p)5113 log_cluster(remote_printer_t *p) {
5114 remote_printer_t *q, *r;
5115 int i;
5116 if (p == NULL || (!debug_stderr && !debug_logfile))
5117 return;
5118 if (p->slave_of)
5119 q = p->slave_of;
5120 else
5121 q = p;
5122 if (q->queue_name == NULL)
5123 return;
5124 debug_printf("Remote CUPS printers clustered as queue %s:\n", q->queue_name);
5125 for (r = (remote_printer_t *)cupsArrayFirst(remote_printers), i = 0;
5126 r; r = (remote_printer_t *)cupsArrayNext(remote_printers), i ++)
5127 if (r->status != STATUS_DISAPPEARED && r->status != STATUS_UNCONFIRMED &&
5128 r->status != STATUS_TO_BE_RELEASED &&
5129 (r == q || r->slave_of == q))
5130 debug_printf(" %s%s%s\n", r->uri,
5131 (r == q ? "*" : ""),
5132 (i == q->last_printer ? " (last job printed)" : ""));
5133 }
5134
5135 void
log_all_printers()5136 log_all_printers() {
5137 remote_printer_t *p, *q;
5138 if (!debug_stderr && !debug_logfile)
5139 return;
5140 debug_printf("=== Remote printer overview ===\n");
5141 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
5142 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
5143 debug_printf("Printer %s (%s, %s): Local queue %s, %s, Slave of %s%s\n",
5144 p->uri,
5145 p->host, (p->ip ? p->ip : "IP not determined"), p->queue_name,
5146 (p->netprinter ? "IPP Printer" : "Remote CUPS Printer"),
5147 ((q = p->slave_of) != NULL ?
5148 (q->uri ? q->uri : "Deleted Printer") : "None"),
5149 (p->status == STATUS_UNCONFIRMED ? " (Unconfirmed)" :
5150 (p->status == STATUS_DISAPPEARED ? " (Disappeared)" :
5151 (p->status == STATUS_TO_BE_RELEASED ?
5152 " (To be released from cups-browsed)" :
5153 (p->status == STATUS_TO_BE_CREATED ?
5154 " (To be created/updated)" : "")))));
5155 debug_printf("===============================\n");
5156 }
5157
5158 char*
is_disabled(const char * printer,const char * reason)5159 is_disabled(const char *printer, const char *reason) {
5160 ipp_t *request, *response;
5161 ipp_attribute_t *attr;
5162 const char *pname = NULL;
5163 ipp_pstate_t pstate = IPP_PRINTER_IDLE;
5164 const char *p;
5165 char *pstatemsg = NULL;
5166 static const char *pattrs[] =
5167 {
5168 "printer-name",
5169 "printer-state",
5170 "printer-state-message"
5171 };
5172 http_t *conn = NULL;
5173
5174 conn = http_connect_local ();
5175 if (conn == NULL) {
5176 debug_printf("Cannot connect to local CUPS to check whether the printer %s is disabled.\n",
5177 printer);
5178 return NULL;
5179 }
5180
5181 request = ippNewRequest(CUPS_GET_PRINTERS);
5182 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
5183 "requested-attributes",
5184 sizeof(pattrs) / sizeof(pattrs[0]),
5185 NULL, pattrs);
5186 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
5187 "requesting-user-name",
5188 NULL, cupsUser());
5189 if ((response = cupsDoRequest(conn, request, "/")) != NULL) {
5190 for (attr = ippFirstAttribute(response); attr != NULL;
5191 attr = ippNextAttribute(response)) {
5192 while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
5193 attr = ippNextAttribute(response);
5194 if (attr == NULL)
5195 break;
5196 pname = NULL;
5197 pstate = IPP_PRINTER_IDLE;
5198 if (pstatemsg) {
5199 free(pstatemsg);
5200 pstatemsg = NULL;
5201 }
5202 while (attr != NULL && ippGetGroupTag(attr) ==
5203 IPP_TAG_PRINTER) {
5204 if (!strcmp(ippGetName(attr), "printer-name") &&
5205 ippGetValueTag(attr) == IPP_TAG_NAME)
5206 pname = ippGetString(attr, 0, NULL);
5207 else if (!strcmp(ippGetName(attr), "printer-state") &&
5208 ippGetValueTag(attr) == IPP_TAG_ENUM)
5209 pstate = (ipp_pstate_t)ippGetInteger(attr, 0);
5210 else if (!strcmp(ippGetName(attr), "printer-state-message") &&
5211 ippGetValueTag(attr) == IPP_TAG_TEXT) {
5212 if (pstatemsg != NULL) {
5213 free(pstatemsg);
5214 pstatemsg = NULL;
5215 }
5216 p = ippGetString(attr, 0, NULL);
5217 if (p != NULL) pstatemsg = strdup(p);
5218 }
5219 attr = ippNextAttribute(response);
5220 }
5221 if (pname == NULL) {
5222 if (attr == NULL)
5223 break;
5224 else
5225 continue;
5226 }
5227 if (!strcasecmp(pname, printer)) {
5228 switch (pstate) {
5229 case IPP_PRINTER_IDLE:
5230 case IPP_PRINTER_PROCESSING:
5231 ippDelete(response);
5232 if (pstatemsg != NULL) {
5233 free(pstatemsg);
5234 pstatemsg = NULL;
5235 }
5236 return NULL;
5237 case IPP_PRINTER_STOPPED:
5238 ippDelete(response);
5239 if (reason == NULL)
5240 return pstatemsg;
5241 else if (pstatemsg != NULL && (strcasestr(pstatemsg, reason) != NULL))
5242 return pstatemsg;
5243 else {
5244 if (pstatemsg != NULL) {
5245 free(pstatemsg);
5246 pstatemsg = NULL;
5247 }
5248 return NULL;
5249 }
5250 }
5251 }
5252 }
5253 debug_printf("No information regarding enabled/disabled found about the requested printer '%s'\n",
5254 printer);
5255 ippDelete(response);
5256 if (pstatemsg != NULL) {
5257 free(pstatemsg);
5258 pstatemsg = NULL;
5259 }
5260 return NULL;
5261 }
5262 debug_printf("ERROR: Request for printer info failed: %s\n",
5263 cupsLastErrorString());
5264 if (pstatemsg != NULL) {
5265 free(pstatemsg);
5266 pstatemsg = NULL;
5267 }
5268 return NULL;
5269 }
5270
5271 int
enable_printer(const char * printer)5272 enable_printer (const char *printer) {
5273 ipp_t *request;
5274 char uri[HTTP_MAX_URI];
5275 http_t *conn = NULL;
5276
5277 conn = http_connect_local ();
5278 if (conn == NULL) {
5279 debug_printf("Cannot connect to local CUPS to enable printer %s.\n",
5280 printer);
5281 return -1;
5282 }
5283
5284 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
5285 "localhost", 0, "/printers/%s", printer);
5286 request = ippNewRequest (IPP_RESUME_PRINTER);
5287 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
5288 "printer-uri", NULL, uri);
5289 ippDelete(cupsDoRequest (conn, request, "/admin/"));
5290 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
5291 debug_printf("ERROR: Failed enabling printer '%s': %s\n",
5292 printer, cupsLastErrorString());
5293 return -1;
5294 }
5295 debug_printf("Enabled printer '%s'\n", printer);
5296 return 0;
5297 }
5298
5299 int
disable_printer(const char * printer,const char * reason)5300 disable_printer (const char *printer, const char *reason) {
5301 ipp_t *request;
5302 char uri[HTTP_MAX_URI];
5303 http_t *conn = NULL;
5304
5305 conn = http_connect_local ();
5306 if (conn == NULL) {
5307 debug_printf("Cannot connect to local CUPS to disable printer %s.\n",
5308 printer);
5309 return -1;
5310 }
5311
5312 if (reason == NULL)
5313 reason = "Disabled by cups-browsed";
5314 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
5315 "localhost", 0, "/printers/%s", printer);
5316 request = ippNewRequest (IPP_PAUSE_PRINTER);
5317 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
5318 "printer-uri", NULL, uri);
5319 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_TEXT,
5320 "printer-state-message", NULL, reason);
5321 ippDelete(cupsDoRequest (conn, request, "/admin/"));
5322 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
5323 debug_printf("ERROR: Failed disabling printer '%s': %s\n",
5324 printer, cupsLastErrorString());
5325 return -1;
5326 }
5327 debug_printf("Disabled printer '%s'\n", printer);
5328 return 0;
5329 }
5330
5331 int
set_cups_default_printer(const char * printer)5332 set_cups_default_printer(const char *printer) {
5333 ipp_t *request;
5334 char uri[HTTP_MAX_URI];
5335 http_t *conn = NULL;
5336
5337 conn = http_connect_local ();
5338 if (conn == NULL) {
5339 debug_printf("Cannot connect to local CUPS to subscribe to set printer %s as default printer.\n",
5340 printer);
5341 return -1;
5342 }
5343
5344 if (printer == NULL)
5345 return 0;
5346 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
5347 "localhost", 0, "/printers/%s", printer);
5348 request = ippNewRequest(IPP_OP_CUPS_SET_DEFAULT);
5349 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
5350 "printer-uri", NULL, uri);
5351 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
5352 NULL, cupsUser());
5353 ippDelete(cupsDoRequest(conn, request, "/admin/"));
5354 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
5355 debug_printf("ERROR: Failed setting CUPS default printer to '%s': %s\n",
5356 printer, cupsLastErrorString());
5357 return -1;
5358 }
5359 debug_printf("Successfully set CUPS default printer to '%s'\n",
5360 printer);
5361 return 0;
5362 }
5363
5364 char*
get_cups_default_printer()5365 get_cups_default_printer() {
5366 ipp_t *request, *response;
5367 ipp_attribute_t *attr;
5368 const char *default_printer_name = NULL;
5369 char *name_string;
5370 http_t *conn = NULL;
5371
5372 conn = http_connect_local ();
5373 if (conn == NULL) {
5374 debug_printf("Cannot connect to local CUPS to find out which is the default printer.\n");
5375 return NULL;
5376 }
5377
5378 request = ippNewRequest(CUPS_GET_DEFAULT);
5379 /* Default user */
5380 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
5381 "requesting-user-name", NULL, cupsUser());
5382 /* Do it */
5383 response = cupsDoRequest(conn, request, "/");
5384 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE || !response) {
5385 debug_printf("Could not determine system default printer!\n");
5386 } else {
5387 for (attr = ippFirstAttribute(response); attr != NULL;
5388 attr = ippNextAttribute(response)) {
5389 while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
5390 attr = ippNextAttribute(response);
5391 if (attr) {
5392 for (; attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER;
5393 attr = ippNextAttribute(response)) {
5394 if (!strcasecmp(ippGetName(attr), "printer-name") &&
5395 ippGetValueTag(attr) == IPP_TAG_NAME) {
5396 default_printer_name = ippGetString(attr, 0, NULL);
5397 break;
5398 }
5399 }
5400 }
5401 if (default_printer_name)
5402 break;
5403 }
5404 }
5405
5406 if (default_printer_name != NULL) {
5407 name_string = strdup(default_printer_name);
5408 } else {
5409 name_string = NULL;
5410 }
5411
5412 ippDelete(response);
5413
5414 return name_string;
5415 }
5416
5417 int
is_cups_default_printer(const char * printer)5418 is_cups_default_printer(const char *printer) {
5419 if (printer == NULL)
5420 return 0;
5421 char *cups_default = get_cups_default_printer();
5422 if (cups_default == NULL)
5423 return 0;
5424 if (!strcasecmp(printer, cups_default)) {
5425 free(cups_default);
5426 return 1;
5427 }
5428 free(cups_default);
5429 return 0;
5430 }
5431
5432 int
invalidate_default_printer(int local)5433 invalidate_default_printer(int local) {
5434 const char *filename = local ? local_default_printer_file :
5435 remote_default_printer_file;
5436 unlink(filename);
5437 return 0;
5438 }
5439
5440 int
record_default_printer(const char * printer,int local)5441 record_default_printer(const char *printer, int local) {
5442 FILE *fp = NULL;
5443 const char *filename = local ? local_default_printer_file :
5444 remote_default_printer_file;
5445
5446 if (printer == NULL || strlen(printer) == 0)
5447 return invalidate_default_printer(local);
5448
5449 fp = fopen(filename, "w+");
5450 if (fp == NULL) {
5451 debug_printf("ERROR: Failed creating file %s\n",
5452 filename);
5453 invalidate_default_printer(local);
5454 return -1;
5455 }
5456 fprintf(fp, "%s", printer);
5457 fclose(fp);
5458
5459 return 0;
5460 }
5461
5462 char*
retrieve_default_printer(int local)5463 retrieve_default_printer(int local) {
5464 FILE *fp = NULL;
5465 const char *filename = local ? local_default_printer_file :
5466 remote_default_printer_file;
5467 const char *printer = NULL;
5468 char *p, buf[1024];
5469 int n;
5470
5471 fp = fopen(filename, "r");
5472 if (fp == NULL) {
5473 debug_printf("Failed reading file %s\n",
5474 filename);
5475 return NULL;
5476 }
5477 p = buf;
5478 n = fscanf(fp, "%s", p);
5479 if (n == 1) {
5480 if (strlen(p) > 0)
5481 printer = p;
5482 }
5483 fclose(fp);
5484
5485 return (printer ? strdup(printer) : NULL);
5486 }
5487
5488 int
invalidate_printer_options(const char * printer)5489 invalidate_printer_options(const char *printer) {
5490 char filename[1024];
5491
5492 snprintf(filename, sizeof(filename), save_options_file,
5493 printer);
5494 unlink(filename);
5495 return 0;
5496 }
5497
5498 char*
loadPPD(http_t * http,const char * name)5499 loadPPD(http_t *http,
5500 const char *name)
5501 {
5502 /* This function replaces cupsGetPPD2(), but is much simplified
5503 (does not support classes) and works with non-standard (!= 631)
5504 ports */
5505
5506 char uri[HTTP_MAX_URI];
5507 char *resource;
5508 int fd, status;
5509 char tempfile[1024] = "";
5510
5511 /* Download URI and resource for the PPD file */
5512 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "http", NULL,
5513 "localhost", 0, "/printers/%s.ppd", name);
5514 resource = strstr(uri, "/printers/");
5515
5516 /* Download the file */
5517 fd = cupsTempFd(tempfile, sizeof(tempfile));
5518 status = cupsGetFd(http, resource, fd);
5519 close(fd);
5520
5521 /* Check for errors */
5522 if (status == HTTP_STATUS_OK)
5523 {
5524 if (tempfile[0])
5525 return(strdup(tempfile));
5526 }
5527 else if (tempfile[0])
5528 unlink(tempfile);
5529 return NULL;
5530 }
5531
5532 int
record_printer_options(const char * printer)5533 record_printer_options(const char *printer) {
5534 remote_printer_t *p;
5535 char filename[1024];
5536 FILE *fp = NULL;
5537 char uri[HTTP_MAX_URI], *resource;
5538 ipp_t *request, *response;
5539 ipp_attribute_t *attr;
5540 const char *key;
5541 char buf[65536], *c;
5542 char *ppdname = NULL;
5543 ppd_file_t *ppd;
5544 ppd_option_t *ppd_opt;
5545 cups_option_t *option;
5546 int i;
5547 /* List of IPP attributes to get recorded */
5548 static const char *attrs_to_record[] =
5549 {
5550 "*-default",
5551 "auth-info-required",
5552 /*"device-uri",*/
5553 "job-quota-period",
5554 "job-k-limit",
5555 "job-page-limit",
5556 /*"port-monitor",*/
5557 "printer-error-policy",
5558 "printer-info",
5559 "printer-is-accepting-jobs",
5560 "printer-is-shared",
5561 "printer-geo-location",
5562 "printer-location",
5563 "printer-op-policy",
5564 "printer-organization",
5565 "printer-organizational-unit",
5566 /*"printer-state",
5567 "printer-state-message",
5568 "printer-state-reasons",*/
5569 "requesting-user-name-allowed",
5570 "requesting-user-name-denied",
5571 NULL
5572 };
5573 const char **ptr;
5574 http_t *conn = NULL;
5575
5576 if (printer == NULL || strlen(printer) == 0)
5577 return 0;
5578
5579 /* Get our data about this printer */
5580 p = printer_record(printer);
5581
5582 if (p == NULL) {
5583 debug_printf("Not recording printer options for %s: Unknown printer!\n",
5584 printer);
5585 return 0;
5586 }
5587
5588 if (p->status == STATUS_TO_BE_RELEASED) {
5589 debug_printf("Not recording printer options for externally modified printer %s.\n",
5590 printer);
5591 return 0;
5592 }
5593
5594 snprintf(filename, sizeof(filename), save_options_file,
5595 printer);
5596
5597 debug_printf("Recording printer options for %s to %s\n",
5598 printer, filename);
5599
5600 conn = http_connect_local ();
5601 if (conn) {
5602 /* If there is a PPD file for this printer, we save the local
5603 settings for the PPD options. */
5604 if (cups_notifier != NULL || (p && p->netprinter)) {
5605 if ((ppdname = loadPPD(conn, printer)) == NULL) {
5606 debug_printf("Unable to get PPD file for %s: %s\n",
5607 printer, cupsLastErrorString());
5608 } else if ((ppd = ppdOpenFile(ppdname)) == NULL) {
5609 unlink(ppdname);
5610 debug_printf("Unable to open PPD file for %s.\n",
5611 printer);
5612 } else {
5613 debug_printf("Recording option settings of the PPD file for %s (%s):\n",
5614 printer, ppd->nickname);
5615 ppdMarkDefaults(ppd);
5616 for (ppd_opt = ppdFirstOption(ppd); ppd_opt;
5617 ppd_opt = ppdNextOption(ppd))
5618 if (strcasecmp(ppd_opt->keyword, "PageRegion") != 0) {
5619 debug_printf(" %s=%s\n",
5620 ppd_opt->keyword, ppd_opt->defchoice);
5621 strncpy(buf, ppd_opt->keyword, sizeof(buf));
5622 p->num_options = cupsAddOption(buf, ppd_opt->defchoice,
5623 p->num_options, &(p->options));
5624 }
5625 ppdClose(ppd);
5626 unlink(ppdname);
5627 }
5628 }
5629
5630 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
5631 "localhost", 0, "/printers/%s", printer);
5632 resource = uri + (strlen(uri) - strlen(printer) - 10);
5633 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
5634 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
5635 uri);
5636 response = cupsDoRequest(conn, request, resource);
5637
5638 /* Write all supported printer attributes */
5639 if (response) {
5640 debug_printf("Recording option settings from the IPP attributes for %s:\n",
5641 printer);
5642 attr = ippFirstAttribute(response);
5643 while (attr) {
5644 key = ippGetName(attr);
5645 for (ptr = attrs_to_record; *ptr; ptr++)
5646 if (strcasecmp(key, *ptr) == 0 ||
5647 (*ptr[0] == '*' &&
5648 strcasecmp(key + strlen(key) - strlen(*ptr) + 1, *ptr + 1) == 0))
5649 break;
5650 if (*ptr != NULL) {
5651 if (strcasecmp(key, CUPS_BROWSED_DEST_PRINTER "-default") != 0) {
5652 ippAttributeString(attr, buf, sizeof(buf));
5653 buf[sizeof(buf) - 1] = '\0';
5654 c = buf;
5655 while (*c) {
5656 if (*c == '\\')
5657 memmove(c, c + 1, strlen(c));
5658 if (*c) c ++;
5659 }
5660 debug_printf(" %s=%s\n", key, buf);
5661 p->num_options = cupsAddOption(key, buf, p->num_options,
5662 &(p->options));
5663 }
5664 }
5665 attr = ippNextAttribute(response);
5666 }
5667 ippDelete(response);
5668 }
5669 } else {
5670 debug_printf("Cannot connect to local CUPS to read out the IPP and PPD attributes for printer %s.\n",
5671 printer);
5672 }
5673
5674 if (ppdname)
5675 free(ppdname);
5676
5677 if (p->num_options > 0) {
5678 fp = fopen(filename, "w+");
5679 if (fp == NULL) {
5680 debug_printf("ERROR: Failed creating file %s: %s\n",
5681 filename, strerror(errno));
5682 return -1;
5683 }
5684
5685 for (i = p->num_options, option = p->options; i > 0; i --, option ++)
5686 if (fprintf (fp, "%s=%s\n", option->name, option->value) < 0) {
5687 debug_printf("ERROR: Failed to write into file %s: %s\n",
5688 filename, strerror(errno));
5689 fclose(fp);
5690 return -1;
5691 }
5692
5693 fclose(fp);
5694
5695 return 0;
5696 } else
5697 return -1;
5698 }
5699
5700 int
load_printer_options(const char * printer,int num_options,cups_option_t ** options)5701 load_printer_options(const char *printer, int num_options,
5702 cups_option_t **options) {
5703 char filename[1024];
5704 FILE *fp = NULL;
5705 char *opt = NULL, *val;
5706 size_t optlen = 0;
5707
5708 if (printer == NULL || strlen(printer) == 0 || options == NULL)
5709 return 0;
5710
5711 /* Prepare reading file with saved option settings */
5712 snprintf(filename, sizeof(filename), save_options_file,
5713 printer);
5714
5715 debug_printf("Loading saved printer options for %s from %s\n",
5716 printer, filename);
5717
5718 /* Open the file with the saved option settings for this print queue */
5719 fp = fopen(filename, "r");
5720 if (fp == NULL) {
5721 debug_printf("Failed reading file %s, probably no options recorded yet\n",
5722 filename);
5723 } else {
5724 /* Now read the lines of the file and add each setting to our request */
5725 errno = 0;
5726 debug_printf("Loading following option settings for printer %s:\n",
5727 printer);
5728 while (getline(&opt, &optlen, fp) != -1) {
5729 if (strlen(opt) > 1 && (val = strchr(opt, '=')) != NULL) {
5730 *val = '\0';
5731 val ++;
5732 val[strlen(val)-1] = '\0';
5733 debug_printf(" %s=%s\n", opt, val);
5734 num_options = cupsAddOption(opt, val, num_options, options);
5735 }
5736 }
5737 debug_printf("\n");
5738 if (errno != 0)
5739 debug_printf("Failed reading saved options file %s: %s\n",
5740 filename, strerror(errno));
5741 free(opt);
5742 fclose(fp);
5743 }
5744 return (num_options);
5745 }
5746
5747 int
queue_creation_handle_default(const char * printer)5748 queue_creation_handle_default(const char *printer) {
5749 /* No default printer management if we cannot get D-Bus notifications
5750 from CUPS */
5751 if (cups_notifier == NULL)
5752 return 0;
5753 /* If this queue is recorded as the former default queue (and the current
5754 default is local), set it as default (the CUPS notification handler
5755 will record the local default printer then) */
5756 char *recorded_default = retrieve_default_printer(0);
5757 if (recorded_default == NULL || strcasecmp(recorded_default, printer)) {
5758 if (recorded_default) free(recorded_default);
5759 return 0;
5760 }
5761 free(recorded_default);
5762 char *current_default = get_cups_default_printer();
5763 if (current_default == NULL || !is_created_by_cups_browsed(current_default)) {
5764 if (set_cups_default_printer(printer) < 0) {
5765 debug_printf("ERROR: Could not set former default printer %s as default again.\n",
5766 printer);
5767 free(current_default);
5768 return -1;
5769 } else {
5770 debug_printf("Former default printer %s re-appeared, set as default again.\n",
5771 printer);
5772 invalidate_default_printer(0);
5773 }
5774 }
5775 free(current_default);
5776 return 0;
5777 }
5778
5779 int
queue_removal_handle_default(const char * printer)5780 queue_removal_handle_default(const char *printer) {
5781 /* No default printer management if we cannot get D-Bus notifications
5782 from CUPS */
5783 if (cups_notifier == NULL)
5784 return 0;
5785 /* If the queue is the default printer, get back
5786 to the recorded local default printer, record this queue for getting the
5787 default set to this queue again if it re-appears. */
5788 /* We call this also if a queue is only conserved because on cups-browsed
5789 shutdown it still has jobs */
5790 if (!is_cups_default_printer(printer))
5791 return 0;
5792 /* Record the fact that this printer was default */
5793 if (record_default_printer(default_printer, 0) < 0) {
5794 /* Delete record file if recording failed */
5795 debug_printf("ERROR: Failed recording remote default printer (%s). Removing the file with possible old recording.\n",
5796 printer);
5797 invalidate_default_printer(0);
5798 } else
5799 debug_printf("Recorded the fact that the current printer (%s) is the default printer before deleting the queue and returning to the local default printer.\n",
5800 printer);
5801 /* Switch back to a recorded local printer, if available */
5802 char *local_default = retrieve_default_printer(1);
5803 if (local_default != NULL) {
5804 if (set_cups_default_printer(local_default) >= 0) {
5805 debug_printf("Switching back to %s as default printer.\n",
5806 local_default);
5807 free(local_default);
5808 } else {
5809 debug_printf("ERROR: Unable to switch back to %s as default printer.\n",
5810 local_default);
5811 free(local_default);
5812 return -1;
5813 }
5814 }
5815 invalidate_default_printer(1);
5816 return 0;
5817 }
5818
5819 static char *
get_local_queue_name(const char * service_name,const char * make_model,const char * resource,const char * remote_host,int * is_cups_queue,const char * exclude)5820 get_local_queue_name(const char *service_name,
5821 const char *make_model,
5822 const char *resource,
5823 const char *remote_host,
5824 int *is_cups_queue,
5825 const char *exclude) {
5826 char *queue_name = NULL, *backup_queue_name = NULL,
5827 *local_queue_name = NULL, *local_queue_name_lower = NULL;
5828 local_printer_t *local_printer = NULL;
5829 cluster_t *cluster = NULL;
5830 char *member = NULL, *str = NULL;
5831
5832 if (*is_cups_queue) {
5833 /* This is a remote CUPS printer */
5834 /* Determine the queue name */
5835 if (LocalQueueNamingRemoteCUPS == LOCAL_QUEUE_NAMING_MAKE_MODEL &&
5836 make_model)
5837 /* Works only with DNS-SD-discovered queues as otherwise we have no
5838 make/model info */
5839 queue_name = remove_bad_chars(make_model, 0);
5840 else if (LocalQueueNamingRemoteCUPS == LOCAL_QUEUE_NAMING_REMOTE_NAME)
5841 /* Not directly used in script generation input later, but taken from
5842 packet, so better safe than sorry. (consider second loop with
5843 backup_queue_name) */
5844 queue_name = remove_bad_chars(strrchr(resource, '/') + 1, 0);
5845 else
5846 /* Convert DNS-SD service name into a CUPS queue name exactly
5847 as CUPS would do it, to override CUPS' own temporary queue
5848 generation mechanism */
5849 queue_name = remove_bad_chars(service_name, 2);
5850 } else {
5851 /* This is an IPP-based network printer */
5852 /* Determine the queue name */
5853 if (LocalQueueNamingIPPPrinter == LOCAL_QUEUE_NAMING_MAKE_MODEL &&
5854 make_model)
5855 /* Works only if we actually have make/model info in the DNS-SD record*/
5856 queue_name = remove_bad_chars(make_model, 0);
5857 else
5858 /* Convert DNS-SD service name into a CUPS queue name exactly
5859 as CUPS would do it, to override CUPS' own temporary queue
5860 generation mechanism */
5861 queue_name = remove_bad_chars(service_name, 2);
5862 }
5863 /* Check if there exists already a CUPS queue with the
5864 requested name Try name@host in such a case and if
5865 this is also taken, ignore the printer */
5866
5867 /* Get available CUPS queues */
5868 update_local_printers ();
5869
5870 /* We skip trying to use the queue name purely derived from the
5871 remote CUPS queue name or make and model for remote CUPS queues
5872 when automatic clustering of remote CUPS queues is turned off,
5873 to directly create queues with names containing the server name
5874 to avoid name clashes and with this remote queues skipped by
5875 cups-browsed. */
5876 if ((!*is_cups_queue ||
5877 AutoClustering == 1 ||
5878 LocalQueueNamingRemoteCUPS == LOCAL_QUEUE_NAMING_DNSSD) &&
5879 (!exclude || strcasecmp(queue_name, exclude))) {
5880 /* Is there a local queue with the name of the remote queue? */
5881 local_queue_name_lower = g_ascii_strdown(queue_name, -1);
5882 local_printer = g_hash_table_lookup (local_printers,
5883 local_queue_name_lower);
5884 free(local_queue_name_lower);
5885 /* To decide on whether the queue name is already taken, only
5886 consider CUPS queues not created by us. */
5887 if (local_printer && !local_printer->cups_browsed_controlled) {
5888 debug_printf("Queue name %s already taken.\n",
5889 queue_name);
5890 local_queue_name = NULL;
5891 } else
5892 local_queue_name = strdup(queue_name);
5893 }
5894 /* Use the originally chosen queue name plus the server name if the
5895 original name is already taken or if we had skipped using it. Do
5896 this only if we do not use DNS-SD-service-name-based naming. */
5897 if (!local_queue_name &&
5898 (!*is_cups_queue ||
5899 LocalQueueNamingRemoteCUPS != LOCAL_QUEUE_NAMING_DNSSD) &&
5900 (is_cups_queue ||
5901 LocalQueueNamingIPPPrinter != LOCAL_QUEUE_NAMING_DNSSD)) {
5902 if ((backup_queue_name = malloc((strlen(queue_name) +
5903 strlen(remote_host) + 2) *
5904 sizeof(char))) == NULL) {
5905 debug_printf("ERROR: Unable to allocate memory.\n");
5906 exit(1);
5907 }
5908 sprintf(backup_queue_name, "%s@%s", queue_name, remote_host);
5909 local_queue_name = backup_queue_name;
5910 debug_printf("Using fallback queue name: %s\n",
5911 local_queue_name);
5912 /* Is there a local queue with the name <queue>@<host>? */
5913 local_queue_name_lower = g_ascii_strdown(local_queue_name, -1);
5914 local_printer = g_hash_table_lookup (local_printers,
5915 local_queue_name_lower);
5916 free(local_queue_name_lower);
5917 if ((local_printer && !local_printer->cups_browsed_controlled) ||
5918 (exclude && !strcasecmp(local_queue_name, exclude))) {
5919 /* Found also a local queue with name <queue>@<host> (or
5920 this name is explicitly excluded), so ignore this remote
5921 printer */
5922 debug_printf("%s also taken, printer ignored.\n",
5923 local_queue_name);
5924 free(backup_queue_name);
5925 local_queue_name = NULL;
5926 }
5927 }
5928 free(queue_name);
5929 if (!local_queue_name) {
5930 debug_printf("No suitable local queue name found, printer ignored.\n");
5931 return NULL;
5932 }
5933
5934 /* Check whether our new printer matches one of the user-defined
5935 printer clusters */
5936 for (cluster = cupsArrayFirst(clusters);
5937 cluster;
5938 cluster = cupsArrayNext(clusters)) {
5939 if (exclude && !strcasecmp(cluster->local_queue_name, exclude))
5940 continue;
5941 local_queue_name_lower = g_ascii_strdown(cluster->local_queue_name, -1);
5942 local_printer = g_hash_table_lookup (local_printers,
5943 local_queue_name_lower);
5944 free(local_queue_name_lower);
5945 if (local_printer && !local_printer->cups_browsed_controlled)
5946 continue;
5947 for (member = cupsArrayFirst(cluster->members);
5948 member;
5949 member = cupsArrayNext(cluster->members)) {
5950 /* Match remote CUPS queue name */
5951 if ((str = strrchr(resource, '/')) != NULL && strlen(str) > 1) {
5952 str = remove_bad_chars(str + 1, 2);
5953 if (strcasecmp(member, str) == 0) /* Match */
5954 break;
5955 free(str);
5956 }
5957 /* Match make and model */
5958 if (make_model) {
5959 str = remove_bad_chars(make_model, 2);
5960 if (strcasecmp(member, str) == 0) /* Match */
5961 break;
5962 free(str);
5963 }
5964 /* Match DNS-SD service name */
5965 if (service_name) {
5966 str = remove_bad_chars(service_name, 2);
5967 if (strcasecmp(member, str) == 0) /* Match */
5968 break;
5969 free(str);
5970 }
5971 }
5972 if (member)
5973 break;
5974 }
5975 if (cluster) {
5976 local_queue_name = strdup(cluster->local_queue_name);
5977 *is_cups_queue = 2;
5978 free(str);
5979 } else if (AutoClustering) {
5980 /* If we do automatic clustering by matching queue names, do not
5981 add a queue to a manually defined cluster because it matches
5982 the cluster's local queue name. Manually defined clusters can
5983 only be joined by printers which match one of the cluster's
5984 member names */
5985 for (cluster = cupsArrayFirst(clusters);
5986 cluster;
5987 cluster = cupsArrayNext(clusters)) {
5988 if (strcasecmp(local_queue_name, cluster->local_queue_name) == 0) {
5989 debug_printf("We have already a manually defined printer cluster with the name %s. Automatic clustering does not add this printer to this cluster as it does not match any of the cluster's member names. Skipping this printer.\n",
5990 local_queue_name);
5991 debug_printf("In cups-browsed.conf try \"LocalQueueNamingRemoteCUPS DNS-SD\" or give another name to your manually defined cluster (\"Cluster\" directive) to avoid name clashes.\n");
5992 free(local_queue_name);
5993 return NULL;
5994 }
5995 }
5996 }
5997 return local_queue_name;
5998 }
5999
6000 int
join_cluster_if_needed(remote_printer_t * p,int is_cups_queue)6001 join_cluster_if_needed(remote_printer_t *p,
6002 int is_cups_queue) {
6003
6004 /* is_cups_queue: -1: Unknown, 0: IPP printer, 1: Remote CUPS queue,
6005 2: Remote CUPS queue in user-defined cluster */
6006
6007 remote_printer_t *q;
6008
6009 for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
6010 q;
6011 q = (remote_printer_t *)cupsArrayNext(remote_printers))
6012 if (q != p &&
6013 !strcasecmp(q->queue_name, p->queue_name) && /* Queue with same name
6014 on server */
6015 !q->slave_of) /* Find the master of the queues with this name,
6016 to avoid "daisy chaining" */
6017 break;
6018 if (q && AutoClustering == 0 && (is_cups_queue == 1 ||is_cups_queue == 0) ) {
6019 debug_printf("We have already created a queue with the name %s for another remote CUPS printer but automatic clustering of equally named printers is turned off nor did we find a manually defined cluster this printer belongs to. Skipping this printer.\n", p->queue_name);
6020 debug_printf("In cups-browsed.conf try setting \"AutoClustering On\" to cluster equally-named remote CUPS printers, \"LocalQueueNamingRemoteCUPS DNS-SD\" to avoid queue name clashes, or define clusters with the \"Cluster\" directive.\n");
6021 return -1;
6022 }
6023
6024 p->slave_of = (q && q->status != STATUS_DISAPPEARED &&
6025 q->status != STATUS_UNCONFIRMED &&
6026 q->status != STATUS_TO_BE_RELEASED) ? q : NULL;
6027 if (p->slave_of) {
6028 debug_printf("Printer %s already available through host %s, port %d.\n",
6029 p->queue_name, q->host, q->port);
6030 /* Update q */
6031 q->status = STATUS_TO_BE_CREATED;
6032 q->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
6033 log_cluster(p);
6034 } else if (q) {
6035 q->slave_of = p;
6036 debug_printf("Unconfirmed/disappeared printer %s already available through host %s, port %d, marking that printer a slave of the newly found one.\n",
6037 p->queue_name, q->host, q->port);
6038 log_cluster(p);
6039 }
6040 return (q ? 1 : 0);
6041 }
6042
6043 static void
on_printer_state_changed(CupsNotifier * object,const gchar * text,const gchar * printer_uri,const gchar * printer,guint printer_state,const gchar * printer_state_reasons,gboolean printer_is_accepting_jobs,gpointer user_data)6044 on_printer_state_changed (CupsNotifier *object,
6045 const gchar *text,
6046 const gchar *printer_uri,
6047 const gchar *printer,
6048 guint printer_state,
6049 const gchar *printer_state_reasons,
6050 gboolean printer_is_accepting_jobs,
6051 gpointer user_data)
6052 {
6053 char *ptr, buf[2048];
6054
6055 debug_printf("on_printer_state_changed() in THREAD %ld\n", pthread_self());
6056
6057 debug_printf("[CUPS Notification] Printer state change on printer %s: %s\n",
6058 printer, text);
6059 debug_printf("[CUPS Notification] Printer state reasons: %s\n",
6060 printer_state_reasons);
6061
6062 if (terminating) {
6063 debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n");
6064 return;
6065 }
6066
6067 if (autoshutdown && autoshutdown_on == NO_JOBS) {
6068 if (check_jobs() == 0) {
6069 /* If auto shutdown is active for triggering on no jobs being left, we
6070 schedule the shutdown in autoshutdown_timeout seconds */
6071 if (!autoshutdown_exec_id) {
6072 debug_printf ("No jobs there any more on printers made available by us, shutting down in %d sec...\n",
6073 autoshutdown_timeout);
6074 autoshutdown_exec_id =
6075 g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
6076 NULL);
6077 }
6078 } else {
6079 /* If auto shutdown is active for triggering on no jobs being left, we
6080 cancel a shutdown in autoshutdown_timeout seconds as there are jobs
6081 again. */
6082 if (autoshutdown_exec_id) {
6083 debug_printf ("New jobs there on the printers made available by us, killing auto shutdown timer.\n");
6084 g_source_remove(autoshutdown_exec_id);
6085 autoshutdown_exec_id = 0;
6086 }
6087 }
6088 }
6089
6090 if ((ptr = strstr(text, " is now the default printer")) != NULL) {
6091 /* Default printer has changed, we are triggered by the new default
6092 printer */
6093 strncpy(buf, text, ptr - text);
6094 buf[ptr - text] = '\0';
6095 debug_printf("[CUPS Notification] Default printer changed from %s to %s.\n",
6096 default_printer, buf);
6097 if (is_created_by_cups_browsed(default_printer)) {
6098 /* Previous default printer created by cups-browsed */
6099 if (!is_created_by_cups_browsed(buf)) {
6100 /* New default printer local */
6101 /* Removed backed-up local default printer as we do not have a
6102 remote printer as default any more */
6103 invalidate_default_printer(1);
6104 debug_printf("Manually switched default printer from a cups-browsed-generated one to a local printer.\n");
6105 }
6106 } else {
6107 /* Previous default printer local */
6108 if (is_created_by_cups_browsed(buf)) {
6109 /* New default printer created by cups-browsed */
6110 /* Back up the local default printer to be able to return to it
6111 if the remote printer disappears */
6112 if (record_default_printer(default_printer, 1) < 0) {
6113 /* Delete record file if recording failed */
6114 debug_printf("ERROR: Failed recording local default printer. Removing the file with possible old recording.\n");
6115 invalidate_default_printer(1);
6116 } else
6117 debug_printf("Recorded previous default printer so that if the currently selected cups-browsed-generated one disappears, we can return to the old local one.\n");
6118 /* Remove a recorded remote printer as after manually selecting
6119 another one as default this one is not relevant any more */
6120 invalidate_default_printer(0);
6121 }
6122 }
6123 if (default_printer != NULL)
6124 free((void *)default_printer);
6125 default_printer = strdup(buf);
6126 } else if ((ptr = strstr(text, " is no longer the default printer"))
6127 != NULL) {
6128 /* Default printer has changed, we are triggered by the former default
6129 printer */
6130 strncpy(buf, text, ptr - text);
6131 buf[ptr - text] = '\0';
6132 debug_printf("[CUPS Notification] %s not default printer any more.\n", buf);
6133 }
6134 }
6135
6136 static void
on_job_state(CupsNotifier * object,const gchar * text,const gchar * printer_uri,const gchar * printer,guint printer_state,const gchar * printer_state_reasons,gboolean printer_is_accepting_jobs,guint job_id,guint job_state,const gchar * job_state_reasons,const gchar * job_name,guint job_impressions_completed,gpointer user_data)6137 on_job_state (CupsNotifier *object,
6138 const gchar *text,
6139 const gchar *printer_uri,
6140 const gchar *printer,
6141 guint printer_state,
6142 const gchar *printer_state_reasons,
6143 gboolean printer_is_accepting_jobs,
6144 guint job_id,
6145 guint job_state,
6146 const gchar *job_state_reasons,
6147 const gchar *job_name,
6148 guint job_impressions_completed,
6149 gpointer user_data)
6150 {
6151 int i, count;
6152 char buf[2048];
6153 remote_printer_t *p, *q, *r, *s=NULL;
6154 http_t *http = NULL;
6155 ipp_t *request, *response, *printer_attributes = NULL;
6156 ipp_attribute_t *attr;
6157 const char *pname = NULL;
6158 ipp_pstate_t pstate = IPP_PRINTER_IDLE;
6159 int paccept = 0;
6160 int num_jobs, min_jobs = 99999999;
6161 char destination_uri[1024];
6162 const char *dest_host = NULL;
6163 int dest_index = 0;
6164 int valid_dest_found = 0;
6165 char uri[HTTP_MAX_URI];
6166 int num_options;
6167 cups_option_t *options;
6168 int num_of_printers;
6169 char* document_format;
6170 int print_quality = 0;
6171 const char *pdl = NULL;
6172 cups_array_t *pdl_list;
6173 char resolution[32];
6174 res_t *max_res = NULL, *min_res = NULL, *res = NULL;
6175 int xres, yres;
6176 int got_printer_info;
6177 static const char *pattrs[] =
6178 {
6179 "printer-name",
6180 "printer-state",
6181 "printer-is-accepting-jobs"
6182 };
6183 http_t *conn = NULL;
6184
6185 debug_printf("on_job_state() in THREAD %ld\n", pthread_self());
6186
6187 debug_printf("[CUPS Notification] Job state changed on printer %s: %s\n",
6188 printer, text);
6189 debug_printf("[CUPS Notification] Printer state reasons: %s\n",
6190 printer_state_reasons);
6191 debug_printf("[CUPS Notification] Job ID: %d\n",
6192 job_id);
6193 debug_printf("[CUPS Notification] Job State: %s\n",
6194 job_state_reasons);
6195 debug_printf("[CUPS Notification] Job is processing: %s\n",
6196 job_state == IPP_JOB_PROCESSING ? "Yes" : "No");
6197
6198 if (terminating) {
6199 debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n");
6200 return;
6201 }
6202
6203 if (autoshutdown && autoshutdown_on == NO_JOBS) {
6204 if (check_jobs() == 0) {
6205 /* If auto shutdown is active for triggering on no jobs being left, we
6206 schedule the shutdown in autoshutdown_timeout seconds */
6207 if (!autoshutdown_exec_id) {
6208 debug_printf ("No jobs there any more on printers made available by us, shutting down in %d sec...\n", autoshutdown_timeout);
6209 autoshutdown_exec_id =
6210 g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
6211 NULL);
6212 }
6213 } else {
6214 /* If auto shutdown is active for triggering on no jobs being left, we
6215 cancel a shutdown in autoshutdown_timeout seconds as there are jobs
6216 again. */
6217 if (autoshutdown_exec_id) {
6218 debug_printf ("New jobs there on the printers made available by us, killing auto shutdown timer.\n");
6219 g_source_remove(autoshutdown_exec_id);
6220 autoshutdown_exec_id = 0;
6221 }
6222 }
6223 }
6224
6225 if (job_id != 0 && job_state == IPP_JOB_PROCESSING) {
6226 /* Printer started processing a job, check if it uses the implicitclass
6227 backend and if so, we select the remote queue to which to send the job
6228 in a way so that we get load balancing between all remote queues
6229 associated with this queue.
6230
6231 There are two methods to do that (configurable in cups-browsed.conf):
6232
6233 Queuing of jobs on the client (LoadBalancingType = QUEUE_ON_CLIENT):
6234
6235 Here we check all remote printers assigned to this printer and to its
6236 slaves which is currently accepting jobs and idle. If all are busy,
6237 we send a failure message and the backend will close with an error code
6238 after some seconds of delay, to make the job getting retried making us
6239 checking again here. If we find a destination, we tell the backend
6240 which remote queue this destination is, making the backend printing the
6241 job there immediately.
6242
6243 With this all waiting jobs get queued up on the client, on the servers
6244 there will only be the jobs which are actually printing, as we do not
6245 send jobs to a server which is already printing. This is also the
6246 method which CUPS uses for classes. Advantage is a more even
6247 distribution of the job workload on the servers, and if a server fails,
6248 there are not several jobs stuck or lost. Disadvantage is that if one
6249 takes the client (laptop, mobile phone, ...) out of the local network,
6250 printing stops with the jobs waiting in the local queue.
6251
6252 Queuing of jobs on the servers (LoadBalancingType = QUEUE_ON_SERVERS):
6253
6254 Here we check all remote printers assigned to this printer and to its
6255 slaves which is currently accepting jobs and find the one with the
6256 lowest amount of jobs waiting and send the job to there. So on the
6257 local queue we have never jobs waiting if at least one remote printer
6258 accepts jobs.
6259
6260 Not having jobs waiting locally has the advantage that we can take the
6261 local machine from the network and all jobs get printed. Disadvantage
6262 is that if a server with a full queue of jobs goes away, the jobs go
6263 away, too.
6264
6265 Default is queuing the jobs on the client as this is what CUPS does
6266 with classes. */
6267
6268 debug_printf("[CUPS Notification] %s starts processing a job.\n", printer);
6269 conn = http_connect_local ();
6270 if (conn == NULL) {
6271 debug_printf("Cannot connect to local CUPS to set destination for job in the load-balanced cluster %s.\n",
6272 printer);
6273 return;
6274 }
6275 q = printer_record(printer);
6276 /* If we hit a slave and not the master, switch to the master */
6277 if (q && q->slave_of)
6278 q = q->slave_of;
6279 if (q && q->queue_name) {
6280 /* We have remote CUPS queue(s) and so are using the implicitclass
6281 backend */
6282 debug_printf("[CUPS Notification] %s is using the \"implicitclass\" CUPS backend, so let us search for a destination for this job.\n", printer);
6283
6284 /* We keep track of the printer which we used last time and start
6285 checking with the next printer this time, to get a "round robin"
6286 type of printer usage instead of having most jobs going to the first
6287 printer in the list. Method taken from the cupsdFindAvailablePrinter()
6288 function of the scheduler/classes.c file of CUPS. */
6289
6290 if (q->last_printer < 0 ||
6291 q->last_printer >= cupsArrayCount(remote_printers))
6292 q->last_printer = 0;
6293 log_cluster(q);
6294 for (i = q->last_printer + 1; ; i++) {
6295 if (i >= cupsArrayCount(remote_printers))
6296 i = 0;
6297 p = (remote_printer_t *)cupsArrayIndex(remote_printers, i);
6298 if (!strcasecmp(p->queue_name, printer) &&
6299 p->status == STATUS_CONFIRMED) {
6300 num_of_printers = 0;
6301 for (r = (remote_printer_t *)cupsArrayFirst(remote_printers);
6302 r; r = (remote_printer_t *)cupsArrayNext(remote_printers)) {
6303 if (!strcmp(r->queue_name, q->queue_name)) {
6304 if(r->status == STATUS_DISAPPEARED ||
6305 r->status == STATUS_UNCONFIRMED ||
6306 r->status == STATUS_TO_BE_RELEASED )
6307 continue;
6308 num_of_printers ++;
6309 }
6310 }
6311
6312 /* If we are in a cluster, see whether the printer supports the
6313 requested job attributes*/
6314 if (num_of_printers > 1) {
6315 if (!supports_job_attributes_requested(printer, i, job_id,
6316 &print_quality)) {
6317 debug_printf("Printer with uri %s in cluster %s doesn't support the requested job attributes\n",
6318 p->uri, p->queue_name);
6319 if (i == q->last_printer)
6320 break;
6321 else
6322 continue;
6323 }
6324 }
6325 debug_printf("Checking state of remote printer %s on host %s, IP %s, port %d.\n",
6326 p->uri, p->host, p->ip, p->port);
6327
6328 /* Check whether the printer is idle, processing, or disabled */
6329 debug_printf("HTTP connection to %s:%d established.\n", p->host,
6330 p->port);
6331 response = get_printer_attributes(p->uri, pattrs,
6332 sizeof(pattrs) / sizeof(pattrs[0]),
6333 NULL, 0, 0);
6334 debug_log_out(get_printer_attributes_log);
6335 if (response != NULL) {
6336 debug_printf("IPP request to %s:%d successful.\n", p->host,
6337 p->port);
6338 pname = NULL;
6339 pstate = IPP_PRINTER_IDLE;
6340 paccept = 0;
6341 for (attr = ippFirstAttribute(response); attr != NULL;
6342 attr = ippNextAttribute(response)) {
6343 while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
6344 attr = ippNextAttribute(response);
6345 if (attr == NULL)
6346 break;
6347 pname = NULL;
6348 pstate = IPP_PRINTER_IDLE;
6349 paccept = 0;
6350 got_printer_info = 0;
6351 while (attr != NULL && ippGetGroupTag(attr) ==
6352 IPP_TAG_PRINTER) {
6353 if (!strcmp(ippGetName(attr), "printer-name") &&
6354 ippGetValueTag(attr) == IPP_TAG_NAME) {
6355 pname = ippGetString(attr, 0, NULL);
6356 } else if (!strcmp(ippGetName(attr), "printer-state") &&
6357 ippGetValueTag(attr) == IPP_TAG_ENUM)
6358 pstate = (ipp_pstate_t)ippGetInteger(attr, 0);
6359 else if (!strcmp(ippGetName(attr),
6360 "printer-is-accepting-jobs") &&
6361 ippGetValueTag(attr) == IPP_TAG_BOOLEAN) {
6362 paccept = ippGetBoolean(attr, 0);
6363 got_printer_info = 1;
6364 }
6365 attr = ippNextAttribute(response);
6366 }
6367 if (got_printer_info == 0) {
6368 if (attr == NULL)
6369 break;
6370 else
6371 continue;
6372 }
6373 debug_printf("IPP Response contains attributes values printer-name %s, accepting-job %d\n",
6374 (pname ? pname : "(Not reported)"), paccept);
6375 if (paccept) {
6376 debug_printf("Printer %s on host %s, port %d is accepting jobs.\n",
6377 p->uri, p->host, p->port);
6378 switch (pstate) {
6379 case IPP_PRINTER_IDLE:
6380 valid_dest_found = 1;
6381 dest_host = p->ip ? p->ip : p->host;
6382 strncpy(destination_uri, p->uri, sizeof(destination_uri) - 1);
6383 printer_attributes = p->prattrs;
6384 pdl = p->pdl;
6385 s = p;
6386 dest_index = i;
6387 debug_printf("Printer %s on host %s, port %d is idle, take this as destination and stop searching.\n",
6388 p->uri, p->host, p->port);
6389 break;
6390 case IPP_PRINTER_PROCESSING:
6391 valid_dest_found = 1;
6392 if (LoadBalancingType == QUEUE_ON_SERVERS) {
6393 num_jobs = 0;
6394 http =
6395 httpConnectEncryptShortTimeout (p->ip ? p->ip : p->host,
6396 p->port,
6397 HTTP_ENCRYPT_IF_REQUESTED);
6398 if (http) {
6399 num_jobs = get_number_of_jobs(http, p->uri, 0,
6400 CUPS_WHICHJOBS_ACTIVE);
6401 if (num_jobs >= 0 && num_jobs < min_jobs) {
6402 min_jobs = num_jobs;
6403 dest_host = p->ip ? p->ip : p->host;
6404 strncpy(destination_uri, p->uri,
6405 sizeof(destination_uri) - 1);
6406 printer_attributes = p->prattrs;
6407 pdl = p->pdl;
6408 s = p;
6409 dest_index = i;
6410 }
6411 debug_printf("Printer %s on host %s, port %d is printing and it has %d jobs.\n",
6412 p->uri, p->host, p->port,
6413 num_jobs);
6414 httpClose(http);
6415 http = NULL;
6416 }
6417 } else
6418 debug_printf("Printer %s on host %s, port %d is printing.\n",
6419 p->uri, p->host, p->port);
6420 break;
6421 case IPP_PRINTER_STOPPED:
6422 debug_printf("Printer %s on host %s, port %d is disabled, skip it.\n",
6423 p->uri, p->host, p->port);
6424 break;
6425 }
6426 } else {
6427 debug_printf("Printer %s on host %s, port %d is not accepting jobs, skip it.\n",
6428 p->uri, p->host, p->port);
6429 }
6430 break;
6431 }
6432
6433 ippDelete(response);
6434 response = NULL;
6435
6436 if (pstate == IPP_PRINTER_IDLE && paccept) {
6437 q->last_printer = i;
6438 break;
6439 }
6440 } else
6441 debug_printf("IPP request to %s:%d failed.\n", p->host,
6442 p->port);
6443 }
6444 if (i == q->last_printer)
6445 break;
6446 }
6447
6448 /* Write the selected destination host into an option of our implicit
6449 class queue (cups-browsed-dest-printer="<dest>") so that the
6450 implicitclass backend will pick it up */
6451
6452 if ((pdl_list = cupsArrayNew3((cups_array_func_t)strcasecmp,
6453 NULL, NULL, 0,
6454 (cups_acopy_func_t)strdup,
6455 (cups_afree_func_t)free)) == NULL){
6456 debug_printf("Could Not allocate memory for cups Array \n");
6457 return;
6458 }
6459
6460 /* Finding the best pdl supported by the printer, we need to send the
6461 document format to the implictclass backend */
6462 if (((attr = ippFindAttribute(printer_attributes,
6463 "document-format-supported",
6464 IPP_TAG_MIMETYPE)) != NULL) ||
6465 (pdl && pdl[0] != '\0')) {
6466 const char *format = pdl;
6467 i = 0;
6468 count = ippGetCount(attr);
6469 while ((attr && i < count) || /* Go through formats in attribute */
6470 (!attr && pdl && pdl[0] != '\0' && format[0] != '\0')) {
6471 /* Go through formats in pdl string (from DNS-SD record) */
6472 /* Pick next format from attribute */
6473 if (attr) format = ippGetString(attr, i, NULL);
6474 /* Add format to list of supported PDLs, skip duplicates */
6475 if (!cupsArrayFind(pdl_list, (void *)format))
6476 cupsArrayAdd(pdl_list, (void *)format);
6477 if (attr)
6478 /* Next format in attribute */
6479 i ++;
6480 else {
6481 /* Find the next format in the string pdl, if there is none left,
6482 go to the terminating zero */
6483 while (!isspace(*format) && *format != ',' && *format != '\0')
6484 format ++;
6485 while ((isspace(*format) || *format == ',') && *format != '\0')
6486 format ++;
6487 }
6488 }
6489 }
6490
6491 /* The priority order for the PDLs is the same as in the
6492 PPD generator in cupsfilters/ppdgenerator.c */
6493 document_format = (char *)malloc(sizeof(char) * 32);
6494 if (cupsArrayFind(pdl_list, "application/vnd.cups-pdf") ||
6495 cupsArrayFind(pdl_list, "application/pdf"))
6496 strcpy(document_format, "pdf");
6497 #ifdef CUPS_RASTER_HAVE_APPLERASTER
6498 else if (cupsArrayFind(pdl_list, "image/urf"))
6499 strcpy(document_format, "apple-raster");
6500 #endif
6501 else if (cupsArrayFind(pdl_list, "image/pwg-raster"))
6502 strcpy(document_format, "raster");
6503 #ifdef QPDF_HAVE_PCLM
6504 else if (cupsArrayFind(pdl_list, "application/PCLm"))
6505 strcpy(document_format, "pclm");
6506 #endif
6507 else if (cupsArrayFind(pdl_list, "application/vnd.hp-pclxl"))
6508 strcpy(document_format, "pclxl");
6509 else if (cupsArrayFind(pdl_list, "application/vnd.cups-postscript") ||
6510 cupsArrayFind(pdl_list, "application/postscript"))
6511 strcpy(document_format, "postscript");
6512 else if (cupsArrayFind(pdl_list, "application/vnd.hp-pcl") ||
6513 cupsArrayFind(pdl_list, "application/pcl") ||
6514 cupsArrayFind(pdl_list, "application/x-pcl"))
6515 strcpy(document_format, "pcl");
6516
6517 if (pdl_list)
6518 cupsArrayDelete(pdl_list);
6519
6520 /* Deciding the resolution to be sent with the job */
6521 /* Finding the minimum and maximum resolution supported by the printer */
6522
6523 max_res = resolutionNew(0, 0);
6524 min_res = resolutionNew(0, 0);
6525
6526 if (s &&
6527 ((attr = ippFindAttribute(s->prattrs, "printer-resolution-supported",
6528 IPP_TAG_RESOLUTION)) != NULL)) {
6529 for (i = 0, count = ippGetCount(attr); i < count; i ++) {
6530 if ((res = ippResolutionToRes(attr, i)) != NULL) {
6531 debug_printf("%d %d\n",res->x,res->y);
6532 if (i == 0) {
6533 max_res->x = res->x;
6534 max_res->y = res->y;
6535 min_res->x = res->x;
6536 min_res->y = res->y;
6537 } else {
6538 if(compare_resolutions((void *)res,(void *)max_res,NULL) > 0) {
6539 max_res->x = res->x;
6540 max_res->y = res->y;
6541 }
6542 if(compare_resolutions((void *)res,(void *)min_res,NULL) < 0) {
6543 min_res->x = res->x;
6544 min_res->y = res->y;
6545 }
6546 }
6547 free_resolution(res, NULL);
6548 res = NULL;
6549 }
6550 }
6551 }
6552
6553 /* If we are requesting normal print quality then send default
6554 resolution, for draft send minimum resolution and for high,
6555 send the maximum resolution */
6556 /* If none of the below dpi is selected then default dpi will be
6557 sent as 600 */
6558 snprintf(resolution,sizeof(resolution), "600dpi");
6559 if (s && print_quality == 3) {
6560 if (min_res != NULL){
6561 if (min_res->x == min_res->y)
6562 snprintf(resolution,sizeof(resolution), "%ddpi", min_res->x);
6563 else
6564 snprintf(resolution,sizeof(resolution), "%dx%ddpi", min_res->x,
6565 min_res->y);
6566 }
6567 } else if (s && print_quality == 5) {
6568 if (max_res != NULL) {
6569 if (max_res->x == max_res->y)
6570 snprintf(resolution, sizeof(resolution), "%ddpi", max_res->x);
6571 else
6572 snprintf(resolution, sizeof(resolution), "%dx%ddpi", max_res->x,
6573 max_res->y);
6574 }
6575 } else if (s) {
6576 if ((attr = ippFindAttribute(s->prattrs, "printer-resolution-default",
6577 IPP_TAG_ZERO)) != NULL) {
6578 if ((res = ippResolutionToRes(attr, 0)) != NULL) {
6579 xres = res->x;
6580 yres = res->y;
6581 if (xres == yres)
6582 snprintf(resolution, sizeof(resolution), "%ddpi", xres);
6583 else
6584 snprintf(resolution, sizeof(resolution), "%dx%ddpi", xres, yres);
6585 free_resolution(res, NULL);
6586 }
6587 }
6588 }
6589
6590 free_resolution(max_res, NULL);
6591 free_resolution(min_res, NULL);
6592
6593 request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
6594 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
6595 "localhost", 0, "/printers/%s", printer);
6596 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
6597 "printer-uri", NULL, uri);
6598 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
6599 "requesting-user-name", NULL, cupsUser());
6600 if (dest_host) {
6601 q->last_printer = dest_index;
6602 snprintf(buf, sizeof(buf), "\"%d %s %s %s\"", job_id, destination_uri,
6603 document_format, resolution);
6604 debug_printf("Destination for job %d to %s: %s\n",
6605 job_id, printer, destination_uri);
6606 } else if (valid_dest_found == 1) {
6607 snprintf(buf, sizeof(buf), "\"%d ALL_DESTS_BUSY\"", job_id);
6608 debug_printf("All destinations busy for job %d to %s\n",
6609 job_id, printer);
6610 } else {
6611 snprintf(buf, sizeof(buf), "\"%d NO_DEST_FOUND\"", job_id);
6612 debug_printf("No destination found for job %d to %s\n",
6613 job_id, printer);
6614 }
6615 num_options = 0;
6616 options = NULL;
6617 num_options = cupsAddOption(CUPS_BROWSED_DEST_PRINTER "-default", buf,
6618 num_options, &options);
6619 cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
6620 cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
6621 ippDelete(cupsDoRequest(conn, request, "/admin/"));
6622
6623 cupsFreeOptions(num_options, options);
6624 free(document_format);
6625
6626 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
6627 debug_printf("ERROR: Unable to set \"" CUPS_BROWSED_DEST_PRINTER
6628 "-default\" option to communicate the destination server for this job (%s)!\n",
6629 cupsLastErrorString());
6630 return;
6631 }
6632 }
6633 }
6634 }
6635
6636 static void
on_printer_deleted(CupsNotifier * object,const gchar * text,const gchar * printer_uri,const gchar * printer,guint printer_state,const gchar * printer_state_reasons,gboolean printer_is_accepting_jobs,gpointer user_data)6637 on_printer_deleted (CupsNotifier *object,
6638 const gchar *text,
6639 const gchar *printer_uri,
6640 const gchar *printer,
6641 guint printer_state,
6642 const gchar *printer_state_reasons,
6643 gboolean printer_is_accepting_jobs,
6644 gpointer user_data)
6645 {
6646 remote_printer_t *p;
6647 char *r;
6648 char *local_queue_name_lower = NULL;
6649 local_printer_t *local_printer = NULL;
6650
6651 debug_printf("on_printer_deleted() in THREAD %ld\n", pthread_self());
6652
6653 debug_printf("[CUPS Notification] Printer deleted: %s\n",
6654 text);
6655
6656 if (terminating) {
6657 debug_printf("[CUPS Notification]: Ignoring because cups-browsed is terminating.\n");
6658 return;
6659 }
6660
6661 if (is_created_by_cups_browsed(printer)) {
6662 /* Get available CUPS queues to check whether the queue did not
6663 already get re-created */
6664 update_local_printers ();
6665 /* Look up print queue in the list */
6666 local_queue_name_lower = g_ascii_strdown(printer, -1);
6667 local_printer = g_hash_table_lookup (local_printers,
6668 local_queue_name_lower);
6669 free(local_queue_name_lower);
6670 /* If the queue is there again, do not re-create it */
6671 if (local_printer) {
6672 debug_printf("Printer %s already re-created.\n",
6673 printer);
6674 return;
6675 }
6676
6677 /* a cups-browsed-generated printer got deleted, re-create it */
6678 debug_printf("Printer %s got deleted, re-creating it.\n",
6679 printer);
6680 /* If the deleted printer was the default printer, make sure it gets the
6681 default printer again */
6682 if (default_printer && !strcasecmp(printer, default_printer)) {
6683 if (record_default_printer(printer, 0) < 0) {
6684 /* Delete record file if recording failed */
6685 debug_printf("ERROR: Failed recording remote default printer. Removing the file with possible old recording.\n");
6686 invalidate_default_printer(0);
6687 } else
6688 debug_printf("Recorded %s as remote default printer so that it gets set as default after re-creating.\n",
6689 printer);
6690 /* Make sure that a recorded local default printer does not get lost
6691 during the recovery operation */
6692 if ((r = retrieve_default_printer(1)) != NULL) {
6693 if (default_printer != NULL)
6694 free((void *)default_printer);
6695 default_printer = r;
6696 }
6697 }
6698 /* Schedule for immediate creation of the CUPS queue */
6699 p = printer_record(printer);
6700 if (p && p->status != STATUS_DISAPPEARED &&
6701 p->status != STATUS_UNCONFIRMED &&
6702 p->status != STATUS_TO_BE_RELEASED) {
6703 p->status = STATUS_TO_BE_CREATED;
6704 p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
6705 if (in_shutdown == 0)
6706 recheck_timer();
6707 }
6708 }
6709 }
6710
6711 static int
queue_overwritten(remote_printer_t * p)6712 queue_overwritten (remote_printer_t *p)
6713 {
6714 http_t *conn = NULL;
6715 ipp_t *response = NULL; /* IPP Response */
6716 ipp_attribute_t *attr; /* Current attribute */
6717 const char *printername, /* Print queue name */
6718 *uri, /* Printer URI */
6719 *device, /* Printer device URI */
6720 *makemodel; /* Printer make and model
6721 (equals PPD NickName) */
6722 char local_queue_uri[1024];
6723 static const char *pattrs[] = /* Attributes we need for printers... */
6724 {
6725 "printer-name",
6726 "printer-uri-supported",
6727 "device-uri",
6728 "printer-make-and-model"
6729 };
6730 int overwritten = 0;
6731
6732 if (p->overwritten)
6733 /* We already have discovered that this queue got overwritten
6734 so we do not repeat the tests and exit positively */
6735 return 1;
6736
6737 if (p->uri[0] == '\0')
6738 /* Also skip unconfirmed printer entries from queues of the
6739 previous session, they do not have a PPD file registered, so we
6740 cannot compare */
6741 return 0;
6742
6743 /* Get the device URI which our CUPS queue actually has now, a
6744 change of the URI means a modification or replacement of the
6745 print queue by something user-defined. So we schedule this queue
6746 for release from handling by cups-browsed.
6747
6748 In a second step get the NickName of the PPD which our CUPS queue
6749 actually uses now, a change of the NickName means a replacement
6750 of the PPD of the print queue by a user-selected one. So we
6751 schedule this queue for release from handling by cups-browsed
6752 also in this case.
6753
6754 We only need the NickName from the PPD and due to the fact that
6755 the cupsGetPPD2() function does not work when CUPS is on a
6756 non-standard port (!= 631, Bug!) and the NickName is also in the
6757 get-printer-attributes IPP response as "printer-make-and-model",
6758 we go the IPP way here and do not download the printer's PPD. */
6759
6760 conn = http_connect_local ();
6761 if (conn == NULL) {
6762 debug_printf("Cannot connect to local CUPS to see whether queue %s got overwritten.\n",
6763 p->queue_name);
6764 return 0;
6765 }
6766
6767 /* URI of the local CUPS queue (not the device URI */
6768 httpAssembleURIf(HTTP_URI_CODING_ALL, local_queue_uri,
6769 sizeof(local_queue_uri),
6770 "ipp", NULL, "localhost", 0,
6771 "/printers/%s", p->queue_name);
6772 response = get_printer_attributes2(conn, local_queue_uri,
6773 pattrs, sizeof(pattrs) / sizeof(pattrs[0]),
6774 pattrs, sizeof(pattrs) / sizeof(pattrs[0]),
6775 1);
6776 debug_log_out(get_printer_attributes_log);
6777 if (!response || cupsLastError() > IPP_STATUS_OK_CONFLICTING) {
6778 debug_printf("lpstat: %s\n", cupsLastErrorString());
6779 } else {
6780 printername = NULL;
6781 device = NULL;
6782 uri = NULL;
6783 makemodel = NULL;
6784 for (attr = ippFirstAttribute(response); attr != NULL;
6785 attr = ippNextAttribute(response)) {
6786 if (!strcmp(ippGetName(attr), "printer-name") &&
6787 ippGetValueTag(attr) == IPP_TAG_NAME)
6788 printername = ippGetString(attr, 0, NULL);
6789 if (!strcmp(ippGetName(attr), "printer-uri-supported") &&
6790 ippGetValueTag(attr) == IPP_TAG_URI)
6791 uri = ippGetString(attr, 0, NULL);
6792 if (!strcmp(ippGetName(attr), "device-uri") &&
6793 ippGetValueTag(attr) == IPP_TAG_URI)
6794 device = ippGetString(attr, 0, NULL);
6795 if (!strcmp(ippGetName(attr), "printer-make-and-model") &&
6796 ippGetValueTag(attr) == IPP_TAG_TEXT)
6797 makemodel = ippGetString(attr, 0, NULL);
6798 }
6799 if (printername != NULL &&
6800 strcasecmp(p->queue_name, printername) == 0) {
6801 if (device == NULL)
6802 device = uri;
6803 /* Check device URI */
6804 if (device != NULL &&
6805 (p->uri == NULL ||
6806 (strlen(device) < 16 ||
6807 strncmp(device, "implicitclass://", 16)))) {
6808 /* The printer's device URI is different to what we have
6809 assigned, so we got notified because the queue was
6810 externally modified and so we will release this printer
6811 from the control of cups-browsed */
6812 debug_printf("Printer %s got modified externally, discovered by a change of its device URI from %s to %s.\n",
6813 p->queue_name,
6814 (p->uri ? (p->netprinter ? p->uri :
6815 "implicitclass://...") :
6816 "(not yet determined)"),
6817 device);
6818 overwritten = 1;
6819 }
6820 /* Check NickName */
6821 if (p->nickname == NULL || makemodel == NULL ||
6822 strcasecmp(p->nickname, makemodel)) {
6823 /* The PPD file of the queue got replaced which we
6824 discovered by comparing the NickName of the PPD with the
6825 NickName which the PPD we have used has. So we were
6826 notified because the queue was externally modified and so
6827 we will release this printer from the control of
6828 cups-browsed */
6829 debug_printf("Printer %s got modified externally, discovered by the NickName of its PPD file having changed from \"%s\" to \"%s\".\n",
6830 p->queue_name, (p->nickname ? p->nickname : "(no PPD)"),
6831 (makemodel ? makemodel :
6832 "(NickName not readable)"));
6833 overwritten = 1;
6834 }
6835 }
6836 }
6837 if (response) ippDelete(response);
6838
6839 return overwritten;
6840 }
6841
6842 static void
on_printer_modified(CupsNotifier * object,const gchar * text,const gchar * printer_uri,const gchar * printer,guint printer_state,const gchar * printer_state_reasons,gboolean printer_is_accepting_jobs,gpointer user_data)6843 on_printer_modified (CupsNotifier *object,
6844 const gchar *text,
6845 const gchar *printer_uri,
6846 const gchar *printer,
6847 guint printer_state,
6848 const gchar *printer_state_reasons,
6849 gboolean printer_is_accepting_jobs,
6850 gpointer user_data)
6851 {
6852 remote_printer_t *p;
6853 http_t *conn = NULL;
6854 ipp_t *request; /* IPP Request */
6855 int re_create, is_cups_queue;
6856 char *new_queue_name;
6857 cups_array_t *to_be_renamed;
6858 char local_queue_uri[1024];
6859 char *resolved_uri = NULL;
6860
6861 debug_printf("on_printer_modified() in THREAD %ld\n", pthread_self());
6862
6863 debug_printf("[CUPS Notification] Printer modified: %s\n",
6864 text);
6865
6866 if (is_created_by_cups_browsed(printer)) {
6867 p = printer_record(printer);
6868 if (p->overwritten)
6869 /* We already have discovered that this queue got overwritten
6870 and are treating the process appropriately, so return now to
6871 avoid an infinite recursion */
6872 return;
6873
6874 if (queue_overwritten(p)) {
6875 /* Our generated local queue pointing to a remote printer got
6876 overwritten by an externally created queue with the same
6877 name.
6878 We will release control from this queue now and try to
6879 re-create our queue under a different name, usually
6880 <old_name>@<remote_host>.
6881 If we have slaves, we have to do this for them, too. */
6882
6883 p->overwritten = 1;
6884
6885 /* First, remove the "cups-browsed=true" from the queue's
6886 options, so that cups-browsed considers this queue as created
6887 manually */
6888 debug_printf("Removing \"cups-browsed=true\" from CUPS queue %s (%s).\n",
6889 p->queue_name, p->uri);
6890 conn = http_connect_local ();
6891 if (conn == NULL)
6892 debug_printf("Browse send failed to connect to localhost\n");
6893 else {
6894 request = ippNewRequest(IPP_OP_CUPS_ADD_MODIFY_PRINTER);
6895 /* Printer URI: ipp://localhost/printers/<queue name> */
6896 httpAssembleURIf(HTTP_URI_CODING_ALL, local_queue_uri,
6897 sizeof(local_queue_uri),
6898 "ipp", NULL, "localhost", 0,
6899 "/printers/%s", p->queue_name);
6900 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
6901 "printer-uri", NULL, local_queue_uri);
6902 /* Default user */
6903 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
6904 "requesting-user-name", NULL, cupsUser());
6905 /* Option to be removed */
6906 ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_DELETEATTR,
6907 CUPS_BROWSED_MARK "-default", 0);
6908 /* Do it */
6909 ippDelete(cupsDoRequest(conn, request, "/admin/"));
6910 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
6911 debug_printf("Unable to remove \"cups-browsed=true\" from CUPS queue!\n");
6912 }
6913
6914 /* Now try to rename all our printer entries with this
6915 queue name. Drop entries where renaming fails */
6916 to_be_renamed = cupsArrayNew(NULL, NULL);
6917 /* Put the printer entries which need attention into
6918 a separate array, as we cannot run two nested loops
6919 on one CUPS array, as our printer entry array */
6920 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
6921 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
6922 if (strcasecmp(p->queue_name, printer) == 0) {
6923 p->overwritten = 1;
6924 cupsArrayAdd(to_be_renamed, p);
6925 }
6926 for (p = (remote_printer_t *)cupsArrayFirst(to_be_renamed);
6927 p; p = (remote_printer_t *)cupsArrayNext(to_be_renamed)) {
6928 is_cups_queue = (p->netprinter == 0 ? 1 : 0);
6929 re_create = 1;
6930 /* Is there a local queue with the same URI as the remote queue? */
6931 if (g_hash_table_find (local_printers,
6932 local_printer_is_same_device, p)) {
6933 /* Found a local queue with the same URI as our discovered printer
6934 would get, so ignore this remote printer */
6935 debug_printf("Printer with URI %s (or IPP/IPPS equivalent) already exists, no replacement queue to be created.\n",
6936 p->uri);
6937 re_create = 0;
6938 } else if ((new_queue_name = /* Try to find a new queue name */
6939 get_local_queue_name(p->service_name, p->make_model,
6940 p->resource, p->host,
6941 &is_cups_queue,
6942 p->queue_name)) == NULL) {
6943 /* Not able to find a new name for the queue */
6944 debug_printf("No new name for printer found, no replacement queue to be created.\n");
6945 re_create = 0;
6946 } else {
6947 free(p->queue_name);
6948 p->queue_name = new_queue_name;
6949 /* Check whether the queue under its new name will be stand-alone or
6950 part of a cluster */
6951 if (join_cluster_if_needed(p, is_cups_queue) < 0) {
6952 /* There are other cups-browsed-generated queues with the new
6953 name, not able to cluster this queue with them */
6954 debug_printf("Not able to cluster this queue with equally-named ones.\n");
6955 re_create = 0;
6956 }
6957 }
6958 if (resolved_uri) free(resolved_uri);
6959 if (re_create) {
6960 p->overwritten = 0;
6961 p->status = STATUS_TO_BE_CREATED;
6962 p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
6963 debug_printf("Released CUPS queue %s from the control of cups-browsed. Printer with URI %s renamed to %s.\n",
6964 printer, p->uri, p->queue_name);
6965 } else {
6966 /* To remove this entry independent of any other entry we
6967 set the slave_of to NULL. This does not lead to an
6968 attempt to remove a CUPS queue as we have the status
6969 STATUS_TO_BE_RELEASED */
6970 p->slave_of = NULL;
6971 p->status = STATUS_TO_BE_RELEASED;
6972 p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
6973 debug_printf("Released CUPS queue %s from the control of cups-browsed. No local queue any more for printer with URI %s.\n",
6974 printer, p->uri);
6975 }
6976 }
6977 cupsArrayDelete(to_be_renamed);
6978 if (in_shutdown == 0)
6979 recheck_timer();
6980 } else {
6981 if (terminating) {
6982 debug_printf("[CUPS Notification]: Not saving external option changes because cups-browsed is terminating.\n");
6983 return;
6984 }
6985 /* The user has changed settings of a printer which we have generated,
6986 backup the changes for the case of a crash or unclean shutdown of
6987 cups-browsed. */
6988 if (!p->no_autosave) {
6989 debug_printf("Settings of printer %s got modified, doing backup.\n",
6990 p->queue_name);
6991 p->no_autosave = 1; /* Avoid infinite recursion */
6992 record_printer_options(p->queue_name);
6993 p->no_autosave = 0;
6994 }
6995 }
6996 }
6997 }
6998
6999
7000
7001 /* This compare function makes the "lo" (looback) interface always
7002 sorted to the beginning of the array, this way one only needs to
7003 check the first element of the error to find out whether a remote
7004 printer is already available through the loopback interface (preferred
7005 interface) or not.
7006 All other interfaces are sorted alphabetically, the types let IPPS
7007 appear before IPP, and the families numerically (makes IPv4 appear
7008 before IPv6). */
7009
7010 int
ipp_discovery_cmp(void * va,void * vb,void * data)7011 ipp_discovery_cmp(void *va, void *vb, void *data) {
7012 ipp_discovery_t *a = (ipp_discovery_t *)va;
7013 ipp_discovery_t *b = (ipp_discovery_t *)vb;
7014 int cmp;
7015
7016 if (!a && !b)
7017 return 0;
7018 if (a && !b)
7019 return -1;
7020 if (!a && b)
7021 return 1;
7022
7023 if (!strcasecmp(a->interface, "lo") && strcasecmp(b->interface, "lo"))
7024 return -1;
7025 if (strcasecmp(a->interface, "lo") && !strcasecmp(b->interface, "lo"))
7026 return 1;
7027
7028 cmp = strcasecmp(a->interface, b->interface);
7029 if (cmp)
7030 return cmp;
7031
7032 if (strcasestr(a->type, "ipps") && !strcasestr(b->type, "ipps"))
7033 return -1;
7034 if (!strcasestr(a->type, "ipps") && strcasestr(b->type, "ipps"))
7035 return 1;
7036
7037 cmp = strcasecmp(a->type, b->type);
7038 if (cmp)
7039 return cmp;
7040
7041 if (a->family < b->family)
7042 return -1;
7043 else if (a->family > b->family)
7044 return 1;
7045 else
7046 return 0;
7047 }
7048
7049 void
ipp_discovery_free(void * ve,void * data)7050 ipp_discovery_free(void *ve, void *data) {
7051 ipp_discovery_t *e = (ipp_discovery_t *)ve;
7052
7053 if (e) {
7054 if (e->interface)
7055 free(e->interface);
7056 if (e->type)
7057 free(e->type);
7058 free(e);
7059 }
7060 }
7061
7062 void
ipp_discoveries_list(cups_array_t * a)7063 ipp_discoveries_list(cups_array_t *a) {
7064 ipp_discovery_t *e;
7065
7066 debug_printf("Printer discovered %d times:\n", cupsArrayCount(a));
7067 for (e = cupsArrayFirst(a); e; e = cupsArrayNext(a))
7068 debug_printf(" %s, %s, %s\n", e->interface, e->type,
7069 (e->family == AF_INET ? "IPv4" :
7070 (e->family == AF_INET6 ? "IPv6" : "???")));
7071 }
7072
7073 int
ipp_discoveries_add(cups_array_t * a,const char * interface,const char * type,int family)7074 ipp_discoveries_add(cups_array_t *a,
7075 const char *interface,
7076 const char *type,
7077 int family) {
7078 ipp_discovery_t *e;
7079
7080 if (!interface || !type)
7081 return 0;
7082 if ((e = (ipp_discovery_t *)calloc(1, sizeof(ipp_discovery_t))) ==
7083 NULL) {
7084 debug_printf("ERROR: Unable to allocate memory.\n");
7085 return 0;
7086 }
7087 e->interface = strdup(interface);
7088 e->type = strdup(type);
7089 e->family = family;
7090 cupsArrayAdd(a, e);
7091 ipp_discoveries_list(a);
7092 return 1;
7093 }
7094
7095 static remote_printer_t *
create_remote_printer_entry(const char * queue_name,const char * location,const char * info,const char * uri,const char * host,const char * ip,int port,const char * resource,const char * service_name,const char * type,const char * domain,const char * interface,int family,const char * pdl,int color,int duplex,const char * make_model,int is_cups_queue)7096 create_remote_printer_entry (const char *queue_name,
7097 const char *location,
7098 const char *info,
7099 const char *uri,
7100 const char *host,
7101 const char *ip,
7102 int port,
7103 const char *resource,
7104 const char *service_name,
7105 const char *type,
7106 const char *domain,
7107 const char *interface,
7108 int family,
7109 const char *pdl,
7110 int color,
7111 int duplex,
7112 const char *make_model,
7113 int is_cups_queue)
7114 {
7115 remote_printer_t *p;
7116 remote_printer_t *q;
7117 http_t *http_printer = NULL;
7118 #ifdef HAVE_CUPS_1_6
7119 int i;
7120 ipp_attribute_t *attr;
7121 char valuebuffer[65536];
7122 int is_pwgraster = 0;
7123 int is_appleraster = 0;
7124 int is_pclm = 0;
7125 int is_pdf = 0;
7126 #endif /* HAVE_CUPS_1_6 */
7127
7128 if (!queue_name || !location || !info || !uri || !host || !resource ||
7129 !service_name || !type || !domain) {
7130 debug_printf("ERROR: create_remote_printer_entry(): Input value missing!\n");
7131 return NULL;
7132 }
7133
7134 /* Mark this as a queue to be created locally pointing to the printer */
7135 if ((p = (remote_printer_t *)calloc(1, sizeof(remote_printer_t))) == NULL) {
7136 debug_printf("ERROR: Unable to allocate memory.\n");
7137 return NULL;
7138 }
7139
7140 /* Assure that, if we have forgotten to set a field in the printer
7141 record, that it is set to zero */
7142 memset(p, 0, sizeof(remote_printer_t));
7143
7144 /* Queue name */
7145 p->queue_name = strdup(queue_name);
7146 if (!p->queue_name)
7147 goto fail;
7148
7149 p->location = strdup(location);
7150 if (!p->location)
7151 goto fail;
7152
7153 p->info = strdup(info);
7154 if (!p->info)
7155 goto fail;
7156
7157 if (make_model)
7158 p->make_model = strdup(make_model);
7159 else
7160 p->make_model = NULL;
7161
7162 if (pdl)
7163 p->pdl = strdup(pdl);
7164 else
7165 p->pdl = NULL;
7166
7167 p->color = color;
7168
7169 p->duplex = duplex;
7170
7171 p->uri = strdup(uri);
7172 if (!p->uri)
7173 goto fail;
7174
7175 p->slave_of = NULL;
7176 p->last_printer = -1;
7177
7178 p->num_options = 0;
7179 p->options = NULL;
7180
7181 p->host = strdup (host);
7182 if (!p->host)
7183 goto fail;
7184
7185 p->ip = (ip != NULL ? strdup (ip) : NULL);
7186
7187 p->port = (port != 0 ? port : 631);
7188
7189 p->resource = strdup (resource);
7190 if (!p->resource)
7191 goto fail;
7192
7193 p->service_name = strdup (service_name);
7194 if (!p->service_name)
7195 goto fail;
7196
7197 /* Record DNS-SD service parameters to identify print queue
7198 entry for removal when service disappears */
7199 p->type = strdup (type);
7200 if (!p->type)
7201 goto fail;
7202
7203 p->domain = strdup (domain);
7204 if (!p->domain)
7205 goto fail;
7206
7207 p->ipp_discoveries =
7208 cupsArrayNew3(ipp_discovery_cmp, NULL, NULL, 0, NULL, ipp_discovery_free);
7209 if (p->ipp_discoveries == NULL) {
7210 debug_printf("ERROR: Unable to allocate memory.\n");
7211 return NULL;
7212 }
7213 if (domain != NULL && domain[0] != '\0' &&
7214 type != NULL && type[0] != '\0')
7215 ipp_discoveries_add(p->ipp_discoveries, interface, type, family);
7216
7217 /* Schedule for immediate creation of the CUPS queue */
7218 p->status = STATUS_TO_BE_CREATED;
7219 p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
7220
7221 /* Flag which can be set to inhibit automatic saving of option settings
7222 by the on_printer_modified() notification handler function */
7223 p->no_autosave = 0;
7224
7225 /* Flag to be set when a local queue generated by us was overwritten
7226 by an external process. It serves to avoid that the process to
7227 treat this case is not repeated in an infinite recursion */
7228 p->overwritten = 0;
7229
7230 /* Flag to mark whether this printer was discovered through a legacy
7231 CUPS broadcast (1) or through DNS-SD (0) */
7232 p->is_legacy = 0;
7233
7234 /* Initialize field for how many timeouts cups-browsed experienced
7235 in a row during creation of this printer's queue */
7236 p->timeouted = 0;
7237
7238 /* Initialize nickname array for *Nickname directive from PPD
7239 * - either from CUPS server or from our PPD generator */
7240 p->nickname = NULL;
7241
7242 /* Remote CUPS printer or local queue remaining from previous cups-browsed
7243 session */
7244 /* is_cups_queue: -1: Unknown, 0: IPP printer, 1: Remote CUPS queue,
7245 2: Remote CUPS queue in user-defined cluster */
7246 if (is_cups_queue != 0) {
7247 if (is_cups_queue > 0 && CreateRemoteCUPSPrinterQueues == 0) {
7248 debug_printf("Printer %s (%s) is a remote CUPS printer and cups-browsed is not configured to set up such printers automatically, ignoring this printer.\n",
7249 p->queue_name, p->uri);
7250 goto fail;
7251 }
7252 /* For a remote CUPS printer our local queue will be raw or get a
7253 PPD file from the remote CUPS server, so that the driver on the
7254 remote CUPS server gets used. So we will not generate a PPD file
7255 or interface script at this point. */
7256 p->netprinter = 0;
7257 if (p->uri[0] != '\0') {
7258 p->prattrs = get_printer_attributes(p->uri, NULL, 0, NULL, 0, 1);
7259 debug_log_out(get_printer_attributes_log);
7260 if (p->prattrs == NULL)
7261 debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n",
7262 p->queue_name, p->uri);
7263 }
7264 } else {
7265 #ifndef HAVE_CUPS_1_6
7266 /* The following code uses a lot of CUPS >= 1.6 specific stuff.
7267 For older CUPS <= 1.5.4 the following functionality is skipped
7268 which means for CUPS <= 1.5.4 only for CUPS printer broadcasts
7269 there are local queues created which should be sufficient
7270 on systems where traditional CUPS <= 1.5.4 is used. */
7271 goto fail;
7272 #else /* HAVE_CUPS_1_6 */
7273 /* Non-CUPS printer broadcasts are most probably from printers
7274 directly connected to the network and using the IPP protocol.
7275 We check whether we can set them up without a device-specific
7276 driver, only using page description languages which the
7277 operating system provides: PCL 5c/5e/6/XL, PostScript, PDF, PWG
7278 Raster, Apple Raster, PCLm. Especially printers designed for
7279 driverless printing (DNS-SD + IPP 2.x + at least one of PWG
7280 Raster, Apple Raster, PCLm, PDF) will work this way. Making
7281 only driverless queues we can get an easy, configuration-less
7282 way to print from mobile devices, even if there is no CUPS
7283 server with shared printers around. */
7284
7285 if (CreateIPPPrinterQueues == IPP_PRINTERS_NO) {
7286 debug_printf("Printer %s (%s) is an IPP network printer and cups-browsed is not configured to set up such printers automatically, ignoring this printer.\n",
7287 p->queue_name, p->uri);
7288 goto fail;
7289 }
7290
7291 if (!pdl || pdl[0] == '\0' ||
7292 (!strcasestr(pdl, "application/postscript") &&
7293 !strcasestr(pdl, "application/pdf") &&
7294 !strcasestr(pdl, "image/pwg-raster") &&
7295 #ifdef CUPS_RASTER_HAVE_APPLERASTER
7296 !strcasestr(pdl, "image/urf") &&
7297 #endif
7298 #ifdef QPDF_HAVE_PCLM
7299 !strcasestr(pdl, "application/PCLm") &&
7300 #endif
7301 ((!strcasestr(pdl, "application/vnd.hp-PCL") &&
7302 !strcasestr(pdl, "application/PCL") &&
7303 !strcasestr(pdl, "application/x-pcl")) ||
7304 ((!strncasecmp(make_model, "HP", 2) || /* HP inkjets not supported */
7305 !strncasecmp(make_model, "Hewlett Packard", 15) ||
7306 !strncasecmp(make_model, "Hewlett-Packard", 15)) &&
7307 !strcasestr(make_model, "LaserJet") &&
7308 !strcasestr(make_model, "Mopier"))) &&
7309 !strcasestr(pdl, "application/vnd.hp-PCLXL"))) {
7310 debug_printf("Cannot create remote printer %s (URI: %s, Model: %s, Accepted data formats: %s) as its PDLs are not known, ignoring this printer.\n",
7311 p->queue_name, p->uri, make_model, pdl);
7312 debug_printf("Supported PDLs: PWG Raster, %s%sPostScript, PDF, PCL XL, PCL 5c/e (HP inkjets report themselves as PCL printers but their PCL is not supported)\n",
7313 #ifdef CUPS_RASTER_HAVE_APPLERASTER
7314 "Apple Raster, ",
7315 #else
7316 "",
7317 #endif
7318 #ifdef QPDF_HAVE_PCLM
7319 "PCLm, "
7320 #else
7321 ""
7322 #endif
7323 );
7324 goto fail;
7325 }
7326
7327 /* Check whether we have an equally named queue already */
7328 for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
7329 q;
7330 q = (remote_printer_t *)cupsArrayNext(remote_printers))
7331 if (!strcasecmp(q->queue_name, p->queue_name)) {/* Queue with same name */
7332 debug_printf("We have already created a queue with the name %s for another printer. Skipping this printer.\n", p->queue_name);
7333 debug_printf("Try setting \"LocalQueueNamingIPPPrinter DNS-SD\" in cups-browsed.conf.\n");
7334 goto fail;
7335 }
7336
7337 p->slave_of = NULL;
7338 p->netprinter = 1;
7339 p->prattrs = get_printer_attributes(p->uri, NULL, 0, NULL, 0, 1);
7340 debug_log_out(get_printer_attributes_log);
7341 if (p->prattrs == NULL) {
7342 debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n",
7343 p->queue_name, p->uri);
7344 goto fail;
7345 }
7346
7347 /* If we have opted for only printers designed for driverless use (PWG
7348 Raster + Apple Raster + PCLm + PDF) being set up automatically, we check
7349 first, whether our printer supports IPP 2.0 or newer. If not, we
7350 skip this printer */
7351 if (CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7352 valuebuffer[0] = '\0';
7353 debug_printf("Checking whether printer %s supports IPP 2.x or newer:\n",
7354 p->queue_name);
7355 if ((attr = ippFindAttribute(p->prattrs,
7356 "ipp-versions-supported",
7357 IPP_TAG_KEYWORD)) != NULL) {
7358 debug_printf(" Attr: %s\n", ippGetName(attr));
7359 for (i = 0; i < ippGetCount(attr); i ++) {
7360 strncpy(valuebuffer, ippGetString(attr, i, NULL),
7361 sizeof(valuebuffer) - 1);
7362 if (strlen(ippGetString(attr, i, NULL)) > 65535)
7363 valuebuffer[65535] = '\0';
7364 debug_printf(" Keyword: %s\n", valuebuffer);
7365 if (valuebuffer[0] > '1')
7366 break;
7367 }
7368 }
7369 if (!attr || valuebuffer[0] == '\0' || valuebuffer[0] <= '1') {
7370 debug_printf(" --> cups-browsed is configured to auto-setup only printers which are designed for driverless printing. These printers require IPP 2.x or newer, but this printer only supports IPP 1.x or older. Skipping.\n");
7371 goto fail;
7372 } else
7373 debug_printf(" --> Printer supports IPP 2.x or newer.\n");
7374 }
7375
7376 /* If we have opted for only PWG Raster printers or for only printers
7377 designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF)
7378 being set up automatically, we check whether the printer has a non-empty
7379 string in its "pwg-raster-document-resolution-supported" IPP attribute
7380 to see whether we have a PWG Raster printer. */
7381 if (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER ||
7382 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7383 valuebuffer[0] = '\0';
7384 debug_printf("Checking whether printer %s understands PWG Raster:\n",
7385 p->queue_name);
7386 if ((attr = ippFindAttribute(p->prattrs,
7387 "pwg-raster-document-resolution-supported",
7388 IPP_TAG_RESOLUTION)) != NULL) {
7389 debug_printf(" Attr: %s\n", ippGetName(attr));
7390 ippAttributeString(attr, valuebuffer, sizeof(valuebuffer));
7391 debug_printf(" Value: %s\n", valuebuffer);
7392 if (valuebuffer[0] == '\0') {
7393 for (i = 0; i < ippGetCount(attr); i ++) {
7394 strncpy(valuebuffer, ippGetString(attr, i, NULL),
7395 sizeof(valuebuffer) - 1);
7396 if (strlen(ippGetString(attr, i, NULL)) > 65535)
7397 valuebuffer[65535] = '\0';
7398 debug_printf(" Keyword: %s\n", valuebuffer);
7399 if (valuebuffer[0] != '\0')
7400 break;
7401 }
7402 }
7403 }
7404 if (attr && valuebuffer[0] != '\0')
7405 is_pwgraster = 1;
7406 debug_printf(" --> Printer %s PWG Raster.\n",
7407 is_pwgraster ? "supports" : "does not support");
7408 }
7409
7410 #ifdef CUPS_RASTER_HAVE_APPLERASTER
7411 /* If we have opted for only Apple Raster printers or for only printers
7412 designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF)
7413 being set up automatically, we check whether the printer has a non-empty
7414 string in its "urf-supported" IPP attribute to see whether we have an
7415 Apple Raster printer. */
7416 if (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER ||
7417 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7418 valuebuffer[0] = '\0';
7419 debug_printf("Checking whether printer %s understands Apple Raster:\n",
7420 p->queue_name);
7421 if ((attr = ippFindAttribute(p->prattrs, "urf-supported", IPP_TAG_KEYWORD)) != NULL) {
7422 debug_printf(" Attr: %s\n", ippGetName(attr));
7423 ippAttributeString(attr, valuebuffer, sizeof(valuebuffer));
7424 debug_printf(" Value: %s\n", valuebuffer);
7425 if (valuebuffer[0] == '\0') {
7426 for (i = 0; i < ippGetCount(attr); i ++) {
7427 strncpy(valuebuffer, ippGetString(attr, i, NULL),
7428 sizeof(valuebuffer) - 1);
7429 if (strlen(ippGetString(attr, i, NULL)) > 65535)
7430 valuebuffer[65535] = '\0';
7431 debug_printf(" Keyword: %s\n", valuebuffer);
7432 if (valuebuffer[0] != '\0')
7433 break;
7434 }
7435 }
7436 }
7437 if (attr && valuebuffer[0] != '\0')
7438 is_appleraster = 1;
7439 debug_printf(" --> Printer %s Apple Raster.\n",
7440 is_appleraster ? "supports" : "does not support");
7441 }
7442 #endif
7443
7444 #ifdef QPDF_HAVE_PCLM
7445 /* If we have opted for only PCLm printers or for only printers
7446 designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF)
7447 being set up automatically, we check whether the printer has a non-empty
7448 string in its "pclm-compression-method-preferred" IPP attribute to see
7449 whether we have a PCLm printer. */
7450 if (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM ||
7451 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7452 valuebuffer[0] = '\0';
7453 debug_printf("Checking whether printer %s understands PCLm:\n",
7454 p->queue_name);
7455 if ((attr = ippFindAttribute(p->prattrs,
7456 "pclm-compression-method-preferred",
7457 IPP_TAG_KEYWORD)) != NULL) {
7458 debug_printf(" Attr: %s\n", ippGetName(attr));
7459 ippAttributeString(attr, valuebuffer, sizeof(valuebuffer));
7460 debug_printf(" Value: %s\n", p->queue_name, valuebuffer);
7461 if (valuebuffer[0] == '\0') {
7462 for (i = 0; i < ippGetCount(attr); i ++) {
7463 strncpy(valuebuffer, ippGetString(attr, i, NULL),
7464 sizeof(valuebuffer) - 1);
7465 if (strlen(ippGetString(attr, i, NULL)) > 65535)
7466 valuebuffer[65535] = '\0';
7467 debug_printf(" Keyword: %s\n", valuebuffer);
7468 if (valuebuffer[0] != '\0')
7469 break;
7470 }
7471 }
7472 }
7473 if (attr && valuebuffer[0] != '\0')
7474 is_pclm = 1;
7475 debug_printf(" --> Printer %s PCLm.\n",
7476 is_pclm ? "supports" : "does not support");
7477 }
7478 #endif
7479
7480 /* If we have opted for only PDF printers or for only printers
7481 designed for driverless use (PWG Raster + Apple Raster + PCLm + PDF)
7482 being set up automatically, we check whether the printer has
7483 "application/pdf" under its PDLs. */
7484 if (CreateIPPPrinterQueues == IPP_PRINTERS_PDF ||
7485 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS) {
7486 debug_printf("Checking whether printer %s understands PDF: PDLs: %s\n",
7487 p->queue_name, pdl);
7488 if(strcasestr(pdl, "application/pdf"))
7489 is_pdf = 1;
7490 debug_printf(" --> Printer %s PDF.\n",
7491 is_pdf ? "supports" : "does not support");
7492 }
7493
7494 /* If the printer is not the driverless printer we opted for, we skip
7495 this printer. */
7496 if ((CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS &&
7497 is_pwgraster == 0 && is_appleraster == 0 && is_pclm == 0 &&
7498 is_pdf == 0) ||
7499 (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER &&
7500 is_pwgraster == 0) ||
7501 (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER &&
7502 is_appleraster == 0) ||
7503 (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM &&
7504 is_pclm == 0) ||
7505 (CreateIPPPrinterQueues == IPP_PRINTERS_PDF &&
7506 is_pdf == 0)) {
7507 debug_printf("Printer %s (%s%s%s%s%s%s%s%s%s%s%s%s%s) does not support the driverless printing protocol cups-browsed is configured to accept for setting up such printers automatically, ignoring this printer.\n",
7508 p->queue_name, p->uri,
7509 (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER ||
7510 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7511 ", " : ""),
7512 (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER ||
7513 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7514 (is_pwgraster ? "" : "not ") : ""),
7515 (CreateIPPPrinterQueues == IPP_PRINTERS_PWGRASTER ||
7516 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7517 "PWG Raster" : ""),
7518 (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER ||
7519 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7520 ", " : ""),
7521 (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER ||
7522 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7523 (is_appleraster ? "" : "not ") : ""),
7524 (CreateIPPPrinterQueues == IPP_PRINTERS_APPLERASTER ||
7525 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7526 "Apple Raster" : ""),
7527 (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM ||
7528 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7529 ", " : ""),
7530 (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM ||
7531 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7532 (is_pclm ? "" : "not ") : ""),
7533 (CreateIPPPrinterQueues == IPP_PRINTERS_PCLM ||
7534 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7535 "PCLm" : ""),
7536 (CreateIPPPrinterQueues == IPP_PRINTERS_PDF ||
7537 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7538 ", " : ""),
7539 (CreateIPPPrinterQueues == IPP_PRINTERS_PDF ||
7540 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7541 (is_pdf ? "" : "not ") : ""),
7542 (CreateIPPPrinterQueues == IPP_PRINTERS_PDF ||
7543 CreateIPPPrinterQueues == IPP_PRINTERS_DRIVERLESS ?
7544 "PDF" : ""));
7545 goto fail;
7546 }
7547
7548 #endif /* HAVE_CUPS_1_6 */
7549 }
7550 /* Check whether we have an equally named queue already from another
7551 server and join a cluster if needed */
7552 if (join_cluster_if_needed(p, is_cups_queue) < 0)
7553 goto fail;
7554 /* Add the new remote printer entry */
7555 log_all_printers();
7556 cupsArrayAdd(remote_printers, p);
7557 log_all_printers();
7558
7559 /* If auto shutdown is active we have perhaps scheduled a timer to shut down
7560 due to not having queues any more to maintain, kill the timer now */
7561 if (autoshutdown && autoshutdown_exec_id &&
7562 autoshutdown_on == NO_QUEUES &&
7563 cupsArrayCount(remote_printers) > 0) {
7564 debug_printf ("New printers there to make available, killing auto shutdown timer.\n");
7565 g_source_remove(autoshutdown_exec_id);
7566 autoshutdown_exec_id = 0;
7567 }
7568
7569 if (http_printer)
7570 httpClose(http_printer);
7571 return p;
7572
7573 fail:
7574 debug_printf("ERROR: Unable to create print queue, ignoring printer.\n");
7575 if (p->prattrs) ippDelete(p->prattrs);
7576 if (http_printer)
7577 httpClose(http_printer);
7578 if (p->type) free (p->type);
7579 if (p->service_name) free (p->service_name);
7580 if (p->host) free (p->host);
7581 if (p->resource) free (p->resource);
7582 if (p->domain) free (p->domain);
7583 cupsArrayDelete(p->ipp_discoveries);
7584 if (p->ip) free (p->ip);
7585 cupsFreeOptions(p->num_options, p->options);
7586 if (p->uri) free (p->uri);
7587 if (p->pdl) free (p->pdl);
7588 if (p->make_model) free (p->make_model);
7589 if (p->location) free (p->location);
7590 if (p->info) free (p->info);
7591 if (p->queue_name) free (p->queue_name);
7592 if (p->nickname) free (p->nickname);
7593 free (p);
7594 return NULL;
7595 }
7596
7597 void
remove_printer_entry(remote_printer_t * p)7598 remove_printer_entry(remote_printer_t *p) {
7599 remote_printer_t *q = NULL, *r;
7600
7601 if (p == NULL) {
7602 debug_printf ("ERROR: remove_printer_entry(): Supplied printer entry is NULL");
7603 return;
7604 }
7605
7606 if (!p->slave_of) {
7607 /* Check whether this queue has a slave from another server and
7608 find it */
7609 for (q = (remote_printer_t *)cupsArrayFirst(remote_printers);
7610 q;
7611 q = (remote_printer_t *)cupsArrayNext(remote_printers))
7612 if (q != p && q->slave_of == p &&
7613 q->status != STATUS_DISAPPEARED && q->status != STATUS_UNCONFIRMED &&
7614 q->status != STATUS_TO_BE_RELEASED)
7615 break;
7616 }
7617 if (q) {
7618 /* Make q the master of the cluster and p a slave of q. This way
7619 removal of p does not delete the cluster's CUPS queue and update
7620 of q makes sure the cluster's queue gets back into working state */
7621 for (r = (remote_printer_t *)cupsArrayFirst(remote_printers);
7622 r;
7623 r = (remote_printer_t *)cupsArrayNext(remote_printers))
7624 if (r != q && r->slave_of == p &&
7625 r->status != STATUS_DISAPPEARED && r->status != STATUS_UNCONFIRMED &&
7626 r->status != STATUS_TO_BE_RELEASED)
7627 r->slave_of = q;
7628 q->slave_of = NULL;
7629 p->slave_of = q;
7630 q->num_options = p->num_options;
7631 q->options = p->options;
7632 p->num_options = 0;
7633 p->options = NULL;
7634 /* Schedule this printer for updating the CUPS queue */
7635 q->status = STATUS_TO_BE_CREATED;
7636 q->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
7637 debug_printf("Printer %s (%s) diasappeared, replacing by backup on host %s, port %d with URI %s.\n",
7638 p->queue_name, p->uri, q->host, q->port, q->uri);
7639 } else
7640 debug_printf("Printer %s (Host: %s, Port: %d, URI: %s) disappeared and no slave available (or it is a slave of another printer), removing entry.\n",
7641 p->queue_name, p->host, p->port, p->uri);
7642
7643 /* Schedule entry and its CUPS queue for removal */
7644 if (p->status != STATUS_TO_BE_RELEASED)
7645 p->status = STATUS_DISAPPEARED;
7646 p->timeout = time(NULL) + TIMEOUT_REMOVE;
7647 }
7648
update_cups_queues(gpointer unused)7649 gboolean update_cups_queues(gpointer unused) {
7650 remote_printer_t *p, *q, *r, *s, *master;
7651 http_t *http;
7652 char uri[HTTP_MAX_URI], device_uri[HTTP_MAX_URI], buf[1024],
7653 line[1024];
7654 int num_options;
7655 cups_option_t *options;
7656 int num_jobs;
7657 cups_job_t *jobs;
7658 ipp_t *request;
7659 time_t current_time;
7660 int i, ap_remote_queue_id_line_inserted,
7661 want_raw, num_cluster_printers = 0;
7662 char *disabled_str;
7663 char *ppdfile, *ifscript;
7664 int fd = 0; /* Script file descriptor */
7665 char tempfile[1024]; /* Temporary file */
7666 char buffer[8192]; /* Buffer for creating script */
7667 int bytes;
7668 const char *cups_serverbin; /* CUPS_SERVERBIN environment variable */
7669 #ifdef HAVE_CUPS_1_6
7670 ipp_attribute_t *attr;
7671 int count, left, right, bottom, top;
7672 const char *default_page_size = NULL, *best_color_space = NULL,
7673 *color_space;
7674 #endif
7675 const char *loadedppd = NULL;
7676 ppd_file_t *ppd = NULL;
7677 ppd_choice_t *choice;
7678 cups_file_t *in, *out;
7679 char keyword[1024], *keyptr;
7680 const char *customval;
7681 const char *val = NULL;
7682 cups_dest_t *dest = NULL;
7683 int is_shared;
7684 cups_array_t *conflicts = NULL;
7685 ipp_t *printer_attributes = NULL;
7686 cups_array_t *sizes=NULL;
7687 ipp_t *printer_ipp_response;
7688 char *make_model = NULL;
7689 const char *pdl=NULL;
7690 int color;
7691 int duplex;
7692 char *default_pagesize = NULL;
7693 const char *default_color = NULL;
7694 int cups_queues_updated = 0;
7695
7696 /* Create dummy entry to point slaves at when their master is about to
7697 get removed now (if we point them to NULL, we would try to remove
7698 the already removed CUPS queue again when it comes to the removal
7699 of the slave. */
7700 if (deleted_master == NULL) {
7701 if ((deleted_master =
7702 (remote_printer_t *)calloc(1, sizeof(remote_printer_t))) == NULL) {
7703 debug_printf("ERROR: Unable to allocate memory.\n");
7704 if (in_shutdown == 0)
7705 recheck_timer ();
7706 return FALSE;
7707 }
7708 memset(deleted_master, 0, sizeof(remote_printer_t));
7709 deleted_master->uri = "<DELETED>";
7710 }
7711
7712 /* Now redirect the slave_of pointers of the masters which get deleted now
7713 to this dummy entry */
7714 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
7715 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
7716 if ((p->status == STATUS_DISAPPEARED ||
7717 p->status == STATUS_TO_BE_RELEASED) &&
7718 (q = p->slave_of) != NULL && q->queue_name &&
7719 (q->status == STATUS_DISAPPEARED || q->status == STATUS_TO_BE_RELEASED))
7720 p->slave_of = deleted_master;
7721
7722 debug_printf("Processing printer list ...\n");
7723 log_all_printers();
7724 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
7725 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
7726
7727 /* We need to get the current time as precise as possible for retries
7728 and reset the timeout flag */
7729 current_time = time(NULL);
7730 timeout_reached = 0;
7731
7732 /* terminating means we have received a signal and should shut down.
7733 in_shutdown means we have exited the main loop.
7734 update_cups_queues() is called after having exited the main loop
7735 in order to remove any queues we have set up */
7736 if (terminating && !in_shutdown) {
7737 debug_printf("Stopping processing printer list because cups-browsed is terminating.\n");
7738 break;
7739 }
7740
7741 /* We do not necessarily update all local CUPS queues which are
7742 scheduled for creation, update, or removal in a single call of
7743 the update_cups_queues() function, as then we could be stuck in
7744 this function for a long time and other tasks of cups-browsed,
7745 especially directing print jobs to destination printers before
7746 the implicitclass backend times out, will not get done in time.
7747 We schedule a new call of update_cups_queues() after a short
7748 delay to continue with the next local CUPS queues. */
7749 if (!in_shutdown && update_cups_queues_max_per_call > 0 &&
7750 cups_queues_updated >= update_cups_queues_max_per_call) {
7751 debug_printf("Stopping processing printer list here because the update_cups_queues() function has reached its per-call limit of %d queue updates. Continuing in further calls.\n",
7752 update_cups_queues_max_per_call);
7753 break;
7754 }
7755
7756 switch (p->status) {
7757
7758 /* Print queue generated by us in a previous session */
7759 case STATUS_UNCONFIRMED:
7760
7761 /* Only act if the timeout has passed */
7762 if (p->timeout > current_time)
7763 break;
7764
7765 /* Queue not reported again by DNS-SD, remove it */
7766 debug_printf("No remote printer named %s available, removing entry from previous session.\n",
7767 p->queue_name);
7768 remove_printer_entry(p);
7769
7770 /* DNS-SD has reported this printer as disappeared or we have replaced
7771 this printer by another one */
7772 case STATUS_DISAPPEARED:
7773 case STATUS_TO_BE_RELEASED:
7774
7775 /* Only act if the timeout has passed */
7776 if (p->timeout > current_time)
7777 break;
7778
7779 debug_printf("Removing entry %s (%s)%s.\n", p->queue_name, p->uri,
7780 (p->slave_of ||
7781 p->status == STATUS_TO_BE_RELEASED ? "" :
7782 " and its CUPS queue"));
7783
7784 /* Slaves do not have a CUPS queue */
7785 if ((q = p->slave_of) == NULL) {
7786 if ((http = http_connect_local ()) == NULL) {
7787 debug_printf("Unable to connect to CUPS!\n");
7788 if (in_shutdown == 0) {
7789 current_time = time(NULL);
7790 p->timeout = current_time + TIMEOUT_RETRY;
7791 }
7792 break;
7793 }
7794
7795 /* Do not auto-save option settings due to the print queue removal
7796 process or release process */
7797 p->no_autosave = 1;
7798
7799 /* Record the option settings to retrieve them when the remote
7800 queue re-appears later or when cups-browsed gets started again */
7801 record_printer_options(p->queue_name);
7802
7803 if (p->status != STATUS_TO_BE_RELEASED &&
7804 !queue_overwritten(p)) {
7805 /* Remove the CUPS queue */
7806
7807 /* Check whether there are still jobs and do not remove the queue
7808 then */
7809 num_jobs = 0;
7810 jobs = NULL;
7811 num_jobs = cupsGetJobs2(http, &jobs, p->queue_name, 0,
7812 CUPS_WHICHJOBS_ACTIVE);
7813 if (num_jobs > 0) { /* There are still jobs */
7814 debug_printf("Queue has still jobs or CUPS error!\n");
7815 cupsFreeJobs(num_jobs, jobs);
7816 /* Disable the queue */
7817 #ifdef HAVE_AVAHI
7818 if (avahi_present || p->domain == NULL || p->domain[0] == '\0')
7819 /* If avahi has got shut down, do not disable queues which are,
7820 created based on DNS-SD broadcasts as the server has most
7821 probably not gone away */
7822 #endif /* HAVE_AVAHI */
7823 disable_printer(p->queue_name,
7824 "Printer disappeared or cups-browsed shutdown");
7825 /* Schedule the removal of the queue for later */
7826 if (in_shutdown == 0) {
7827 current_time = time(NULL);
7828 p->timeout = current_time + TIMEOUT_RETRY;
7829 p->no_autosave = 0;
7830 break;
7831 } else
7832 /* Make sure queue's list entry gets freed */
7833 goto keep_queue;
7834 }
7835
7836 /* If this queue was the default printer, note that fact so that
7837 it gets the default printer again when it re-appears, also switch
7838 back to the last local default printer */
7839 queue_removal_handle_default(p->queue_name);
7840
7841 /* If we do not have a subscription to CUPS' D-Bus notifications and
7842 so no default printer management, we simply do not remove this
7843 CUPS queue if it is the default printer, to not cause a change
7844 of the default printer or the loss of the information that this
7845 printer is the default printer. */
7846 if (cups_notifier == NULL && is_cups_default_printer(p->queue_name)) {
7847 /* Schedule the removal of the queue for later */
7848 if (in_shutdown == 0) {
7849 current_time = time(NULL);
7850 p->timeout = current_time + TIMEOUT_RETRY;
7851 p->no_autosave = 0;
7852 break;
7853 } else
7854 /* Make sure queue's list entry gets freed */
7855 goto keep_queue;
7856 }
7857
7858 /* No jobs, remove the CUPS queue */
7859 debug_printf("Removing local CUPS queue %s (%s).\n", p->queue_name,
7860 p->uri);
7861 request = ippNewRequest(CUPS_DELETE_PRINTER);
7862 /* Printer URI: ipp://localhost/printers/<queue name> */
7863 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
7864 "localhost", 0, "/printers/%s",
7865 p->queue_name);
7866 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
7867 "printer-uri", NULL, uri);
7868 /* Default user */
7869 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
7870 "requesting-user-name", NULL, cupsUser());
7871 /* Do it */
7872 ippDelete(cupsDoRequest(http, request, "/admin/"));
7873
7874 cups_queues_updated ++;
7875
7876 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE &&
7877 cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND) {
7878 debug_printf("Unable to remove CUPS queue! (%s)\n",
7879 cupsLastErrorString());
7880 if (in_shutdown == 0) {
7881 current_time = time(NULL);
7882 p->timeout = current_time + TIMEOUT_RETRY;
7883 p->no_autosave = 0;
7884 break;
7885 }
7886 }
7887 }
7888 }
7889
7890 keep_queue:
7891
7892 /* CUPS queue removed or released from cups-browsed, remove the list
7893 entry */
7894 /* Note that we do not need to break out of the loop passing through
7895 all elements of a CUPS array when we remove an element via the
7896 cupsArrayRemove() function, as the function decreases the array-
7897 internal index by one and so the cupsArrayNext() call gives us
7898 the element right after the deleted element. So no skipping
7899 of an element and especially no reading beyond the end of the
7900 array. */
7901 cupsArrayRemove(remote_printers, p);
7902 if (p->queue_name) free (p->queue_name);
7903 if (p->location) free (p->location);
7904 if (p->info) free (p->info);
7905 if (p->make_model) free (p->make_model);
7906 if (p->pdl) free (p->pdl);
7907 if (p->uri) free (p->uri);
7908 cupsFreeOptions(p->num_options, p->options);
7909 if (p->host) free (p->host);
7910 if (p->ip) free (p->ip);
7911 if (p->resource) free (p->resource);
7912 if (p->service_name) free (p->service_name);
7913 if (p->type) free (p->type);
7914 if (p->domain) free (p->domain);
7915 cupsArrayDelete(p->ipp_discoveries);
7916 if (p->prattrs) ippDelete (p->prattrs);
7917 if (p->nickname) free (p->nickname);
7918 free(p);
7919 p = NULL;
7920
7921 /* If auto shutdown is active and all printers we have set up got removed
7922 again, schedule the shutdown in autoshutdown_timeout seconds
7923 Note that in this case we also do not have jobs any more so if we
7924 auto shutdown on running out of jobs, trigger it here, too. */
7925 if (in_shutdown == 0 && autoshutdown && !autoshutdown_exec_id &&
7926 (cupsArrayCount(remote_printers) == 0 ||
7927 (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
7928 debug_printf ("No printers there any more to make available or no jobs, shutting down in %d sec...\n", autoshutdown_timeout);
7929 autoshutdown_exec_id =
7930 g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
7931 NULL);
7932 }
7933
7934 break;
7935
7936 /* DNS-SD has reported a new remote printer, create a CUPS queue for it,
7937 or upgrade an existing queue, or update a queue to use a backup host
7938 when it has disappeared on the currently used host */
7939 /* (...or, we've just received a CUPS Browsing packet for this queue) */
7940 case STATUS_TO_BE_CREATED:
7941
7942 /* Do not create a queue for slaves */
7943 if (p->slave_of) {
7944 master = p->slave_of;
7945 if (master->queue_name) {
7946 p->status = STATUS_CONFIRMED;
7947 master->status = STATUS_TO_BE_CREATED;
7948 master->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
7949 if (p->is_legacy) {
7950 p->timeout = time(NULL) + BrowseTimeout;
7951 debug_printf("starting BrowseTimeout timer for %s (%ds)\n",
7952 p->queue_name, BrowseTimeout);
7953 } else
7954 p->timeout = (time_t) -1;
7955 } else {
7956 debug_printf("Master for slave %s is invalid (deleted?)\n",
7957 p->queue_name);
7958 p->status = STATUS_DISAPPEARED;
7959 p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
7960 }
7961 break;
7962 }
7963
7964 /* Only act if the timeout has passed */
7965 if (p->timeout > current_time)
7966 break;
7967
7968 /* cups-browsed tried to add this print queue unsuccessfully for too
7969 many times due to timeouts - Skip print queue creation for this one */
7970 if (p->timeouted >= HttpMaxRetries) {
7971 debug_printf("Max number of retries (%d) for creating print queue %s reached, skipping it.\n",
7972 HttpMaxRetries, p->queue_name);
7973 continue;
7974 }
7975
7976 debug_printf("Creating/Updating CUPS queue %s\n",
7977 p->queue_name);
7978
7979 /* Make sure to have a connection to the local CUPS daemon */
7980 if ((http = http_connect_local ()) == NULL) {
7981 debug_printf("Unable to connect to CUPS!\n");
7982 current_time = time(NULL);
7983 p->timeout = current_time + TIMEOUT_RETRY;
7984 break;
7985 }
7986 httpSetTimeout(http, HttpLocalTimeout, http_timeout_cb, NULL);
7987
7988 /* Do not auto-save option settings due to the print queue creation
7989 process */
7990 p->no_autosave = 1;
7991
7992 /* Printer URI: ipp://localhost/printers/<queue name> */
7993 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
7994 "localhost", 0, "/printers/%s", p->queue_name);
7995
7996 ifscript = NULL;
7997 ppdfile = NULL;
7998
7999 #ifdef HAVE_CUPS_1_6
8000 /* Check whether there is a temporary CUPS queue which we would
8001 overwrite */
8002 dest = NULL;
8003 if (OnlyUnsupportedByCUPS == 0)
8004 dest = cupsGetNamedDest(http, p->queue_name, NULL);
8005 if (dest) {
8006 /* CUPS has found a queue with this name.
8007 Either CUPS generates a temporary queue here or we have already
8008 made this queue permanent. In any case, load the PPD from this
8009 queue to conserve the PPD which CUPS has originally generated. */
8010 if (p->netprinter == 1 && IPPPrinterQueueType == PPD_YES &&
8011 UseCUPSGeneratedPPDs) {
8012 if (LocalQueueNamingIPPPrinter != LOCAL_QUEUE_NAMING_DNSSD) {
8013 debug_printf("Local queue %s: We can replace temporary CUPS queues and keep their PPD file only when we name our queues like them, to avoid duplicate queues to the same printer.\n",
8014 p->queue_name);
8015 debug_printf("Not loading PPD from temporary CUPS queue for this printer.\n");
8016 debug_printf("Try setting \"LocalQueueNamingIPPPrinter DNS-SD\" in cups-browsed.conf.\n");
8017 } else {
8018 /* This call makes CUPS actually create the queue so that we can
8019 grab the PPD. We discard the result of the call. */
8020 debug_printf("Establishing dummy connection to make CUPS create the temporary queue.\n");
8021 cups_dinfo_t *dinfo = cupsCopyDestInfo(http, dest);
8022 if (dinfo == NULL)
8023 debug_printf("Unable to connect to destination.\n");
8024 else {
8025 debug_printf("Temporary queue created, grabbing the PPD.\n");
8026 cupsFreeDestInfo(dinfo);
8027 loadedppd = NULL;
8028 if ((loadedppd = loadPPD(http, p->queue_name)) == NULL)
8029 debug_printf("Unable to load PPD from local temporary queue %s!\n",
8030 p->queue_name);
8031 else {
8032 ppdfile = strdup(loadedppd);
8033 debug_printf("Loaded PPD file %s from local temporary queue %s.\n",
8034 ppdfile, p->queue_name);
8035 }
8036 }
8037 }
8038 }
8039 /* If we have already a temporary CUPS queue our local queue we
8040 are creating would overwrite the temporary queue, and so the
8041 resulting queue will still be considered temporary by CUPS and
8042 removed after one minute of inactivity. To avoid this we need
8043 to convert the queue into a permanent one and CUPS does this
8044 only by sharing the queue (setting its boolean printer-is-shared
8045 option. We unset the bit right after that to not actually share
8046 the queue (if we want to share the queue we take care about this
8047 later).
8048 Note that we cannot reliably determine whether we have a
8049 temporary queue via the printer-is-temporary attribute,
8050 therefore we consider only shared queues as for sure
8051 permanent and not shared queues as possibly temporary. To
8052 assure we have a permanent queue in the end we set and
8053 remove the shared bit on any queue which is not shared.
8054 If the temporary queue is pointing to a remote CUPS printer
8055 we cannot modify its printer-is-shared option as CUPS prevents
8056 this. In this case we remove the temporary queue so that we
8057 create a fresh one which will always be permanent.
8058 If the temporary queue has still jobs we will not remove it to
8059 not loose the jobs and wait with creating our new queue until
8060 the jobs are done. */
8061 val = cupsGetOption ("printer-is-shared",
8062 dest->num_options,
8063 dest->options);
8064 is_shared = val && (!strcasecmp (val, "yes") ||
8065 !strcasecmp (val, "on") ||
8066 !strcasecmp (val, "true"));
8067 cupsFreeDests(1, dest);
8068 if (!is_shared) {
8069 debug_printf("Our new queue overwrites the possibly temporary CUPS queue %s, so we need to assure the queue gets permanent.\n",
8070 p->queue_name);
8071 /* We need to modify the printer-is-shared bit twice if we need to
8072 make a temporary queue permanent but not share this queue */
8073 for (i = 0; i <= 1; i ++) {
8074 if (i == 0)
8075 debug_printf("Setting printer-is-shared bit to make this queue permanent.\n");
8076 else
8077 debug_printf("Unsetting printer-is-shared bit.\n");
8078 request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
8079 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8080 "printer-uri", NULL, uri);
8081 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8082 "requesting-user-name", NULL, cupsUser());
8083 num_options = 0;
8084 options = NULL;
8085 num_options = cupsAddOption("printer-is-shared",
8086 (i == 0 ? "true" : "false"),
8087 num_options, &options);
8088 num_options = cupsAddOption(CUPS_BROWSED_MARK "-default", "true", num_options, &options);
8089 cupsEncodeOptions2(request, num_options, options,
8090 IPP_TAG_OPERATION);
8091 cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
8092 /*
8093 * Do IPP request for printer-is-shared option only when we have
8094 * network printer or if we have remote CUPS queue, do IPP request
8095 * only if we have CUPS older than 2.2.
8096 * When you have remote queue, clean up and break from the loop.
8097 */
8098 if (p->netprinter != 0 || !HAVE_CUPS_2_2 || AllowResharingRemoteCUPSPrinters)
8099 ippDelete(cupsDoRequest(http, request, "/admin/"));
8100 else {
8101 ippDelete(request);
8102 cupsFreeOptions(num_options, options);
8103 break;
8104 }
8105 cupsFreeOptions(num_options, options);
8106 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
8107 debug_printf("Unable change printer-is-shared bit to %s (%s)!\n",
8108 (i == 0 ? "true" : "false"),
8109 cupsLastErrorString());
8110 break;
8111 }
8112 }
8113 /* Error on modifying printer-is-shared bit, removing possibly
8114 temporary queue */
8115 if (i <= 1) {
8116 debug_printf("Removing the possibly temporary CUPS queue.\n");
8117 /* Check whether there are still jobs and do not remove the queue
8118 then */
8119 num_jobs = 0;
8120 jobs = NULL;
8121 num_jobs = cupsGetJobs2(http, &jobs, p->queue_name, 0,
8122 CUPS_WHICHJOBS_ACTIVE);
8123 if (num_jobs > 0) { /* there are still jobs */
8124 debug_printf("Temporary queue has still jobs or CUPS error, retrying later.\n");
8125 cupsFreeJobs(num_jobs, jobs);
8126 /* Schedule the removal of the queue for later */
8127 if (in_shutdown == 0) {
8128 current_time = time(NULL);
8129 p->timeout = current_time + TIMEOUT_RETRY;
8130 p->no_autosave = 0;
8131 }
8132 break;
8133 }
8134 /* No jobs, remove the CUPS queue */
8135 request = ippNewRequest(CUPS_DELETE_PRINTER);
8136 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8137 "printer-uri", NULL, uri);
8138 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8139 "requesting-user-name", NULL, cupsUser());
8140 ippDelete(cupsDoRequest(http, request, "/admin/"));
8141 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE &&
8142 cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND) {
8143 debug_printf("Unable to remove temporary CUPS queue (%s), retrying later\n",
8144 cupsLastErrorString());
8145 if (in_shutdown == 0) {
8146 current_time = time(NULL);
8147 p->timeout = current_time + TIMEOUT_RETRY;
8148 p->no_autosave = 0;
8149 break;
8150 }
8151 }
8152 }
8153 } else
8154 debug_printf("Creating/Updating permanent CUPS queue %s.\n",
8155 p ->queue_name);
8156 } else
8157 debug_printf("Creating permanent CUPS queue %s.\n",
8158 p->queue_name);
8159
8160 /* If we did not already obtain a PPD file from the temporary CUPS queue
8161 or if we want to use a System V interface script for our IPP network
8162 printer, we proceed here */
8163 if (p->netprinter == 1) {
8164 if (p->prattrs == NULL) {
8165 p->prattrs = get_printer_attributes(p->uri, NULL, 0, NULL, 0, 1);
8166 debug_log_out(get_printer_attributes_log);
8167 }
8168 if (p->prattrs == NULL) {
8169 debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n",
8170 p->queue_name, p->uri);
8171 p->status = STATUS_DISAPPEARED;
8172 current_time = time(NULL);
8173 p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8174 goto cannot_create;
8175 }
8176 if (IPPPrinterQueueType == PPD_YES) {
8177 num_cluster_printers = 0;
8178 for (s = (remote_printer_t *)cupsArrayFirst(remote_printers);
8179 s; s = (remote_printer_t *)cupsArrayNext(remote_printers)) {
8180 if (!strcmp(s->queue_name, p->queue_name)) {
8181 if (s->status == STATUS_DISAPPEARED ||
8182 s->status == STATUS_UNCONFIRMED ||
8183 s->status == STATUS_TO_BE_RELEASED )
8184 continue;
8185 num_cluster_printers ++;
8186 }
8187 }
8188
8189 if (num_cluster_printers == 1) {
8190 printer_attributes = p->prattrs;
8191 conflicts = NULL;
8192 default_pagesize = NULL;
8193 default_color = NULL;
8194 make_model = p->make_model;
8195 pdl = p->pdl;
8196 color = p->color;
8197 duplex = p->duplex;
8198 sizes = NULL;
8199 } else {
8200 make_model = (char*)malloc(sizeof(char) * 256);
8201 printer_attributes = get_cluster_attributes(p->queue_name);
8202 if ((attr = ippFindAttribute(printer_attributes,
8203 "printer-make-and-model",
8204 IPP_TAG_TEXT)) != NULL)
8205 strncpy(make_model, ippGetString(attr, 0, NULL),
8206 sizeof(make_model) - 1);
8207 color = 0;
8208 duplex = 0;
8209 for (r = (remote_printer_t *)cupsArrayFirst(remote_printers);
8210 r; r = (remote_printer_t *)cupsArrayNext(remote_printers)) {
8211 if (!strcmp(p->queue_name, r->queue_name)) {
8212 if (r->color == 1)
8213 color = 1;
8214 if (r->duplex == 1)
8215 duplex = 1;
8216 }
8217 }
8218 default_pagesize = (char *)malloc(sizeof(char)*32);
8219 debug_printf("Generated Merged Attributes for local queue %s\n",
8220 p->queue_name);
8221 conflicts = generate_cluster_conflicts(p->queue_name,
8222 printer_attributes);
8223 debug_printf("Generated Constraints for queue %s\n",
8224 p->queue_name);
8225 sizes = get_cluster_sizes(p->queue_name);
8226 get_cluster_default_attributes(&printer_attributes,
8227 p->queue_name, default_pagesize,
8228 &default_color);
8229 debug_printf("Generated Default Attributes for local queue %s\n",
8230 p->queue_name);
8231 }
8232 if (ppdfile == NULL) {
8233 /* If we do not want CUPS-generated PPDs or we cannot obtain a
8234 CUPS-generated PPD, for example if CUPS does not create a
8235 temporary queue for this printer, we generate a PPD by
8236 ourselves */
8237 printer_ipp_response = (num_cluster_printers == 1) ? p->prattrs :
8238 printer_attributes;
8239 if (!ppdCreateFromIPP2(buffer, sizeof(buffer), printer_ipp_response,
8240 make_model,
8241 pdl, color, duplex, conflicts, sizes,
8242 default_pagesize, default_color)) {
8243 if (errno != 0)
8244 debug_printf("Unable to create PPD file: %s\n",
8245 strerror(errno));
8246 else
8247 debug_printf("Unable to create PPD file: %s\n",
8248 ppdgenerator_msg);
8249 p->status = STATUS_DISAPPEARED;
8250 current_time = time(NULL);
8251 p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8252 goto cannot_create;
8253 } else {
8254 debug_printf("PPD generation successful: %s\n", ppdgenerator_msg);
8255 debug_printf("Created temporary PPD file: %s\n", buffer);
8256 ppdfile = strdup(buffer);
8257 }
8258 }
8259
8260 if (num_cluster_printers != 1) {
8261 if (default_pagesize != NULL) {
8262 free(default_pagesize);
8263 default_pagesize = NULL;
8264 }
8265 if (make_model != NULL) {
8266 free(make_model);
8267 make_model = NULL;
8268 }
8269 if (conflicts != NULL) {
8270 cupsArrayDelete(conflicts);
8271 conflicts = NULL;
8272 }
8273 if (printer_attributes != NULL) {
8274 ippDelete(printer_attributes);
8275 printer_attributes = NULL;
8276 }
8277 if (sizes != NULL) {
8278 cupsArrayDelete(sizes);
8279 sizes = NULL;
8280 }
8281 }
8282 } else if (IPPPrinterQueueType == PPD_NO) {
8283 ppdfile = NULL;
8284
8285 /* Find default page size of the printer */
8286 attr = ippFindAttribute(p->prattrs,
8287 "media-default",
8288 IPP_TAG_ZERO);
8289 if (attr) {
8290 default_page_size = ippGetString(attr, 0, NULL);
8291 debug_printf("Default page size: %s\n",
8292 default_page_size);
8293 p->num_options = cupsAddOption("media-default",
8294 default_page_size,
8295 p->num_options, &(p->options));
8296 } else {
8297 attr = ippFindAttribute(p->prattrs,
8298 "media-ready",
8299 IPP_TAG_ZERO);
8300 if (attr) {
8301 default_page_size = ippGetString(attr, 0, NULL);
8302 debug_printf("Default page size: %s\n",
8303 default_page_size);
8304 p->num_options = cupsAddOption("media-default",
8305 default_page_size,
8306 p->num_options, &(p->options));
8307 } else
8308 debug_printf("No default page size found!\n");
8309 }
8310
8311 /* Find maximum unprintable margins of the printer */
8312 if ((attr = ippFindAttribute(p->prattrs,
8313 "media-bottom-margin-supported",
8314 IPP_TAG_INTEGER)) != NULL) {
8315 for (i = 1, bottom = ippGetInteger(attr, 0),
8316 count = ippGetCount(attr);
8317 i < count;
8318 i ++)
8319 if (ippGetInteger(attr, i) > bottom)
8320 bottom = ippGetInteger(attr, i);
8321 } else
8322 bottom = 1270;
8323 snprintf(buffer, sizeof(buffer), "%d", bottom);
8324 p->num_options = cupsAddOption("media-bottom-margin-default",
8325 buffer,
8326 p->num_options, &(p->options));
8327
8328 if ((attr = ippFindAttribute(p->prattrs,
8329 "media-left-margin-supported",
8330 IPP_TAG_INTEGER)) != NULL) {
8331 for (i = 1, left = ippGetInteger(attr, 0),
8332 count = ippGetCount(attr);
8333 i < count;
8334 i ++)
8335 if (ippGetInteger(attr, i) > left)
8336 left = ippGetInteger(attr, i);
8337 } else
8338 left = 635;
8339 snprintf(buffer, sizeof(buffer), "%d", left);
8340 p->num_options = cupsAddOption("media-left-margin-default",
8341 buffer,
8342 p->num_options, &(p->options));
8343
8344 if ((attr = ippFindAttribute(p->prattrs,
8345 "media-right-margin-supported",
8346 IPP_TAG_INTEGER)) != NULL) {
8347 for (i = 1, right = ippGetInteger(attr, 0),
8348 count = ippGetCount(attr);
8349 i < count;
8350 i ++)
8351 if (ippGetInteger(attr, i) > right)
8352 right = ippGetInteger(attr, i);
8353 } else
8354 right = 635;
8355 snprintf(buffer, sizeof(buffer), "%d", right);
8356 p->num_options = cupsAddOption("media-right-margin-default",
8357 buffer,
8358 p->num_options, &(p->options));
8359
8360 if ((attr = ippFindAttribute(p->prattrs,
8361 "media-top-margin-supported",
8362 IPP_TAG_INTEGER)) != NULL) {
8363 for (i = 1, top = ippGetInteger(attr, 0),
8364 count = ippGetCount(attr);
8365 i < count;
8366 i ++)
8367 if (ippGetInteger(attr, i) > top)
8368 top = ippGetInteger(attr, i);
8369 } else
8370 top = 1270;
8371 snprintf(buffer, sizeof(buffer), "%d", top);
8372 p->num_options = cupsAddOption("media-top-margin-default",
8373 buffer,
8374 p->num_options, &(p->options));
8375
8376 debug_printf("Margins: Left: %d, Right: %d, Top: %d, Bottom: %d\n",
8377 left, right, top, bottom);
8378
8379 /* Find best color space of the printer */
8380 attr = ippFindAttribute(p->prattrs,
8381 "pwg-raster-document-type-supported",
8382 IPP_TAG_ZERO);
8383 if (attr) {
8384 for (i = 0; i < ippGetCount(attr); i ++) {
8385 color_space = ippGetString(attr, i, NULL);
8386 debug_printf("Supported color space: %s\n", color_space);
8387 if (color_space_score(color_space) >
8388 color_space_score(best_color_space))
8389 best_color_space = color_space;
8390 }
8391 debug_printf("Best color space: %s\n",
8392 best_color_space);
8393 p->num_options = cupsAddOption("print-color-mode-default",
8394 best_color_space,
8395 p->num_options, &(p->options));
8396 } else {
8397 debug_printf("No info about supported color spaces found!\n");
8398 p->num_options = cupsAddOption("print-color-mode-default",
8399 p->color == 1 ? "rgb" : "black",
8400 p->num_options, &(p->options));
8401 }
8402
8403 if (p->duplex)
8404 p->num_options = cupsAddOption("sides-default",
8405 "two-sided-long-edge",
8406 p->num_options, &(p->options));
8407
8408 p->num_options = cupsAddOption("output-format-default",
8409 p->pdl,
8410 p->num_options, &(p->options));
8411 p->num_options = cupsAddOption("make-and-model-default",
8412 remove_bad_chars(p->make_model, 0),
8413 p->num_options, &(p->options));
8414
8415 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
8416 cups_serverbin = CUPS_SERVERBIN;
8417
8418 if ((fd = cupsTempFd(tempfile, sizeof(tempfile))) < 0) {
8419 debug_printf("Unable to create interface script file\n");
8420 p->status = STATUS_DISAPPEARED;
8421 current_time = time(NULL);
8422 p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8423 goto cannot_create;
8424 }
8425
8426 debug_printf("Creating temp script file \"%s\"\n", tempfile);
8427
8428 snprintf(buffer, sizeof(buffer),
8429 "#!/bin/sh\n"
8430 "# System V interface script for printer %s generated by cups-browsed\n"
8431 "\n"
8432 "if [ $# -lt 5 -o $# -gt 6 ]; then\n"
8433 " echo \"ERROR: $0 job-id user title copies options [file]\" >&2\n"
8434 " exit 1\n"
8435 "fi\n"
8436 "\n"
8437 "# Read from given file\n"
8438 "if [ -n \"$6\" ]; then\n"
8439 " exec \"$0\" \"$1\" \"$2\" \"$3\" \"$4\" \"$5\" < \"$6\"\n"
8440 "fi\n"
8441 "\n"
8442 "%s/filter/sys5ippprinter \"$1\" \"$2\" \"$3\" \"$4\" \"$5\"\n",
8443 p->queue_name, cups_serverbin);
8444
8445 bytes = write(fd, buffer, strlen(buffer));
8446 if (bytes != strlen(buffer)) {
8447 debug_printf("Unable to write interface script into the file\n");
8448 p->status = STATUS_DISAPPEARED;
8449 current_time = time(NULL);
8450 p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8451 goto cannot_create;
8452 }
8453
8454 close(fd);
8455
8456 ifscript = strdup(tempfile);
8457 }
8458 }
8459 #endif /* HAVE_CUPS_1_6 */
8460
8461 /* Do we have default option settings in cups-browsed.conf? */
8462 if (DefaultOptions) {
8463 debug_printf("Applying default option settings to printer %s: %s\n",
8464 p->queue_name, DefaultOptions);
8465 p->num_options = cupsParseOptions(DefaultOptions, p->num_options,
8466 &p->options);
8467 }
8468
8469 /* Loading saved option settings from last session */
8470 p->num_options = load_printer_options(p->queue_name, p->num_options,
8471 &p->options);
8472
8473 /* Determine whether we have an IPP network printer. If not we
8474 have remote CUPS queue(s) and so we use an implicit class for
8475 load balancing. In this case we will assign an
8476 implicitclass://... device URI, which makes cups-browsed find
8477 the best destination for each job. */
8478 loadedppd = NULL;
8479 if (cups_notifier != NULL && p->netprinter == 0) {
8480 /* We are not an IPP network printer, so we use the device URI
8481 implicitclass://<queue name>/
8482 We use the httpAssembleURI() function here, to percent-encode
8483 the queue name in the URI, so that any allowed character in
8484 a queue name, especially the '@' when we add the server name
8485 to a remote queue's name, goes safely into the URI.
8486 The implicitclass backend uses httpSeparateURI() to decode the
8487 queue name.
8488 We never use the implicitclass backend if we do not have D-Bus
8489 notification from CUPS as we cannot assign a destination printer
8490 to an incoming job then. */
8491 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
8492 "implicitclass", NULL, p->queue_name, 0, NULL);
8493 debug_printf("Print queue %s is for remote CUPS queue(s) and we get notifications from CUPS, using implicit class device URI %s\n",
8494 p->queue_name, device_uri);
8495 if (!ppdfile && !ifscript) {
8496 /* Having another backend than the CUPS "ipp" backend the
8497 options from the PPD of the queue on the server are not
8498 automatically used on the client any more, so we have to
8499 explicitly load the PPD from one of the servers, apply it
8500 to our local queue, and replace its "*cupsFilter(2): ..."
8501 lines by one line making the print data get passed through
8502 to the server without filtering on the client (where not
8503 necessarily the right filters/drivers are installed) so
8504 that it gets filtered on the server. In addition, we prefix
8505 the PPD's NickName, so that automatic PPD updating by the
8506 distribution's package installation/update infrastructure
8507 is suppressed. */
8508 /* Generating the ppd file for the remote cups queue */
8509 if (p->prattrs == NULL) {
8510 p->prattrs = get_printer_attributes(p->uri, NULL, 0, NULL, 0, 1);
8511 debug_log_out(get_printer_attributes_log);
8512 }
8513 if (p->prattrs == NULL) {
8514 debug_printf("get-printer-attributes IPP call failed on printer %s (%s).\n",
8515 p->queue_name, p->uri);
8516 goto cannot_create;
8517 }
8518 num_cluster_printers = 0;
8519 for (s = (remote_printer_t *)cupsArrayFirst(remote_printers);
8520 s; s = (remote_printer_t *)cupsArrayNext(remote_printers)) {
8521 if (!strcmp(s->queue_name, p->queue_name)) {
8522 if (s->status == STATUS_DISAPPEARED ||
8523 s->status == STATUS_UNCONFIRMED ||
8524 s->status == STATUS_TO_BE_RELEASED )
8525 continue;
8526 num_cluster_printers++;
8527 }
8528 }
8529 if (num_cluster_printers == 1) {
8530 printer_attributes = p->prattrs;
8531 conflicts = NULL;
8532 default_pagesize = NULL;
8533 default_color = NULL;
8534 make_model = p->make_model;
8535 pdl = p->pdl;
8536 color = p->color;
8537 duplex = p->duplex;
8538 sizes = NULL;
8539 } else {
8540 make_model = (char*)malloc(sizeof(char)*256);
8541 printer_attributes = get_cluster_attributes(p->queue_name);
8542 if((attr = ippFindAttribute(printer_attributes,
8543 "printer-make-and-model",
8544 IPP_TAG_TEXT)) != NULL)
8545 strncpy(make_model, ippGetString(attr, 0, NULL),
8546 sizeof(make_model) - 1);
8547 color = 0;
8548 duplex = 0;
8549 for (r = (remote_printer_t *)cupsArrayFirst(remote_printers);
8550 r; r = (remote_printer_t *)cupsArrayNext(remote_printers)) {
8551 if (!strcmp(p->queue_name, r->queue_name)) {
8552 if (r->color == 1)
8553 color = 1;
8554 if (r->duplex == 1)
8555 duplex = 1;
8556 }
8557 }
8558 default_pagesize = (char *)malloc(sizeof(char)*32);
8559 debug_printf("Generated Merged Attributes for local queue %s\n",
8560 p->queue_name);
8561 conflicts = generate_cluster_conflicts(p->queue_name,
8562 printer_attributes);
8563 debug_printf("Generated Constraints for queue %s\n",p->queue_name);
8564 sizes = get_cluster_sizes(p->queue_name);
8565 get_cluster_default_attributes(&printer_attributes, p->queue_name,
8566 default_pagesize,&default_color);
8567 debug_printf("Generated Default Attributes for local queue %s\n",
8568 p->queue_name);
8569 }
8570 if (ppdfile == NULL) {
8571 /* If we do not want CUPS-generated PPDs or we cannot obtain a
8572 CUPS-generated PPD, for example if CUPS does not create a
8573 temporary queue for this printer, we generate a PPD by
8574 ourselves */
8575 printer_ipp_response = (num_cluster_printers == 1) ? p->prattrs :
8576 printer_attributes;
8577 if (!ppdCreateFromIPP2(buffer, sizeof(buffer), printer_ipp_response,
8578 make_model,
8579 pdl, color, duplex, conflicts, sizes,
8580 default_pagesize, default_color)) {
8581 if (errno != 0)
8582 debug_printf("Unable to create PPD file: %s\n",
8583 strerror(errno));
8584 else
8585 debug_printf("Unable to create PPD file: %s\n", ppdgenerator_msg);
8586 p->status = STATUS_DISAPPEARED;
8587 current_time = time(NULL);
8588 p->timeout = current_time + TIMEOUT_IMMEDIATELY;
8589 goto cannot_create;
8590 } else {
8591 debug_printf("PPD generation successful: %s\n", ppdgenerator_msg);
8592 debug_printf("Created temporary PPD file: %s\n", buffer);
8593 ppdfile = strdup(buffer);
8594 }
8595 }
8596 }
8597
8598 if (num_cluster_printers != 1) {
8599 if (default_pagesize != NULL) {
8600 free(default_pagesize);
8601 default_pagesize = NULL;
8602 }
8603 if (make_model != NULL) {
8604 free(make_model);
8605 make_model = NULL;
8606 }
8607 if (conflicts != NULL) {
8608 cupsArrayDelete(conflicts);
8609 conflicts = NULL;
8610 }
8611 if (printer_attributes != NULL) {
8612 ippDelete(printer_attributes);
8613 printer_attributes = NULL;
8614 }
8615 if (sizes != NULL) {
8616 cupsArrayDelete(sizes);
8617 sizes = NULL;
8618 }
8619 }
8620 } else {
8621 /* Device URI: using implicitclass backend for IPP network printer */
8622 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
8623 "implicitclass", NULL, p->queue_name, 0, NULL);
8624 if (strlen(device_uri) > HTTP_MAX_URI-1)
8625 device_uri[HTTP_MAX_URI-1] = '\0';
8626 debug_printf("Print queue %s is for an IPP network printer, using implicitclass backend for the printer: %s\n",
8627 p->queue_name, device_uri);
8628 }
8629
8630 /* PPD readily available */
8631 if (ppdfile) {
8632 debug_printf("Using PPD %s for queue %s.\n",
8633 ppdfile, p->queue_name);
8634 loadedppd = ppdfile;
8635 }
8636 if (loadedppd) {
8637 if ((ppd = ppdOpenFile(loadedppd)) == NULL) {
8638 int linenum; /* Line number of error */
8639 ppd_status_t status = ppdLastError(&linenum);
8640 debug_printf("Unable to open PPD \"%s\": %s on line %d.",
8641 loadedppd, ppdErrorString(status), linenum);
8642 current_time = time(NULL);
8643 p->timeout = current_time + TIMEOUT_RETRY;
8644 p->no_autosave = 0;
8645 unlink(loadedppd);
8646 break;
8647 }
8648 ppdMarkDefaults(ppd);
8649 cupsMarkOptions(ppd, p->num_options, p->options);
8650 if ((out = cupsTempFile2(buf, sizeof(buf))) == NULL) {
8651 debug_printf("Unable to create temporary file!\n");
8652 current_time = time(NULL);
8653 p->timeout = current_time + TIMEOUT_RETRY;
8654 p->no_autosave = 0;
8655 ppdClose(ppd);
8656 ppd = NULL;
8657 unlink(loadedppd);
8658 break;
8659 }
8660 if ((in = cupsFileOpen(loadedppd, "r")) == NULL) {
8661 debug_printf("Unable to open the downloaded PPD file!\n");
8662 current_time = time(NULL);
8663 p->timeout = current_time + TIMEOUT_RETRY;
8664 p->no_autosave = 0;
8665 cupsFileClose(out);
8666 ppdClose(ppd);
8667 ppd = NULL;
8668 unlink(loadedppd);
8669 break;
8670 }
8671 debug_printf("Editing PPD file %s for printer %s, setting the option defaults of the previous cups-browsed session%s, saving the resulting PPD in %s.\n",
8672 loadedppd, p->queue_name,
8673 " and doing client-side filtering of the job" ,
8674 buf);
8675 ap_remote_queue_id_line_inserted = 0;
8676 while (cupsFileGets(in, line, sizeof(line))) {
8677 if (!strncmp(line, "*Default", 8)) {
8678 strncpy(keyword, line + 8, sizeof(keyword) - 1);
8679 if ((strlen(line) + 8) > 1023)
8680 keyword[1023] = '\0';
8681 for (keyptr = keyword; *keyptr; keyptr ++)
8682 if (*keyptr == ':' || isspace(*keyptr & 255))
8683 break;
8684 *keyptr++ = '\0';
8685 while (isspace(*keyptr & 255))
8686 keyptr ++;
8687 if (!strcmp(keyword, "PageRegion") ||
8688 !strcmp(keyword, "PageSize") ||
8689 !strcmp(keyword, "PaperDimension") ||
8690 !strcmp(keyword, "ImageableArea")) {
8691 if ((choice = ppdFindMarkedChoice(ppd, "PageSize")) == NULL)
8692 choice = ppdFindMarkedChoice(ppd, "PageRegion");
8693 } else
8694 choice = ppdFindMarkedChoice(ppd, keyword);
8695 if (choice && strcmp(choice->choice, keyptr)) {
8696 if (strcmp(choice->choice, "Custom"))
8697 cupsFilePrintf(out, "*Default%s: %s\n", keyword,
8698 choice->choice);
8699 else if ((customval = cupsGetOption(keyword, p->num_options,
8700 p->options)) != NULL)
8701 cupsFilePrintf(out, "*Default%s: %s\n", keyword, customval);
8702 else
8703 cupsFilePrintf(out, "%s\n", line);
8704 } else
8705 cupsFilePrintf(out, "%s\n", line);
8706 } else if (strncmp(line, "*End", 4)) {
8707 /* Write an "APRemoteQueueID" line to make this queue marked
8708 as remote printer by CUPS */
8709 if (p->netprinter == 0 &&
8710 strncmp(line, "*%", 2) &&
8711 strncmp(line, "*PPD-Adobe:", 11) &&
8712 ap_remote_queue_id_line_inserted == 0 &&
8713 !AllowResharingRemoteCUPSPrinters) {
8714 ap_remote_queue_id_line_inserted = 1;
8715 cupsFilePrintf(out, "*APRemoteQueueID: \"\"\n");
8716 }
8717 /* Simply write out the line as we read it */
8718 cupsFilePrintf(out, "%s\n", line);
8719 }
8720 /* Save the NickName of the PPD to check whether external
8721 manipulations of the print queue have replaced the PPD.
8722 Check whether nickname is defined too */
8723 if (!strncmp(line, "*NickName:", 10) && p->nickname == NULL) {
8724 char *ptr = NULL;
8725 char *end_ptr = NULL;
8726 int nickname_len = 0;
8727
8728 ptr = strchr(line, '"');
8729
8730 if (ptr == NULL)
8731 {
8732 debug_printf("Malformed *Nickname directive in PPD - no double quote in line.\n");
8733 continue;
8734 }
8735
8736 ptr ++;
8737 end_ptr = strchr(ptr, '"');
8738
8739 if (end_ptr == NULL)
8740 {
8741 debug_printf("Malformed *Nickname directive in PPD - no ending double quote\n");
8742 continue;
8743 }
8744
8745 /* both pointers are null terminated, because cupsFileGets() puts
8746 * a null terminator into returned buffer with one line
8747 * here as 'line' array) and those two pointers points on two places
8748 * in the 'line' array.
8749 */
8750 nickname_len = strlen(ptr) - strlen(end_ptr);
8751
8752 if (nickname_len == 0)
8753 {
8754 debug_printf("Malformed *Nickname directive in PPD - empty nickname.\n");
8755 continue;
8756 }
8757
8758 /* alloc one more space for null terminator, calloc() will initialize
8759 * it to null automatically, so then we only copy a string with 'nickname_len'
8760 * length to get a proper null terminated p->nickname.
8761 */
8762 p->nickname = (char*)calloc(nickname_len + 1, sizeof(char));
8763
8764 if (p->nickname != NULL)
8765 strncpy(p->nickname, ptr, nickname_len);
8766 }
8767 }
8768 cupsFilePrintf(out,"*cupsFilter2: \"application/vnd.cups-pdf application/pdf 0 -\"\n");
8769
8770 cupsFileClose(in);
8771 cupsFileClose(out);
8772 ppdClose(ppd);
8773 ppd = NULL;
8774 unlink(loadedppd);
8775 loadedppd = NULL;
8776 if (ppdfile)
8777 {
8778 free(ppdfile);
8779 ppdfile = NULL;
8780 }
8781 ppdfile = strdup(buf);
8782 }
8783
8784 /* Create a new CUPS queue or modify the existing queue */
8785 request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
8786 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8787 "printer-uri", NULL, uri);
8788 /* Default user */
8789 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8790 "requesting-user-name", NULL, cupsUser());
8791 /* Queue should be enabled ... */
8792 ippAddInteger(request, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
8793 IPP_PRINTER_IDLE);
8794 /* ... and accepting jobs */
8795 ippAddBoolean(request, IPP_TAG_PRINTER, "printer-is-accepting-jobs", 1);
8796 num_options = 0;
8797 options = NULL;
8798 /* Device URI: ipp(s)://<remote host>:631/printers/<remote queue>
8799 OR implicitclass://<queue name>/ */
8800 num_options = cupsAddOption("device-uri", device_uri,
8801 num_options, &options);
8802 /* Option cups-browsed=true, marking that we have created this queue */
8803 num_options = cupsAddOption(CUPS_BROWSED_MARK "-default", "true",
8804 num_options, &options);
8805 /* Default option settings from printer entry */
8806 for (i = 0; i < p->num_options; i ++)
8807 if (strcasecmp(p->options[i].name, "printer-is-shared"))
8808 num_options = cupsAddOption(p->options[i].name,
8809 p->options[i].value,
8810 num_options, &options);
8811 /* Encode option list into IPP attributes */
8812 cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
8813 cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
8814 /* Do it */
8815 if (ppdfile) {
8816 debug_printf("Non-raw queue %s with PPD file: %s\n", p->queue_name, ppdfile);
8817 ippDelete(cupsDoFileRequest(http, request, "/admin/", ppdfile));
8818 want_raw = 0;
8819 unlink(ppdfile);
8820 free(ppdfile);
8821 ppdfile = NULL;
8822 } else if (ifscript) {
8823 debug_printf("Non-raw queue %s with interface script: %s\n", p->queue_name, ifscript);
8824 ippDelete(cupsDoFileRequest(http, request, "/admin/", ifscript));
8825 want_raw = 0;
8826 unlink(ifscript);
8827 free(ifscript);
8828 ifscript = NULL;
8829 } else {
8830 if (p->netprinter == 0) {
8831 debug_printf("Raw queue %s\n", p->queue_name);
8832 want_raw = 1;
8833 } else {
8834 debug_printf("Queue %s keeping its current PPD file/interface script\n", p->queue_name);
8835 want_raw = 0;
8836 }
8837 ippDelete(cupsDoRequest(http, request, "/admin/"));
8838 }
8839 cupsFreeOptions(num_options, options);
8840 cups_queues_updated ++;
8841
8842 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
8843 debug_printf("Unable to create/modify CUPS queue (%s)!\n",
8844 cupsLastErrorString());
8845 current_time = time(NULL);
8846 p->timeout = current_time + TIMEOUT_RETRY;
8847 p->no_autosave = 0;
8848 break;
8849 }
8850
8851 /* Do not share a queue which serves only to point to a remote CUPS
8852 printer
8853
8854 We do this in a seperate IPP request as on newer CUPS versions we
8855 get an error when changing the printer-is-shared bit on a queue
8856 pointing to a remote CUPS printer, this way we assure all other
8857 settings be applied amd when setting the printer-is-shared to
8858 false amd this errors, we can safely ignore the error as on queues
8859 pointing to remote CUPS printers the bit is set to false by default
8860 (these printers are never shared)
8861
8862 If our printer is an IPP network printer and not a CUPS queue, we
8863 keep track of whether the user has changed the printer-is-shared
8864 bit and recover this setting. The default setting for a new
8865 queue is configurable via the NewIPPPrinterQueuesShared directive
8866 in cups-browsed.conf */
8867
8868 request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
8869 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8870 "printer-uri", NULL, uri);
8871 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8872 "requesting-user-name", NULL, cupsUser());
8873 num_options = 0;
8874 options = NULL;
8875 if (p->netprinter == 1 &&
8876 (val = cupsGetOption("printer-is-shared", p->num_options,
8877 p->options)) != NULL) {
8878 num_options = cupsAddOption("printer-is-shared", val,
8879 num_options, &options);
8880 debug_printf("Setting printer-is-shared bit to %s.\n", val);
8881 } else if (p->netprinter == 1 && NewIPPPrinterQueuesShared) {
8882 num_options = cupsAddOption("printer-is-shared", "true",
8883 num_options, &options);
8884 debug_printf("Setting printer-is-shared bit.\n");
8885 } else if (NewBrowsePollQueuesShared &&
8886 (val = cupsGetOption("printer-to-be-shared", p->num_options,
8887 p->options)) != NULL) {
8888 num_options = cupsAddOption("printer-is-shared", "true",
8889 num_options, &options);
8890 debug_printf("Setting printer-is-shared bit.\n");
8891 } else {
8892 num_options = cupsAddOption("printer-is-shared", "false",
8893 num_options, &options);
8894 debug_printf("Unsetting printer-is-shared bit.\n");
8895 }
8896 cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
8897 cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
8898 /*
8899 * Do IPP request for printer-is-shared option only when we have
8900 * network printer or if we have remote CUPS queue, do IPP request
8901 * only if we have CUPS older than 2.2.
8902 */
8903 if (p->netprinter != 0 || !HAVE_CUPS_2_2 || AllowResharingRemoteCUPSPrinters)
8904 ippDelete(cupsDoRequest(http, request, "/admin/"));
8905 else
8906 ippDelete(request);
8907 cupsFreeOptions(num_options, options);
8908 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
8909 debug_printf("Unable to modify the printer-is-shared bit (%s)!\n",
8910 cupsLastErrorString());
8911
8912 /* If we are about to create a raw queue or turn a non-raw queue
8913 into a raw one, we apply the "ppd-name=raw" option to remove any
8914 existing PPD file assigned to the queue.
8915
8916 Also here we do a separate IPP request as it errors in some
8917 cases. */
8918 if (want_raw) {
8919 debug_printf("Removing local PPD file for printer %s\n", p->queue_name);
8920 request = ippNewRequest(CUPS_ADD_MODIFY_PRINTER);
8921 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
8922 "printer-uri", NULL, uri);
8923 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
8924 "requesting-user-name", NULL, cupsUser());
8925 num_options = 0;
8926 options = NULL;
8927 num_options = cupsAddOption("ppd-name", "raw",
8928 num_options, &options);
8929 cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
8930 cupsEncodeOptions2(request, num_options, options, IPP_TAG_PRINTER);
8931 ippDelete(cupsDoRequest(http, request, "/admin/"));
8932 cupsFreeOptions(num_options, options);
8933 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
8934 debug_printf("Unable to remove PPD file from the print queue (%s)!\n",
8935 cupsLastErrorString());
8936 }
8937
8938 /* If this queue was the default printer in its previous life, make
8939 it the default printer again. */
8940 queue_creation_handle_default(p->queue_name);
8941
8942 /* If cups-browsed or a failed backend has disabled this
8943 queue, re-enable it. */
8944 if ((disabled_str = is_disabled(p->queue_name, "cups-browsed")) != NULL) {
8945 enable_printer(p->queue_name);
8946 free(disabled_str);
8947 } else if ((disabled_str =
8948 is_disabled(p->queue_name,
8949 "Printer stopped due to backend errors")) !=
8950 NULL) {
8951 enable_printer(p->queue_name);
8952 free(disabled_str);
8953 }
8954
8955 p->status = STATUS_CONFIRMED;
8956 if (p->is_legacy) {
8957 p->timeout = time(NULL) + BrowseTimeout;
8958 debug_printf("starting BrowseTimeout timer for %s (%ds)\n",
8959 p->queue_name, BrowseTimeout);
8960 } else
8961 p->timeout = (time_t) -1;
8962
8963 /* Check if an HTTP timeout happened during the print queue creation
8964 If it does - increment p->timeouted and set status to TO_BE_CREATED
8965 because the creation can fall through the process, have state changed
8966 to STATUS_CONFIRMED and experience the timeout */
8967 /* If no timeout has happened, clear p->timeouted */
8968 if (timeout_reached == 1) {
8969 debug_printf("Timeout happened during creation of the queue %s.\n",
8970 p->queue_name);
8971 p->timeouted ++;
8972 debug_printf("The queue %s already timeouted %d times in a row.\n",
8973 p->queue_name, p->timeouted);
8974 p->status = STATUS_TO_BE_CREATED;
8975 p->timeout = current_time + TIMEOUT_RETRY;
8976 } else if (p->timeouted != 0) {
8977 debug_printf("Creating the queue %s went smoothly after %d timeouts.\n",
8978 p->queue_name, p->timeouted);
8979 p->timeouted = 0;
8980 }
8981
8982 p->no_autosave = 0;
8983 break;
8984
8985 case STATUS_CONFIRMED:
8986 /* Only act if the timeout has passed */
8987 if (p->timeout > current_time)
8988 break;
8989
8990 if (p->is_legacy) {
8991 /* Remove a queue based on a legacy CUPS broadcast when the
8992 broadcast timeout expires without a new broadcast of this
8993 queue from the server */
8994 remove_printer_entry(p);
8995 } else
8996 p->timeout = (time_t) -1;
8997
8998 break;
8999
9000 }
9001 }
9002
9003 /* If we have printer entries which we did not treat yet because of
9004 update_cups_queues_max_per_call we push their timeouts by the
9005 value of pause_between_cups_queue_updates into the future, so
9006 that they only get worked on then. Also printer entries which are
9007 scheduled in a time less than the value of
9008 pause_between_cups_queue_updates will be pushed, so that
9009 update_cups_queues will run the next time only after this
9010 interval */
9011 if (p && !in_shutdown)
9012 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
9013 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
9014 if (p->timeout <= current_time + pause_between_cups_queue_updates)
9015 p->timeout = current_time + pause_between_cups_queue_updates;
9016
9017 cannot_create:
9018 if (printer_attributes != NULL && num_cluster_printers != 1)
9019 ippDelete(printer_attributes);
9020
9021 if (default_pagesize != NULL && num_cluster_printers != 1)
9022 free(default_pagesize);
9023
9024 if (conflicts != NULL && num_cluster_printers != 1)
9025 cupsArrayDelete(conflicts);
9026
9027 if (make_model != NULL && num_cluster_printers != 1)
9028 free(make_model);
9029
9030 if (sizes != NULL && num_cluster_printers != 1)
9031 cupsArrayDelete(sizes);
9032
9033 if (p && !in_shutdown)
9034 remove_printer_entry(p);
9035
9036 log_all_printers();
9037
9038 if (in_shutdown == 0)
9039 recheck_timer ();
9040
9041 /* Don't run this callback again */
9042 return FALSE;
9043 }
9044
9045 static void
recheck_timer(void)9046 recheck_timer (void)
9047 {
9048 remote_printer_t *p;
9049 time_t timeout = (time_t) -1;
9050 time_t now = time(NULL);
9051
9052 if (!gmainloop)
9053 return;
9054
9055 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
9056 p;
9057 p = (remote_printer_t *)cupsArrayNext(remote_printers))
9058 if (p->timeout == (time_t) -1)
9059 continue;
9060 else if (now > p->timeout) {
9061 timeout = 0;
9062 break;
9063 } else if (timeout == (time_t) -1 || p->timeout - now < timeout)
9064 timeout = p->timeout - now;
9065
9066 if (queues_timer_id)
9067 g_source_remove (queues_timer_id);
9068
9069 if (timeout != (time_t) -1) {
9070 debug_printf("checking queues in %ds\n", timeout);
9071 queues_timer_id =
9072 g_timeout_add_seconds (timeout, update_cups_queues, NULL);
9073 } else {
9074 debug_printf("listening\n");
9075 queues_timer_id = 0;
9076 }
9077 }
9078
9079 static gboolean
matched_filters(const char * queue_name,const char * host,uint16_t port,const char * service_name,const char * domain,void * txt)9080 matched_filters (const char *queue_name,
9081 const char *host,
9082 uint16_t port,
9083 const char *service_name,
9084 const char *domain,
9085 void *txt) {
9086 browse_filter_t *filter;
9087 const char *property = NULL;
9088 char buf[10];
9089 #ifdef HAVE_AVAHI
9090 AvahiStringList *entry = NULL;
9091 char *key = NULL, *value = NULL;
9092 #endif /* HAVE_AVAHI */
9093
9094 debug_printf("Matching printer \"%s\" with properties Host = \"%s\", Port = %d, Service Name = \"%s\", Domain = \"%s\" with the BrowseFilter lines in cups-browsed.conf\n",
9095 queue_name, host, port, service_name, domain);
9096 /* Go through all BrowseFilter lines and stop if one line does not match,
9097 rejecting this printer */
9098 for (filter = cupsArrayFirst (browsefilter);
9099 filter;
9100 filter = cupsArrayNext (browsefilter)) {
9101 debug_printf("Matching with line \"BrowseFilter %s%s%s %s\"",
9102 (filter->sense == FILTER_NOT_MATCH ? "NOT " : ""),
9103 (filter->regexp && !filter->cregexp ? "EXACT " : ""),
9104 filter->field, (filter->regexp ? filter->regexp : ""));
9105 #ifdef HAVE_AVAHI
9106 /* Go through the TXT record to see whether this rule applies to a field
9107 in there */
9108 if (txt) {
9109 entry = avahi_string_list_find((AvahiStringList *)txt, filter->field);
9110 if (entry) {
9111 avahi_string_list_get_pair(entry, &key, &value, NULL);
9112 if (key) {
9113 debug_printf(", TXT record entry: %s = %s",
9114 key, (value ? value : ""));
9115 if (filter->regexp) {
9116 /* match regexp */
9117 if (!value)
9118 value = strdup("");
9119 if ((filter->cregexp &&
9120 regexec(filter->cregexp, value, 0, NULL, 0) == 0) ||
9121 (!filter->cregexp && !strcasecmp(filter->regexp, value))) {
9122 if (filter->sense == FILTER_NOT_MATCH) {
9123 avahi_free(key);
9124 avahi_free(value);
9125 goto filter_failed;
9126 }
9127 } else {
9128 if (filter->sense == FILTER_MATCH) {
9129 avahi_free(key);
9130 avahi_free(value);
9131 goto filter_failed;
9132 }
9133 }
9134 } else {
9135 /* match boolean value */
9136 if (filter->sense == FILTER_MATCH) {
9137 if (!value || strcasecmp(value, "T")) {
9138 avahi_free(key);
9139 avahi_free(value);
9140 goto filter_failed;
9141 }
9142 } else {
9143 if (value && !strcasecmp(value, "T")) {
9144 avahi_free(key);
9145 avahi_free(value);
9146 goto filter_failed;
9147 }
9148 }
9149 }
9150 }
9151 avahi_free(key);
9152 avahi_free(value);
9153 goto filter_matched;
9154 }
9155 }
9156 #endif /* HAVE_AVAHI */
9157
9158 /* Does one of the properties outside the TXT record match? */
9159 property = buf;
9160 buf[0] = '\0';
9161 if (!strcasecmp(filter->field, "Name") ||
9162 !strcasecmp(filter->field, "Printer") ||
9163 !strcasecmp(filter->field, "PrinterName") ||
9164 !strcasecmp(filter->field, "Queue") ||
9165 !strcasecmp(filter->field, "QueueName")) {
9166 if (queue_name)
9167 property = queue_name;
9168 } else if (!strcasecmp(filter->field, "Host") ||
9169 !strcasecmp(filter->field, "HostName") ||
9170 !strcasecmp(filter->field, "RemoteHost") ||
9171 !strcasecmp(filter->field, "RemoteHostName") ||
9172 !strcasecmp(filter->field, "Server") ||
9173 !strcasecmp(filter->field, "ServerName")) {
9174 if (host)
9175 property = host;
9176 } else if (!strcasecmp(filter->field, "Port")) {
9177 if (port)
9178 snprintf(buf, sizeof(buf), "%d", port);
9179 } else if (!strcasecmp(filter->field, "Service") ||
9180 !strcasecmp(filter->field, "ServiceName")) {
9181 if (service_name)
9182 property = service_name;
9183 } else if (!strcasecmp(filter->field, "Domain")) {
9184 if (domain)
9185 property = domain;
9186 } else
9187 property = NULL;
9188 if (property) {
9189 if (!filter->regexp)
9190 filter->regexp = "";
9191 if ((filter->cregexp &&
9192 regexec(filter->cregexp, property, 0, NULL, 0) == 0) ||
9193 (!filter->cregexp && !strcasecmp(filter->regexp, property))) {
9194 if (filter->sense == FILTER_NOT_MATCH)
9195 goto filter_failed;
9196 } else {
9197 if (filter->sense == FILTER_MATCH)
9198 goto filter_failed;
9199 }
9200 goto filter_matched;
9201 }
9202
9203 debug_printf(": Field not found --> SKIPPED\n");
9204 continue;
9205
9206 filter_matched:
9207 debug_printf(" --> MATCHED\n");
9208 }
9209
9210 /* All BrowseFilter lines matching, accept this printer */
9211 debug_printf("All BrowseFilter lines matched or skipped, accepting printer %s\n",
9212 queue_name);
9213 return TRUE;
9214
9215 filter_failed:
9216 debug_printf(" --> FAILED\n");
9217 debug_printf("One BrowseFilter line did not match, ignoring printer %s\n",
9218 queue_name);
9219 return FALSE;
9220 }
9221
9222 static gboolean
update_netifs(gpointer data)9223 update_netifs (gpointer data)
9224 {
9225 struct ifaddrs *ifaddr, *ifa;
9226 netif_t *iface, *iface2;
9227 int i, add_to_netifs, addr_size, dupe, if_found, addr_found;
9228 char *host, buf[HTTP_MAX_HOST], *p, list[65536], *l;
9229
9230 debug_printf("update_netifs() in THREAD %ld\n", pthread_self());
9231
9232 update_netifs_sourceid = 0;
9233 if (getifaddrs (&ifaddr) == -1) {
9234 debug_printf("unable to get interface addresses: %s\n",
9235 strerror (errno));
9236 return FALSE;
9237 }
9238
9239 while ((iface = cupsArrayFirst (netifs)) != NULL) {
9240 cupsArrayRemove (netifs, iface);
9241 free (iface->address);
9242 free (iface);
9243 }
9244 while ((host = cupsArrayFirst (local_hostnames)) != NULL) {
9245 cupsArrayRemove (local_hostnames, host);
9246 free (host);
9247 }
9248
9249 memset(list, 0, sizeof(list));
9250 snprintf(list, sizeof(list) - 1, "Network interfaces: ");
9251 l = list + strlen(list);
9252
9253 for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
9254 if_found = 0;
9255 addr_found = 0;
9256
9257 netif_t *iface;
9258
9259 add_to_netifs = 1;
9260
9261 if (ifa->ifa_addr == NULL)
9262 continue;
9263
9264 if (ifa->ifa_broadaddr == NULL)
9265 add_to_netifs = 0;
9266
9267 if (ifa->ifa_flags & IFF_LOOPBACK)
9268 add_to_netifs = 0;
9269
9270 if (!(ifa->ifa_flags & IFF_BROADCAST))
9271 add_to_netifs = 0;
9272
9273 if (ifa->ifa_addr->sa_family == AF_INET)
9274 addr_size = sizeof (struct sockaddr_in);
9275 else if (ifa->ifa_addr->sa_family == AF_INET6)
9276 addr_size = sizeof (struct sockaddr_in6);
9277 else
9278 addr_size = 0;
9279 if (addr_size) {
9280 if (strlen(list) + strlen(ifa->ifa_name) + 1 <=
9281 sizeof(list)) {
9282 snprintf(l, sizeof(list) - strlen(list) - 1,
9283 "%s", ifa->ifa_name);
9284 l = list + strlen(list);
9285 if_found = 1;
9286 }
9287 for (i = 0; i <= 1; i ++)
9288 if (getnameinfo (ifa->ifa_addr, addr_size,
9289 buf, HTTP_MAX_HOST, NULL, 0,
9290 i == 0 ? NI_NUMERICHOST : NI_NAMEREQD) == 0)
9291 if (buf[0]) {
9292 /* Cut off "%..." from IPv6 IP addresses */
9293 if (ifa->ifa_addr->sa_family == AF_INET6 && i == 0 &&
9294 (p = strchr(buf, '%')) != NULL)
9295 *p = '\0';
9296 /* discard if we already have this name or address */
9297 dupe = 0;
9298 for (host = (char *)cupsArrayFirst (local_hostnames);
9299 host != NULL;
9300 host = (char *)cupsArrayNext (local_hostnames))
9301 if (strcasecmp(buf, host) == 0) {
9302 dupe = 1;
9303 break;
9304 }
9305 if (dupe == 0) {
9306 cupsArrayAdd (local_hostnames, strdup(buf));
9307 if (addr_found == 1 && strlen(list) + 3 <=
9308 sizeof(list)) {
9309 snprintf(l, sizeof(list) - strlen(list) - 1,
9310 ", ");
9311 l = list + strlen(list);
9312 }
9313 if (addr_found == 0 && strlen(list) + 3 <=
9314 sizeof(list)) {
9315 snprintf(l, sizeof(list) - strlen(list) - 1,
9316 " (");
9317 l = list + strlen(list);
9318 addr_found = 1;
9319 }
9320 if (strlen(list) + strlen(buf) + 1 <=
9321 sizeof(list)) {
9322 snprintf(l, sizeof(list) - strlen(list) - 1,
9323 "%s", buf);
9324 l = list + strlen(list);
9325 }
9326 }
9327 }
9328 }
9329
9330 if (add_to_netifs == 0)
9331 goto done;
9332
9333 iface = malloc (sizeof (netif_t));
9334 if (iface == NULL) {
9335 debug_printf ("malloc failure\n");
9336 exit (1);
9337 }
9338
9339 iface->address = malloc (HTTP_MAX_HOST);
9340 if (iface->address == NULL) {
9341 free (iface);
9342 debug_printf ("malloc failure\n");
9343 exit (1);
9344 }
9345
9346 iface->address[0] = '\0';
9347 switch (ifa->ifa_addr->sa_family) {
9348 case AF_INET:
9349 /* copy broadcast addr/fill in port first to faciliate dupe compares */
9350 memcpy (&iface->broadcast, ifa->ifa_broadaddr,
9351 sizeof (struct sockaddr_in));
9352 iface->broadcast.ipv4.sin_port = htons (BrowsePort);
9353 /* discard if we already have an interface sharing the broadcast
9354 address */
9355 dupe = 0;
9356 for (iface2 = (netif_t *)cupsArrayFirst (netifs);
9357 iface2 != NULL;
9358 iface2 = (netif_t *)cupsArrayNext (netifs)) {
9359 if (memcmp(&iface2->broadcast, &iface->broadcast,
9360 sizeof(struct sockaddr_in)) == 0) {
9361 dupe = 1;
9362 break;
9363 }
9364 }
9365 if (dupe) break;
9366 getnameinfo (ifa->ifa_addr, sizeof (struct sockaddr_in),
9367 iface->address, HTTP_MAX_HOST,
9368 NULL, 0, NI_NUMERICHOST);
9369 break;
9370
9371 case AF_INET6:
9372 if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *)(ifa->ifa_addr))
9373 ->sin6_addr))
9374 break;
9375
9376 /* see above for order */
9377 memcpy (&iface->broadcast, ifa->ifa_broadaddr,
9378 sizeof (struct sockaddr_in6));
9379 iface->broadcast.ipv6.sin6_port = htons (BrowsePort);
9380 /* discard alias addresses (identical broadcast) */
9381 dupe = 0;
9382 for (iface2 = (netif_t *)cupsArrayFirst (netifs);
9383 iface2 != NULL;
9384 iface2 = (netif_t *)cupsArrayNext (netifs)) {
9385 if (memcmp(&iface2->broadcast, ifa->ifa_broadaddr,
9386 sizeof(struct sockaddr_in6)) == 0) {
9387 dupe = 1;
9388 break;
9389 }
9390 }
9391 if (dupe) break;
9392 getnameinfo (ifa->ifa_addr, sizeof (struct sockaddr_in6),
9393 iface->address, HTTP_MAX_HOST, NULL, 0, NI_NUMERICHOST);
9394 break;
9395 }
9396
9397 if (iface->address[0]) {
9398 cupsArrayAdd (netifs, iface);
9399 if (if_found == 1) {
9400 if (addr_found == 1 && strlen(list) + 3 <= sizeof(list)) {
9401 snprintf(l, sizeof(list) - strlen(list) - 1,
9402 ", ");
9403 l = list + strlen(list);
9404 }
9405 if (addr_found == 0 && strlen(list) + 3 <= sizeof(list)) {
9406 snprintf(l, sizeof(list) - strlen(list) - 1,
9407 " (");
9408 l = list + strlen(list);
9409 addr_found = 1;
9410 }
9411 if (strlen(list) + strlen(iface->address) + 2 <= sizeof(list)) {
9412 snprintf(l, sizeof(list) - strlen(list) - 1,
9413 "%s*", iface->address);
9414 l = list + strlen(list);
9415 }
9416 }
9417 } else {
9418 free (iface->address);
9419 free (iface);
9420 }
9421
9422 done:
9423 if (if_found == 1) {
9424 if (addr_found == 1 && strlen(list) + 2 <= sizeof(list)) {
9425 snprintf(l, sizeof(list) - strlen(list) - 1,
9426 ")");
9427 l = list + strlen(list);
9428 }
9429 if (strlen(list) + 3 <= sizeof(list)) {
9430 snprintf(l, sizeof(list) - strlen(list) - 1,
9431 ", ");
9432 l = list + strlen(list);
9433 }
9434 }
9435 }
9436
9437 if ((l = strrchr(list, ')')) != NULL) {
9438 if (strlen(list) + 2 <= sizeof(list))
9439 *(l + 1) = '\0';
9440 } else {
9441 if (strlen(list) + 5 <= sizeof(list))
9442 snprintf(list + strlen(list), sizeof(list) - strlen(list) - 1,
9443 "None");
9444 }
9445 debug_printf("%s\n", list);
9446
9447 freeifaddrs (ifaddr);
9448
9449 /* If run as a timeout, don't run it again. */
9450 return FALSE;
9451 }
9452
9453 int
is_local_hostname(const char * host_name)9454 is_local_hostname(const char *host_name) {
9455 char *host;
9456
9457 if (host_name == NULL)
9458 return 0;
9459
9460 for (host = (char *)cupsArrayFirst (local_hostnames);
9461 host != NULL;
9462 host = (char *)cupsArrayNext (local_hostnames))
9463 if (strncasecmp(host_name, host, strlen(host)) == 0 &&
9464 (strlen(host_name) == strlen(host) ||
9465 (strlen(host_name) > strlen(host) &&
9466 (strcasecmp(host_name + strlen(host), ".local") == 0 ||
9467 strcasecmp(host_name + strlen(host), ".local.") == 0))))
9468 return 1;
9469
9470 return 0;
9471 }
9472
9473 static remote_printer_t *
examine_discovered_printer_record(const char * host,const char * ip,uint16_t port,char * resource,const char * service_name,const char * location,const char * info,const char * type,const char * domain,const char * interface,int family,void * txt)9474 examine_discovered_printer_record(const char *host,
9475 const char *ip,
9476 uint16_t port,
9477 char *resource,
9478 const char *service_name,
9479 const char *location,
9480 const char *info,
9481 const char *type,
9482 const char *domain,
9483 const char *interface,
9484 int family,
9485 void *txt) {
9486
9487 char uri[HTTP_MAX_URI];
9488 char *remote_host = NULL, *pdl = NULL,
9489 *make_model = NULL;
9490 int color = 1, duplex = 1;
9491 #ifdef HAVE_AVAHI
9492 char *fields[] = { "product", "usb_MDL", "ty", NULL }, **f;
9493 AvahiStringList *entry = NULL;
9494 char *key = NULL, *value = NULL;
9495 char *note_value = NULL;
9496 char service_host_name[1024];
9497 #endif /* HAVE_AVAHI */
9498 remote_printer_t *p = NULL, key_rec;
9499 char *local_queue_name = NULL;
9500 int is_cups_queue;
9501 int raw_queue = 0;
9502 char *ptr;
9503
9504 if (!host || !resource || !service_name || !location || !info || !type ||
9505 !domain) {
9506 debug_printf("ERROR: examine_discovered_printer_record(): Input value missing!\n");
9507 return NULL;
9508 }
9509
9510 is_cups_queue = 0;
9511 memset(uri, 0, sizeof(uri));
9512
9513 /* Find the remote host name.
9514 Used in constructing backup queue name, so need to sanitize.
9515 strdup() is called inside remove_bad_chars() and result is free()-able. */
9516 remote_host = remove_bad_chars(host, 1);
9517
9518 /* If we only want to create queues for printers for which CUPS does
9519 not already auto-create queues, we check here whether we can skip
9520 this printer */
9521 if (OnlyUnsupportedByCUPS) {
9522 if (g_hash_table_find (cups_supported_remote_printers,
9523 local_printer_service_name_matches,
9524 (gpointer *)service_name)) {
9525 /* Found a DNS-SD-discovered CUPS-supported printer whose service name
9526 matches our discovered printer */
9527 debug_printf("Printer with DNS-SD service name \"%s\" does not need to be covered by us as it is already supported by CUPS, skipping.\n",
9528 service_name);
9529 goto fail;
9530 }
9531 }
9532
9533 #ifdef HAVE_AVAHI
9534 if (txt) {
9535 /* Find make and model by the TXT record */
9536 for (f = fields; *f; f ++) {
9537 entry = avahi_string_list_find((AvahiStringList *)txt, *f);
9538 if (entry) {
9539 avahi_string_list_get_pair(entry, &key, &value, NULL);
9540 if (key && value && !strcasecmp(key, *f) && strlen(value) >= 3) {
9541 if (!strcasecmp(key, "product")) {
9542 make_model = strdup(value + 1);
9543 make_model[strlen(make_model) - 1] = '\0';
9544 } else
9545 make_model = strdup(value);
9546 avahi_free(key);
9547 avahi_free(value);
9548 break;
9549 }
9550 avahi_free(key);
9551 avahi_free(value);
9552 }
9553 }
9554 /* Check by the printer-type TXT field whether the discovered printer is a
9555 CUPS queue */
9556 entry = avahi_string_list_find((AvahiStringList *)txt, "printer-type");
9557 if (entry) {
9558 avahi_string_list_get_pair(entry, &key, &value, NULL);
9559 if (key && value && strlen(value) > 1 &&
9560 !strcasecmp(key, "printer-type") && value[0] == '0' &&
9561 value[1] == 'x') {
9562 is_cups_queue = 1;
9563 }
9564 avahi_free(key);
9565 avahi_free(value);
9566 }
9567 }
9568 #else
9569 /* Check by the resource whether the discovered printer is a CUPS queue */
9570 if (!strncasecmp(resource, "printers/", 9) ||
9571 !strncasecmp(resource, "classes/", 8))
9572 /* This is a remote CUPS queue or class */
9573 is_cups_queue = 1;
9574 #endif /* HAVE_AVAHI */
9575 /* If we do not have a TXT record the printer was not discovered via
9576 DNS-SD but via CUPS legacy or LDAP, so it is a remote CUPS queue
9577 and not an IPP network printer. */
9578 if (txt == NULL)
9579 is_cups_queue = 1;
9580 if (is_cups_queue)
9581 debug_printf("Found CUPS queue/class: %s on host %s.\n",
9582 strrchr(resource, '/') + 1, remote_host);
9583 #ifdef HAVE_AVAHI
9584 if (is_cups_queue) {
9585 /* If the remote queue has a PPD file, the "product" field of the
9586 TXT record is populated. If it has no PPD file the remote queue
9587 is a raw queue and so we do not know enough about the printer
9588 behind it for auto-creating a local queue pointing to it. */
9589 if (txt) {
9590 entry = avahi_string_list_find((AvahiStringList *)txt, "product");
9591 if (entry) {
9592 avahi_string_list_get_pair(entry, &key, &value, NULL);
9593 if (!key || !value || strcasecmp(key, "product") || value[0] != '(' ||
9594 value[strlen(value) - 1] != ')') {
9595 raw_queue = 1;
9596 }
9597 avahi_free(key);
9598 avahi_free(value);
9599 } else
9600 raw_queue = 1;
9601 } else if (domain && domain[0] != '\0')
9602 raw_queue = 1;
9603 if (raw_queue && CreateRemoteRawPrinterQueues == 0) {
9604 /* The remote CUPS queue is raw, ignore it */
9605 debug_printf("Remote DNS-SD-advertised CUPS queue %s on host %s is raw, ignored.\n",
9606 strrchr(resource, '/') + 1, remote_host);
9607 free (remote_host);
9608 if (make_model) free (make_model);
9609 return NULL;
9610 }
9611 } else {
9612 if (txt) {
9613 /* Find out which PDLs the printer understands */
9614 entry = avahi_string_list_find((AvahiStringList *)txt, "pdl");
9615 if (entry) {
9616 avahi_string_list_get_pair(entry, &key, &value, NULL);
9617 if (key && value && !strcasecmp(key, "pdl") && strlen(value) >= 3) {
9618 pdl = remove_bad_chars(value, 1);
9619 }
9620 avahi_free(key);
9621 avahi_free(value);
9622 }
9623 /* Find out if we have a color printer */
9624 entry = avahi_string_list_find((AvahiStringList *)txt, "Color");
9625 if (entry) {
9626 avahi_string_list_get_pair(entry, &key, &value, NULL);
9627 if (key && value && !strcasecmp(key, "Color")) {
9628 if (!strcasecmp(value, "T")) color = 1;
9629 if (!strcasecmp(value, "F")) color = 0;
9630 }
9631 avahi_free(key);
9632 avahi_free(value);
9633 }
9634 /* Find out if we have a duplex printer */
9635 entry = avahi_string_list_find((AvahiStringList *)txt, "Duplex");
9636 if (entry) {
9637 avahi_string_list_get_pair(entry, &key, &value, NULL);
9638 if (key && value && !strcasecmp(key, "Duplex")) {
9639 if (!strcasecmp(value, "T")) duplex = 1;
9640 if (!strcasecmp(value, "F")) duplex = 0;
9641 }
9642 avahi_free(key);
9643 avahi_free(value);
9644 }
9645 }
9646 }
9647 /* Extract location from DNS-SD TXT record's "note" field */
9648 if (location[0] == '\0') {
9649 if (txt) {
9650 entry = avahi_string_list_find((AvahiStringList *)txt, "note");
9651 if (entry) {
9652 avahi_string_list_get_pair(entry, &key, ¬e_value, NULL);
9653 if (key && note_value && !strcasecmp(key, "note")) {
9654 debug_printf("examine_discovered_printer_record: TXT.note: |%s|\n",
9655 note_value); /* !! */
9656 location = note_value;
9657 }
9658 avahi_free(key);
9659 /* don't avahi_free(note_value) here! */
9660 }
9661 }
9662 }
9663 /* A NULL location is only passed in from resolve_callback(), which is
9664 HAVE_AVAHI */
9665 #endif /* HAVE_AVAHI */
9666
9667 /* Determine the device URI of the remote printer */
9668 #ifdef HAVE_AVAHI
9669 if (txt && DNSSDBasedDeviceURIs) {
9670 /* Printer is DNS-SD-discovered, so we can give a DNS-SD-service-name-based
9671 device URI to it (only if DNSSDBasedDeviceURIs config option is set) */
9672 snprintf(service_host_name, sizeof(service_host_name), "%s.%s.%s",
9673 service_name, type, domain);
9674 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri) - 1,
9675 (strcasestr(type, "_ipps") ? "ipps" : "ipp"), NULL,
9676 service_host_name, 0, "/%s",
9677 (is_cups_queue ? "cups" : ""));
9678 } else
9679 #endif /* HAVE_AVAHI */
9680 /* Printer is discovered via legacy CUPS or LDAP, so we have to give
9681 a IP-based/host-name-based URI to it ( or for DNS-SD-discovered
9682 printers if DNSSDBasedDeviceURIs config option is not set) */
9683 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri) - 1,
9684 (strcasestr(type, "_ipps") ? "ipps" : "ipp"), NULL,
9685 (ip != NULL ? ip : host), port, "/%s", resource);
9686
9687 /* Determine the queue name */
9688 local_queue_name = get_local_queue_name(service_name, make_model, resource,
9689 remote_host, &is_cups_queue, NULL);
9690 if (local_queue_name == NULL)
9691 goto fail;
9692
9693 if (!matched_filters (local_queue_name, remote_host, port, service_name,
9694 domain, txt)) {
9695 debug_printf("Printer %s does not match BrowseFilter lines in cups-browsed.conf, printer ignored.\n",
9696 local_queue_name);
9697 goto fail;
9698 }
9699
9700
9701 /* Update network interface info if we were discovered by LDAP
9702 or legacy CUPS, needed for the is_local_hostname() function calls.
9703 During DNS-SD discovery the update is already done by the Avahi
9704 event handler function. */
9705 if (type == NULL || type[0] == '\0')
9706 update_netifs(NULL);
9707
9708 /* Check if we have already created a queue for the discovered
9709 printer */
9710 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
9711 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
9712 if (!strcasecmp(p->queue_name, local_queue_name) &&
9713 (p->host[0] == '\0' ||
9714 p->status == STATUS_UNCONFIRMED ||
9715 p->status == STATUS_DISAPPEARED ||
9716 ((!strcasecmp(p->host, remote_host) ||
9717 (is_local_hostname(p->host) && is_local_hostname(remote_host))) &&
9718 (p->port == port ||
9719 (p->port == 631 && port == 443) ||
9720 (p->port == 443 && port == 631)) &&
9721 (txt ||
9722 (strlen(p->uri) - strlen(resource) > 0 &&
9723 !strcasecmp(p->uri + strlen(p->uri) - strlen(resource),
9724 resource))))))
9725 break;
9726
9727 /* Is there a local queue with the same URI as the remote queue? */
9728 if (!p) {
9729 memset(&key_rec, 0, sizeof(key_rec));
9730 key_rec.uri = uri;
9731 key_rec.host = remote_host;
9732 key_rec.port = port;
9733 key_rec.resource = resource;
9734 key_rec.service_name = (char *)service_name;
9735 key_rec.type = (char *)type;
9736 key_rec.domain = (char *)domain;
9737 if (g_hash_table_find (local_printers,
9738 local_printer_is_same_device, &key_rec)) {
9739 /* Found a local queue with the same URI as our discovered printer
9740 would get, so ignore this remote printer */
9741 debug_printf("Printer with URI %s (or IPP/IPPS equivalent) already exists, printer ignored.\n",
9742 uri);
9743 goto fail;
9744 }
9745
9746 /* We need to create a local queue pointing to the
9747 discovered printer */
9748 p = create_remote_printer_entry (local_queue_name, location, info, uri,
9749 remote_host, ip, port, resource,
9750 service_name ? service_name : "", type,
9751 domain, interface, family, pdl, color,
9752 duplex, make_model, is_cups_queue);
9753 } else {
9754 debug_printf("Entry for %s (URI: %s) already exists.\n",
9755 p->queue_name, p->uri);
9756 /* We have already created a local queue, check whether the
9757 discovered service allows us to upgrade the queue to IPPS
9758 or whether the URI part after ipp(s):// has changed, or
9759 whether the discovered queue is discovered via DNS-SD
9760 having more info in contrary to the existing being
9761 discovered by legacy CUPS or LDAP */
9762
9763 int downgrade = 0, upgrade = 0;
9764
9765 /* Get first element of array of interfaces on which this printer
9766 got already discovered, as this one is "lo" when it already got
9767 discovered through the loopback interface (preferred interface) */
9768 ipp_discovery_t *ippdis = cupsArrayFirst(p->ipp_discoveries);
9769
9770 /* Force upgrade if the found entry is marked unconfirmed or
9771 disappeared */
9772 if (p->status == STATUS_UNCONFIRMED ||
9773 p->status == STATUS_DISAPPEARED) {
9774 upgrade = 1;
9775 debug_printf("Replacing printer entry %s (Host: %s, Port: %d) as it was marked %s. New URI: %s\n",
9776 p->queue_name, remote_host, port,
9777 (p->status == STATUS_UNCONFIRMED ? "unconfirmed" :
9778 "disappeared"),
9779 uri);
9780 /* Check if there is a downgrade */
9781 /* IPPS -> IPP */
9782 } else if ((ptr = strcasestr(type, "_ipp")) != NULL &&
9783 *(ptr + 4) != 's' &&
9784 !strncasecmp(p->uri, "ipps:", 5)) {
9785 downgrade = 1;
9786 debug_printf("Printer %s: New discovered service from host %s, port %d, URI %s is only IPP, we have already IPPS, skipping\n",
9787 p->queue_name, remote_host, port, uri);
9788 /* "lo" -> Any non-"lo" interface */
9789 } else if (strcasecmp(interface, "lo") &&
9790 ippdis && !strcasecmp(ippdis->interface, "lo")) {
9791 downgrade = 1;
9792 debug_printf("Printer %s: New discovered service from host %s, port %d, URI %s is from a non-loopback interface, we have already one from the loopback interface, skipping\n",
9793 p->queue_name, remote_host, port, uri);
9794 /* DNS-SD -> CUPS Legacy/LDAP */
9795 } else if (p->domain != NULL && p->domain[0] != '\0' &&
9796 (domain == NULL || domain[0] == '\0') &&
9797 p->type != NULL && p->type[0] != '\0' &&
9798 (type == NULL || type[0] == '\0')) {
9799 downgrade = 1;
9800 debug_printf("Printer %s: New discovered service from host %s, port %d, URI %s is only discovered via legacy CUPS or LDAP, we have already a DNS-SD-discovered one, skipping\n",
9801 p->queue_name, remote_host, port, uri);
9802 }
9803
9804 if (downgrade == 0) {
9805 /* Check if there is an upgrade */
9806 /* IPP -> IPPS */
9807 if (strcasestr(type, "_ipps") &&
9808 !strncasecmp(p->uri, "ipp:", 4)) {
9809 upgrade = 1;
9810 debug_printf("Upgrading printer %s (Host: %s, Port: %d) to IPPS. New URI: %s\n",
9811 p->queue_name, remote_host, port, uri);
9812 /* Any non-"lo" interface -> "lo" */
9813 } else if (!strcasecmp(interface, "lo")) {
9814 upgrade = 1;
9815 debug_printf("Upgrading printer %s (Host: %s, Port: %d) to use loopback interface \"lo\". New URI: %s\n",
9816 p->queue_name, remote_host, port, uri);
9817 /* CUPS Legacy/LDAP -> DNS-SD */
9818 } else if ((p->domain == NULL || p->domain[0] == '\0') &&
9819 domain != NULL && domain[0] != '\0' &&
9820 (p->type == NULL || p->type[0] == '\0') &&
9821 type != NULL && type[0] != '\0') {
9822 upgrade = 1;
9823 debug_printf("Discovered printer %s (Host: %s, Port: %d, URI: %s) by DNS-SD now.\n",
9824 p->queue_name, remote_host, port, uri);
9825 }
9826 }
9827
9828 /* Switch local queue over to this newly discovered service */
9829 if (upgrade == 1) {
9830 /* Remove tiemout of legacy CUPS broadcasting */
9831 if (domain != NULL && domain[0] != '\0' &&
9832 type != NULL && type[0] != '\0' &&
9833 p->is_legacy) {
9834 p->is_legacy = 0;
9835 if (p->status == STATUS_CONFIRMED)
9836 p->timeout = (time_t) -1;
9837 }
9838 free(p->queue_name);
9839 free(p->location);
9840 free(p->info);
9841 free(p->make_model);
9842 free(p->pdl);
9843 free(p->uri);
9844 free(p->host);
9845 free(p->ip);
9846 free(p->resource);
9847 free(p->service_name);
9848 free(p->type);
9849 free(p->domain);
9850 p->queue_name = strdup(local_queue_name);
9851 p->location = strdup(location);
9852 p->info = strdup(info);
9853 p->make_model = (make_model != NULL ? strdup(make_model) : NULL);
9854 p->pdl = (pdl != NULL ? strdup(pdl) : NULL);
9855 p->color = color;
9856 p->duplex = duplex;
9857 p->uri = strdup(uri);
9858 p->status = STATUS_TO_BE_CREATED;
9859 p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
9860 p->host = strdup(remote_host);
9861 p->ip = (ip != NULL ? strdup(ip) : NULL);
9862 p->port = port;
9863 p->resource = strdup(resource);
9864 p->service_name = strdup(service_name);
9865 p->type = strdup(type);
9866 p->domain = strdup(domain);
9867 debug_printf("Switched over to newly discovered entry for this printer.\n");
9868 } else
9869 debug_printf("Staying with previously discovered entry for this printer.\n");
9870
9871 /* Mark queue entry as confirmed if the entry
9872 is unconfirmed */
9873 if (p->status == STATUS_UNCONFIRMED ||
9874 p->status == STATUS_DISAPPEARED) {
9875 debug_printf("Marking entry for %s (URI: %s) as confirmed.\n",
9876 p->queue_name, p->uri);
9877 p->status = STATUS_CONFIRMED;
9878 if (p->is_legacy) {
9879 p->timeout = time(NULL) + BrowseTimeout;
9880 debug_printf("starting BrowseTimeout timer for %s (%ds)\n",
9881 p->queue_name, BrowseTimeout);
9882 } else
9883 p->timeout = (time_t) -1;
9884 /* If this queue was the default printer in its previous life, make
9885 it the default printer again. */
9886 queue_creation_handle_default(p->queue_name);
9887 /* If this queue is disabled, re-enable it. */
9888 enable_printer(p->queue_name);
9889 /* Record the options, to record any changes which happened
9890 while cups-browsed was not running */
9891 record_printer_options(p->queue_name);
9892 }
9893
9894 /* Gather extra info from our new discovery */
9895 if (p->uri[0] == '\0') {
9896 free (p->uri);
9897 p->uri = strdup(uri);
9898 }
9899 if (p->location[0] == '\0') {
9900 free (p->location);
9901 p->location = strdup(location);
9902 }
9903 if (p->info[0] == '\0') {
9904 free (p->info);
9905 p->info = strdup(info);
9906 }
9907 if (p->make_model == NULL || p->make_model[0] == '\0') {
9908 if (p->make_model) free (p->make_model);
9909 p->make_model = (make_model != NULL ? strdup(make_model) : NULL);
9910 }
9911 if (p->pdl == NULL || p->pdl[0] == '\0') {
9912 if (p->pdl) free (p->pdl);
9913 p->pdl = (pdl != NULL ? strdup(pdl) : NULL);
9914 }
9915 p->color = color;
9916 p->duplex = duplex;
9917 if (p->host[0] == '\0') {
9918 free (p->host);
9919 p->host = strdup(remote_host);
9920 }
9921 if (p->ip == NULL || p->ip[0] == '\0') {
9922 if (p->ip) free (p->ip);
9923 p->ip = (ip != NULL ? strdup(ip) : NULL);
9924 }
9925 if (p->port == 0)
9926 p->port = port;
9927 if (p->service_name[0] == '\0' && service_name) {
9928 free (p->service_name);
9929 p->service_name = strdup(service_name);
9930 }
9931 if (p->resource[0] == '\0') {
9932 free (p->resource);
9933 p->resource = strdup(resource);
9934 }
9935 if (p->type[0] == '\0' && type) {
9936 free (p->type);
9937 p->type = strdup(type);
9938 }
9939 if (p->domain[0] == '\0' && domain) {
9940 free (p->domain);
9941 p->domain = strdup(domain);
9942 }
9943 if (domain != NULL && domain[0] != '\0' &&
9944 type != NULL && type[0] != '\0')
9945 ipp_discoveries_add(p->ipp_discoveries, interface, type, family);
9946 p->netprinter = is_cups_queue ? 0 : 1;
9947 }
9948
9949 fail:
9950 free (remote_host);
9951 free (pdl);
9952 free (make_model);
9953 free (local_queue_name);
9954 #ifdef HAVE_AVAHI
9955 if (note_value) avahi_free(note_value);
9956 #endif /* HAVE_AVAHI */
9957
9958 if (p)
9959 debug_printf("DNS-SD IDs: Service name: \"%s\", "
9960 "Service type: \"%s\", Domain: \"%s\"\n",
9961 p->service_name, p->type, p->domain);
9962
9963 return p;
9964 }
9965
9966 static gboolean
allowed(struct sockaddr * srcaddr)9967 allowed (struct sockaddr *srcaddr)
9968 {
9969 allow_t *allow;
9970 int i;
9971 gboolean server_allowed;
9972 allow_sense_t sense;
9973
9974 if (browse_order == ORDER_DENY_ALLOW)
9975 /* BrowseOrder Deny,Allow: Allow server, then apply BrowseDeny lines,
9976 after that BrowseAllow lines */
9977 server_allowed = TRUE;
9978 else
9979 /* BrowseOrder Allow,Deny: Deny server, then apply BrowseAllow lines,
9980 after that BrowseDeny lines */
9981 server_allowed = FALSE;
9982
9983 for (i = 0; i <= 1; i ++) {
9984 if (browse_order == ORDER_DENY_ALLOW)
9985 /* Treat BrowseDeny lines first, then BrowseAllow lines */
9986 sense = (i == 0 ? ALLOW_DENY : ALLOW_ALLOW);
9987 else
9988 /* Treat BrowseAllow lines first, then BrowseDeny lines */
9989 sense = (i == 0 ? ALLOW_ALLOW : ALLOW_DENY);
9990
9991 if (server_allowed == (sense == ALLOW_ALLOW ? TRUE : FALSE))
9992 continue;
9993
9994 if (browseallow_all && sense == ALLOW_ALLOW) {
9995 server_allowed = TRUE;
9996 continue;
9997 }
9998 if (browsedeny_all && sense == ALLOW_DENY) {
9999 server_allowed = FALSE;
10000 continue;
10001 }
10002
10003 for (allow = cupsArrayFirst (browseallow);
10004 allow;
10005 allow = cupsArrayNext (browseallow)) {
10006 if (allow->sense != sense)
10007 continue;
10008
10009 switch (allow->type) {
10010 case ALLOW_INVALID:
10011 break;
10012
10013 case ALLOW_IP:
10014 switch (srcaddr->sa_family) {
10015 case AF_INET:
10016 if (((struct sockaddr_in *) srcaddr)->sin_addr.s_addr ==
10017 allow->addr.ipv4.sin_addr.s_addr) {
10018 server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE);
10019 goto match;
10020 }
10021 break;
10022
10023 case AF_INET6:
10024 if (!memcmp (&((struct sockaddr_in6 *) srcaddr)->sin6_addr,
10025 &allow->addr.ipv6.sin6_addr,
10026 sizeof (allow->addr.ipv6.sin6_addr))) {
10027 server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE);
10028 goto match;
10029 }
10030 break;
10031 }
10032 break;
10033
10034 case ALLOW_NET:
10035 switch (srcaddr->sa_family) {
10036 struct sockaddr_in6 *src6addr;
10037
10038 case AF_INET:
10039 if ((((struct sockaddr_in *) srcaddr)->sin_addr.s_addr &
10040 allow->mask.ipv4.sin_addr.s_addr) ==
10041 allow->addr.ipv4.sin_addr.s_addr) {
10042 server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE);
10043 goto match;
10044 }
10045 break;
10046
10047 case AF_INET6:
10048 src6addr = (struct sockaddr_in6 *) srcaddr;
10049 if (((src6addr->sin6_addr.s6_addr[0] &
10050 allow->mask.ipv6.sin6_addr.s6_addr[0]) ==
10051 allow->addr.ipv6.sin6_addr.s6_addr[0]) &&
10052 ((src6addr->sin6_addr.s6_addr[1] &
10053 allow->mask.ipv6.sin6_addr.s6_addr[1]) ==
10054 allow->addr.ipv6.sin6_addr.s6_addr[1]) &&
10055 ((src6addr->sin6_addr.s6_addr[2] &
10056 allow->mask.ipv6.sin6_addr.s6_addr[2]) ==
10057 allow->addr.ipv6.sin6_addr.s6_addr[2]) &&
10058 ((src6addr->sin6_addr.s6_addr[3] &
10059 allow->mask.ipv6.sin6_addr.s6_addr[3]) ==
10060 allow->addr.ipv6.sin6_addr.s6_addr[3])) {
10061 server_allowed = (sense == ALLOW_ALLOW ? TRUE : FALSE);
10062 goto match;
10063 }
10064 break;
10065 }
10066 }
10067 }
10068 match:
10069 continue;
10070 }
10071
10072 return server_allowed;
10073 }
10074
10075 #ifdef HAVE_AVAHI
resolve_callback(AvahiServiceResolver * r,AvahiIfIndex interface,AvahiProtocol protocol,AvahiResolverEvent event,const char * name,const char * type,const char * domain,const char * host_name,const AvahiAddress * address,uint16_t port,AvahiStringList * txt,AvahiLookupResultFlags flags,AVAHI_GCC_UNUSED void * userdata)10076 static void resolve_callback(AvahiServiceResolver *r,
10077 AvahiIfIndex interface,
10078 AvahiProtocol protocol,
10079 AvahiResolverEvent event,
10080 const char *name,
10081 const char *type,
10082 const char *domain,
10083 const char *host_name,
10084 const AvahiAddress *address,
10085 uint16_t port,
10086 AvahiStringList *txt,
10087 AvahiLookupResultFlags flags,
10088 AVAHI_GCC_UNUSED void* userdata) {
10089 char ifname[IF_NAMESIZE];
10090 AvahiStringList *uuid_entry, *printer_type_entry;
10091 char *uuid_key, *uuid_value;
10092
10093 debug_printf("resolve_callback() in THREAD %ld\n", pthread_self());
10094
10095 if (r == NULL || name == NULL || type == NULL || domain == NULL)
10096 return;
10097
10098 /* Get the interface name */
10099 if (!if_indextoname(interface, ifname)) {
10100 debug_printf("Unable to find interface name for interface %d: %s\n",
10101 interface, strerror(errno));
10102 strncpy(ifname, "Unknown", sizeof(ifname) - 1);
10103 }
10104
10105 /* Ignore local queues of the cupsd we are serving for, identifying them
10106 via UUID */
10107 update_netifs(NULL);
10108 if ((flags & AVAHI_LOOKUP_RESULT_LOCAL) || !strcasecmp(ifname, "lo") ||
10109 is_local_hostname(host_name)) {
10110 update_local_printers ();
10111 uuid_value = NULL;
10112 if (txt && (uuid_entry = avahi_string_list_find(txt, "UUID")))
10113 avahi_string_list_get_pair(uuid_entry, &uuid_key, &uuid_value, NULL);
10114 if (uuid_value && g_hash_table_find (local_printers,
10115 local_printer_has_uuid,
10116 uuid_value)) {
10117 debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s) with UUID %s is from local CUPS, ignored (Avahi lookup result or host name of local machine).\n",
10118 name, type, domain, host_name, port, ifname,
10119 (address ?
10120 (address->proto == AVAHI_PROTO_INET ? "IPv4" :
10121 address->proto == AVAHI_PROTO_INET6 ? "IPv6" :
10122 "IPv4/IPv6 Unknown") :
10123 "IPv4/IPv6 Unknown"), uuid_value);
10124 goto ignore;
10125 }
10126 if (txt &&
10127 (printer_type_entry = avahi_string_list_find(txt, "printer-type")) &&
10128 strcasestr(type, "_ipps")) {
10129 debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s) with UUID %s is from another CUPS instance on the local system and uses IPPS, the local CUPS has problems to print on this printer, so we ignore it (Avahi lookup result or host name of local machine).\n",
10130 name, type, domain, host_name, port, ifname,
10131 (address ?
10132 (address->proto == AVAHI_PROTO_INET ? "IPv4" :
10133 address->proto == AVAHI_PROTO_INET6 ? "IPv6" :
10134 "IPv4/IPv6 Unknown") :
10135 "IPv4/IPv6 Unknown"),
10136 (uuid_value ? uuid_value : "(unknown)"));
10137 goto ignore;
10138 }
10139 }
10140
10141 /* Called whenever a service has been resolved successfully or timed out */
10142
10143 switch (event) {
10144
10145 /* Resolver error */
10146 case AVAHI_RESOLVER_FAILURE:
10147 debug_printf("Avahi-Resolver: Failed to resolve service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s): %s\n",
10148 name, type, domain, host_name, port, ifname,
10149 (address ?
10150 (address->proto == AVAHI_PROTO_INET ? "IPv4" :
10151 address->proto == AVAHI_PROTO_INET6 ? "IPv6" :
10152 "IPv4/IPv6 Unknown") :
10153 "IPv4/IPv6 Unknown"),
10154 avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))));
10155 break;
10156
10157 /* New remote printer found */
10158 case AVAHI_RESOLVER_FOUND: {
10159 AvahiStringList *rp_entry, *adminurl_entry;
10160 char *rp_key, *rp_value, *adminurl_key, *adminurl_value;
10161
10162 debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with host name '%s' and port %d on interface '%s' (%s).\n",
10163 name, type, domain, host_name, port, ifname,
10164 (address ?
10165 (address->proto == AVAHI_PROTO_INET ? "IPv4" :
10166 address->proto == AVAHI_PROTO_INET6 ? "IPv6" :
10167 "IPv4/IPv6 Unknown") :
10168 "IPv4/IPv6 Unknown"));
10169
10170 /* Ignore if terminated (by SIGTERM) */
10171 if (terminating) {
10172 debug_printf("Avahi Resolver: Ignoring because cups-browsed is terminating.\n");
10173 break;
10174 }
10175
10176 if (txt && (rp_entry = avahi_string_list_find(txt, "rp")))
10177 avahi_string_list_get_pair(rp_entry, &rp_key, &rp_value, NULL);
10178 else {
10179 rp_key = strdup("rp");
10180 rp_value = strdup("");
10181 }
10182 if (txt && (adminurl_entry = avahi_string_list_find(txt, "adminurl")))
10183 avahi_string_list_get_pair(adminurl_entry, &adminurl_key,
10184 &adminurl_value, NULL);
10185 else {
10186 adminurl_key = strdup("adminurl");
10187 if (host_name && (adminurl_value = malloc(strlen(host_name) + 8)) != NULL)
10188 sprintf(adminurl_value, "http://%s", host_name);
10189 else
10190 adminurl_value = strdup("");
10191 }
10192
10193 /* If we create queues only for local IPP printers (like IPP-over-USB
10194 with ippusbxd) check whether the entry is local and skip if not.
10195 We also check for remote CUPS (with "printer-type" TXT field) as this
10196 option is only for IPP network printers */
10197 if (CreateIPPPrinterQueues == IPP_PRINTERS_LOCAL_ONLY &&
10198 strcasecmp(ifname, "lo") &&
10199 (!txt || avahi_string_list_find(txt, "printer-type") == NULL)) {
10200 debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, not a local service.\n",
10201 name, type, domain);
10202 goto clean_up;
10203 }
10204
10205 if (txt && rp_key && rp_value && adminurl_key && adminurl_value &&
10206 !strcasecmp(rp_key, "rp") && !strcasecmp(adminurl_key, "adminurl")) {
10207 char *p, instance[64];
10208 /* Extract instance from DNSSD service name (to serve as info field) */
10209 p = strstr(name, " @ ");
10210 if (p) {
10211 int n;
10212 n = p - name;
10213 if (n >= sizeof(instance))
10214 n = sizeof(instance) - 1;
10215 strncpy(instance, name, sizeof(instance) - 1);
10216 instance[n] = '\0';
10217 debug_printf("Avahi-Resolver: Instance: %s\n", instance); /* !! */
10218 } else {
10219 instance[0] = '\0';
10220 }
10221 /* Determine the remote printer's IP */
10222 if (IPBasedDeviceURIs != IP_BASED_URIS_NO ||
10223 (!browseallow_all && cupsArrayCount(browseallow) > 0)) {
10224 struct sockaddr saddr;
10225 struct sockaddr *addr = &saddr;
10226 char *addrstr;
10227 int addrlen;
10228 int addrfound = 0;
10229 if ((addrstr = calloc(256, sizeof(char))) == NULL) {
10230 debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, could not allocate memory to determine IP address.\n",
10231 name, type, domain);
10232 goto clean_up;
10233 }
10234 if (address &&
10235 address->proto == AVAHI_PROTO_INET &&
10236 IPBasedDeviceURIs != IP_BASED_URIS_IPV6_ONLY) {
10237 avahi_address_snprint(addrstr, 256, address);
10238 addr->sa_family = AF_INET;
10239 if (inet_aton(addrstr,
10240 &((struct sockaddr_in *) addr)->sin_addr) &&
10241 allowed(addr))
10242 addrfound = 1;
10243 } else if (address &&
10244 address->proto == AVAHI_PROTO_INET6 &&
10245 interface != AVAHI_IF_UNSPEC &&
10246 IPBasedDeviceURIs != IP_BASED_URIS_IPV4_ONLY) {
10247 strncpy(addrstr, "[v1.", sizeof(addrstr) - 1);
10248 avahi_address_snprint(addrstr + 4, 256 - 6, address);
10249 addrlen = strlen(addrstr + 4);
10250 addr->sa_family = AF_INET6;
10251 if (inet_pton(AF_INET6, addrstr + 4,
10252 &((struct sockaddr_in6 *) addr)->sin6_addr) &&
10253 allowed(addr)) {
10254 if (!strncasecmp(addrstr + 4, "fe", 2) &&
10255 (addrstr[6] == '8' || addrstr[6] == '9' ||
10256 addrstr[6] == 'A' || addrstr[6] == 'B' ||
10257 addrstr[6] == 'a' || addrstr[6] == 'B'))
10258 /* Link-local address, needs specification of interface */
10259 snprintf(addrstr + addrlen + 4, 256 -
10260 addrlen - 4, "%%%s]",
10261 ifname);
10262 else {
10263 addrstr[addrlen + 4] = ']';
10264 addrstr[addrlen + 5] = '\0';
10265 }
10266 addrfound = 1;
10267 }
10268 } else
10269 debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s': No IP address information available.\n",
10270 name, type, domain);
10271 if (addrfound == 1) {
10272 /* Check remote printer type and create appropriate local queue to
10273 point to it */
10274 if (IPBasedDeviceURIs != IP_BASED_URIS_NO ||
10275 !host_name) {
10276 debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' with IP address %s.\n",
10277 name, type, domain, addrstr);
10278 examine_discovered_printer_record((strcasecmp(ifname, "lo") ?
10279 host_name : "localhost"),
10280 addrstr, port, rp_value, name,
10281 "", instance, type, domain,
10282 ifname, addr->sa_family, txt);
10283 } else
10284 examine_discovered_printer_record((strcasecmp(ifname, "lo") ?
10285 host_name : "localhost"),
10286 NULL, port, rp_value,
10287 name, "", instance, type,
10288 domain, ifname, addr->sa_family,
10289 txt);
10290 } else
10291 debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, could not determine IP address.\n",
10292 name, type, domain);
10293 free(addrstr);
10294 } else {
10295 /* Check remote printer type and create appropriate local queue to
10296 point to it */
10297 if (host_name)
10298 examine_discovered_printer_record((strcasecmp(ifname, "lo") ?
10299 host_name : "localhost"),
10300 NULL, port, rp_value,
10301 name, "", instance, type, domain,
10302 ifname,
10303 (address->proto ==
10304 AVAHI_PROTO_INET ? AF_INET :
10305 (address->proto ==
10306 AVAHI_PROTO_INET6 ? AF_INET6 :
10307 0)),
10308 txt);
10309 else
10310 debug_printf("Avahi Resolver: Service '%s' of type '%s' in domain '%s' skipped, host name not supplied.\n",
10311 name, type, domain);
10312 }
10313 }
10314
10315 clean_up:
10316
10317 /* Clean up */
10318
10319 if (rp_entry) {
10320 avahi_free(rp_key);
10321 avahi_free(rp_value);
10322 } else {
10323 free(rp_key);
10324 free(rp_value);
10325 }
10326 if (adminurl_entry) {
10327 avahi_free(adminurl_key);
10328 avahi_free(adminurl_value);
10329 } else {
10330 free(adminurl_key);
10331 free(adminurl_value);
10332 }
10333 break;
10334 }
10335 }
10336
10337 ignore:
10338 avahi_service_resolver_free(r);
10339
10340 if (in_shutdown == 0)
10341 recheck_timer ();
10342 }
10343
browse_callback(AvahiServiceBrowser * b,AvahiIfIndex interface,AvahiProtocol protocol,AvahiBrowserEvent event,const char * name,const char * type,const char * domain,AvahiLookupResultFlags flags,void * userdata)10344 static void browse_callback(AvahiServiceBrowser *b,
10345 AvahiIfIndex interface,
10346 AvahiProtocol protocol,
10347 AvahiBrowserEvent event,
10348 const char *name,
10349 const char *type,
10350 const char *domain,
10351 AvahiLookupResultFlags flags,
10352 void* userdata) {
10353
10354 AvahiClient *c = userdata;
10355 char ifname[IF_NAMESIZE];
10356
10357 debug_printf("browse_callback() in THREAD %ld\n", pthread_self());
10358
10359 if (b == NULL)
10360 return;
10361
10362 /* Called whenever a new services becomes available on the LAN or
10363 is removed from the LAN */
10364
10365 switch (event) {
10366
10367 /* Avahi browser error */
10368 case AVAHI_BROWSER_FAILURE:
10369
10370 debug_printf("Avahi Browser: ERROR: %s\n",
10371 avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))));
10372 g_main_loop_quit(gmainloop);
10373 g_main_context_wakeup(NULL);
10374 return;
10375
10376 /* New service (remote printer) */
10377 case AVAHI_BROWSER_NEW:
10378
10379 if (c == NULL || name == NULL || type == NULL || domain == NULL)
10380 return;
10381
10382 /* Get the interface name */
10383 if (!if_indextoname(interface, ifname)) {
10384 debug_printf("Unable to find interface name for interface %d: %s\n",
10385 interface, strerror(errno));
10386 strncpy(ifname, "Unknown", sizeof(ifname) - 1);
10387 }
10388
10389 debug_printf("Avahi Browser: NEW: service '%s' of type '%s' in domain '%s' on interface '%s' (%s)\n",
10390 name, type, domain, ifname,
10391 protocol != AVAHI_PROTO_UNSPEC ?
10392 avahi_proto_to_string(protocol) : "Unknown");
10393
10394 /* Ignore if terminated (by SIGTERM) */
10395 if (terminating) {
10396 debug_printf("Avahi Browser: Ignoring because cups-browsed is terminating.\n");
10397 break;
10398 }
10399
10400 /* We ignore the returned resolver object. In the callback
10401 function we free it. If the server is terminated before
10402 the callback function is called the server will free
10403 the resolver for us. */
10404
10405 if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c)))
10406 debug_printf("Failed to resolve service '%s': %s\n",
10407 name, avahi_strerror(avahi_client_errno(c)));
10408 break;
10409
10410 /* A service (remote printer) has disappeared */
10411 case AVAHI_BROWSER_REMOVE: {
10412 remote_printer_t *p;
10413
10414 if (name == NULL || type == NULL || domain == NULL)
10415 return;
10416
10417 /* Get the interface name */
10418 if (!if_indextoname(interface, ifname)) {
10419 debug_printf("Unable to find interface name for interface %d: %s\n",
10420 interface, strerror(errno));
10421 strncpy(ifname, "Unknown", sizeof(ifname) - 1);
10422 }
10423
10424 debug_printf("Avahi Browser: REMOVE: service '%s' of type '%s' in domain '%s' on interface '%s' (%s)\n",
10425 name, type, domain, ifname,
10426 protocol != AVAHI_PROTO_UNSPEC ?
10427 avahi_proto_to_string(protocol) : "Unknown");
10428
10429 /* Ignore if terminated (by SIGTERM) */
10430 if (terminating) {
10431 debug_printf("Avahi Browser: Ignoring because cups-browsed is terminating.\n");
10432 break;
10433 }
10434
10435 /* Check whether we have listed this printer */
10436 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
10437 p; p = (remote_printer_t *)cupsArrayNext(remote_printers))
10438 if (p->status != STATUS_DISAPPEARED &&
10439 p->status != STATUS_TO_BE_RELEASED &&
10440 !strcasecmp(p->service_name, name) &&
10441 !strcasecmp(p->domain, domain))
10442 break;
10443 if (p) {
10444 int family =
10445 (protocol == AVAHI_PROTO_INET ? AF_INET :
10446 (protocol == AVAHI_PROTO_INET6 ? AF_INET6 : 0));
10447 if (p->ipp_discoveries) {
10448 ipp_discovery_t *ippdis;
10449 for (ippdis = cupsArrayFirst(p->ipp_discoveries); ippdis;
10450 ippdis = cupsArrayNext(p->ipp_discoveries))
10451 if (!strcasecmp(ippdis->interface, ifname) &&
10452 !strcasecmp(ippdis->type, type) &&
10453 ippdis->family == family) {
10454 debug_printf("Discovered instance for printer with Service name \"%s\", Domain \"%s\" unregistered: Interface \"%s\", Service type: \"%s\", Protocol: \"%s\"\n",
10455 p->service_name, p->domain,
10456 ippdis->interface, ippdis->type,
10457 (ippdis->family == AF_INET ? "IPv4" :
10458 (ippdis->family == AF_INET6 ? "IPv6" : "Unknown")));
10459 cupsArrayRemove(p->ipp_discoveries, (void *)ippdis);
10460 ipp_discoveries_list(p->ipp_discoveries);
10461 break;
10462 }
10463 /* Remove the entry if no discovered instances are left */
10464 if (cupsArrayCount(p->ipp_discoveries) == 0) {
10465 debug_printf("Removing printer with Service name \"%s\", Domain \"%s\", all discovered instances disappeared.\n",
10466 p->service_name, p->domain);
10467 remove_printer_entry(p);
10468 }
10469 }
10470
10471 if (in_shutdown == 0)
10472 recheck_timer ();
10473 }
10474 break;
10475 }
10476
10477 /* All cached Avahi events are treated now */
10478 case AVAHI_BROWSER_ALL_FOR_NOW:
10479 case AVAHI_BROWSER_CACHE_EXHAUSTED:
10480 debug_printf("Avahi Browser: %s\n",
10481 event == AVAHI_BROWSER_CACHE_EXHAUSTED ?
10482 "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
10483 break;
10484 }
10485
10486 }
10487
avahi_browser_shutdown()10488 void avahi_browser_shutdown() {
10489 remote_printer_t *p;
10490
10491 avahi_present = 0;
10492
10493 /* Remove all queues which we have set up based on DNS-SD discovery*/
10494 if (cupsArrayCount(remote_printers) > 0) {
10495 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
10496 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
10497 if (p->type && p->type[0]) {
10498 if (KeepGeneratedQueuesOnShutdown) {
10499 if (p->status != STATUS_TO_BE_RELEASED &&
10500 p->status != STATUS_DISAPPEARED) {
10501 p->status = STATUS_UNCONFIRMED;
10502 p->timeout = time(NULL) + TIMEOUT_CONFIRM;
10503 }
10504 } else {
10505 if (p->status != STATUS_TO_BE_RELEASED)
10506 p->status = STATUS_DISAPPEARED;
10507 p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
10508 }
10509 }
10510 }
10511 if (in_shutdown == 0)
10512 recheck_timer();
10513 else
10514 update_cups_queues(NULL);
10515 }
10516
10517 /* Free the data structures for DNS-SD browsing */
10518 if (sb1) {
10519 avahi_service_browser_free(sb1);
10520 sb1 = NULL;
10521 }
10522 if (sb2) {
10523 avahi_service_browser_free(sb2);
10524 sb2 = NULL;
10525 }
10526
10527 /* Switch on auto shutdown mode */
10528 if (autoshutdown_avahi && in_shutdown == 0) {
10529 autoshutdown = 1;
10530 debug_printf("Avahi server disappeared, switching to auto shutdown mode ...\n");
10531 /* If there are no printers or no jobs schedule the shutdown in
10532 autoshutdown_timeout seconds */
10533 if (!autoshutdown_exec_id &&
10534 (cupsArrayCount(remote_printers) == 0 ||
10535 (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
10536 debug_printf ("We entered auto shutdown mode and no printers are there to make available or no jobs on them, shutting down in %d sec...\n", autoshutdown_timeout);
10537 autoshutdown_exec_id =
10538 g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
10539 NULL);
10540 }
10541 }
10542 }
10543
avahi_shutdown()10544 void avahi_shutdown() {
10545 avahi_browser_shutdown();
10546 if (client) {
10547 avahi_client_free(client);
10548 client = NULL;
10549 }
10550 if (glib_poll) {
10551 avahi_glib_poll_free(glib_poll);
10552 glib_poll = NULL;
10553 }
10554 }
10555
client_callback(AvahiClient * c,AvahiClientState state,AVAHI_GCC_UNUSED void * userdata)10556 static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
10557 int error;
10558
10559 if (c == NULL)
10560 return;
10561
10562 /* Called whenever the client or server state changes */
10563 switch (state) {
10564
10565 /* avahi-daemon available */
10566 case AVAHI_CLIENT_S_REGISTERING:
10567 case AVAHI_CLIENT_S_RUNNING:
10568 case AVAHI_CLIENT_S_COLLISION:
10569
10570 debug_printf("Avahi server connection got available, setting up service browsers.\n");
10571
10572 /* Create the service browsers */
10573 if (!sb1)
10574 if (!(sb1 =
10575 avahi_service_browser_new(c, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
10576 "_ipp._tcp", NULL, 0, browse_callback,
10577 c))) {
10578 debug_printf("ERROR: Failed to create service browser for IPP: %s\n",
10579 avahi_strerror(avahi_client_errno(c)));
10580 }
10581 if (!sb2)
10582 if (!(sb2 =
10583 avahi_service_browser_new(c, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
10584 "_ipps._tcp", NULL, 0, browse_callback,
10585 c))) {
10586 debug_printf("ERROR: Failed to create service browser for IPPS: %s\n",
10587 avahi_strerror(avahi_client_errno(c)));
10588 }
10589
10590 avahi_present = 1;
10591
10592 /* switch off auto shutdown mode */
10593 if (autoshutdown_avahi) {
10594 autoshutdown = 0;
10595 debug_printf("Avahi server available, switching to permanent mode ...\n");
10596 /* If there is still an active auto shutdown timer, kill it */
10597 if (autoshutdown_exec_id) {
10598 debug_printf ("We have left auto shutdown mode, killing auto shutdown timer.\n");
10599 g_source_remove(autoshutdown_exec_id);
10600 autoshutdown_exec_id = 0;
10601 }
10602 }
10603
10604 break;
10605
10606 /* Avahi client error */
10607 case AVAHI_CLIENT_FAILURE:
10608
10609 if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
10610 debug_printf("Avahi server disappeared, shutting down service browsers, removing DNS-SD-discovered print queues.\n");
10611 avahi_browser_shutdown();
10612 /* Renewing client */
10613 avahi_client_free(client);
10614 client = avahi_client_new(avahi_glib_poll_get(glib_poll),
10615 AVAHI_CLIENT_NO_FAIL,
10616 client_callback, NULL, &error);
10617 if (!client) {
10618 debug_printf("ERROR: Failed to create client: %s\n",
10619 avahi_strerror(error));
10620 BrowseRemoteProtocols &= ~BROWSE_DNSSD;
10621 avahi_shutdown();
10622 }
10623 } else {
10624 debug_printf("ERROR: Avahi server connection failure: %s\n",
10625 avahi_strerror(avahi_client_errno(c)));
10626 g_main_loop_quit(gmainloop);
10627 g_main_context_wakeup(NULL);
10628 }
10629 break;
10630
10631 default:
10632 break;
10633 }
10634 }
10635
avahi_init()10636 void avahi_init() {
10637 int error;
10638
10639 if (BrowseRemoteProtocols & BROWSE_DNSSD) {
10640 /* Allocate main loop object */
10641 if (!glib_poll)
10642 if (!(glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT))) {
10643 debug_printf("ERROR: Failed to create glib poll object.\n");
10644 goto avahi_init_fail;
10645 }
10646
10647 /* Allocate a new client */
10648 if (!client)
10649 client = avahi_client_new(avahi_glib_poll_get(glib_poll),
10650 AVAHI_CLIENT_NO_FAIL,
10651 client_callback, NULL, &error);
10652
10653 /* Check wether creating the client object succeeded */
10654 if (!client) {
10655 debug_printf("ERROR: Failed to create client: %s\n",
10656 avahi_strerror(error));
10657 goto avahi_init_fail;
10658 }
10659
10660 return;
10661
10662 avahi_init_fail:
10663 BrowseRemoteProtocols &= ~BROWSE_DNSSD;
10664 avahi_shutdown();
10665 }
10666 }
10667 #endif /* HAVE_AVAHI */
10668
10669 /*
10670 * A CUPS printer has been discovered via CUPS Browsing
10671 * or with BrowsePoll
10672 */
10673 void
found_cups_printer(const char * remote_host,const char * uri,const char * location,const char * info)10674 found_cups_printer (const char *remote_host, const char *uri,
10675 const char *location, const char *info)
10676 {
10677 char scheme[32];
10678 char username[64];
10679 char host[HTTP_MAX_HOST];
10680 char resource[HTTP_MAX_URI];
10681 int port;
10682 netif_t *iface;
10683 char local_resource[HTTP_MAX_URI];
10684 char service_name[HTTP_MAX_URI];
10685 char *c;
10686 int hl;
10687 remote_printer_t *printer;
10688
10689 memset(scheme, 0, sizeof(scheme));
10690 memset(username, 0, sizeof(username));
10691 memset(host, 0, sizeof(host));
10692 memset(resource, 0, sizeof(resource));
10693 memset(local_resource, 0, sizeof(local_resource));
10694
10695 httpSeparateURI (HTTP_URI_CODING_ALL, uri,
10696 scheme, sizeof(scheme) - 1,
10697 username, sizeof(username) - 1,
10698 host, sizeof(host) - 1,
10699 &port,
10700 resource, sizeof(resource)- 1);
10701
10702 /* Check this isn't one of our own broadcasts */
10703 for (iface = cupsArrayFirst (netifs);
10704 iface;
10705 iface = cupsArrayNext (netifs))
10706 if (!strcasecmp (host, iface->address))
10707 break;
10708 if (iface) {
10709 debug_printf("ignoring own broadcast on %s\n",
10710 iface->address);
10711 return;
10712 }
10713
10714 if (strncasecmp (resource, "/printers/", 10) &&
10715 strncasecmp (resource, "/classes/", 9)) {
10716 debug_printf("Don't understand URI: %s\n", uri);
10717 return;
10718 }
10719
10720 strncpy (local_resource, resource + 1, sizeof (local_resource) - 1);
10721 local_resource[sizeof (local_resource) - 1] = '\0';
10722 c = strchr (local_resource, '?');
10723 if (c)
10724 *c = '\0';
10725
10726 /* Build the DNS-SD service name which CUPS would give to this printer
10727 when DNS-SD-broadcasting it */
10728 snprintf(service_name, sizeof (service_name), "%s @ %s",
10729 (info ? info : strchr(local_resource, '/') + 1), host);
10730 /* Cut off trailing ".local" of host name */
10731 hl = strlen(service_name);
10732 if (hl > 6 && !strcasecmp(service_name + hl - 6, ".local"))
10733 service_name[hl - 6] = '\0';
10734 if (hl > 7 && !strcasecmp(service_name + hl - 7, ".local."))
10735 service_name[hl - 7] = '\0';
10736 /* DNS-SD service name has max. 63 characters */
10737 service_name[63] = '\0';
10738
10739 debug_printf("CUPS browsing: Remote host: %s; Port: %d; Remote queue name: %s; Service Name: %s\n",
10740 host, port, strchr(local_resource, '/') + 1, service_name);
10741
10742 printer = examine_discovered_printer_record(host, NULL, port, local_resource,
10743 service_name,
10744 location ? location : "",
10745 info ? info : "", "", "", "", 0,
10746 NULL);
10747
10748 if (printer &&
10749 (printer->domain == NULL || printer->domain[0] == '\0' ||
10750 printer->type == NULL || printer->type[0] == '\0')) {
10751 printer->is_legacy = 1;
10752
10753 if (printer->status != STATUS_TO_BE_CREATED) {
10754 printer->timeout = time(NULL) + BrowseTimeout;
10755 debug_printf("starting BrowseTimeout timer for %s (%ds)\n",
10756 printer->queue_name, BrowseTimeout);
10757 }
10758 }
10759
10760 if (printer && NewBrowsePollQueuesShared &&
10761 (HAVE_CUPS_1_6 || (!HAVE_CUPS_1_6 && !printer->is_legacy)))
10762 printer->num_options = cupsAddOption("printer-to-be-shared", "true", printer->num_options, &(printer->options));
10763
10764 }
10765
10766 gboolean
process_browse_data(GIOChannel * source,GIOCondition condition,gpointer data)10767 process_browse_data (GIOChannel *source,
10768 GIOCondition condition,
10769 gpointer data)
10770 {
10771 char packet[2048];
10772 http_addr_t srcaddr;
10773 socklen_t srclen;
10774 ssize_t got;
10775 unsigned int type;
10776 unsigned int state;
10777 char remote_host[256];
10778 char uri[1024];
10779 char location[1024];
10780 char info[1024];
10781 char *c = NULL, *end = NULL;
10782
10783 debug_printf("process_browse_data() in THREAD %ld\n", pthread_self());
10784
10785 memset(packet, 0, sizeof(packet));
10786 memset(remote_host, 0, sizeof(remote_host));
10787 memset(uri, 0, sizeof(uri));
10788 memset(info, 0, sizeof(info));
10789
10790 srclen = sizeof (srcaddr);
10791 got = recvfrom (browsesocket, packet, sizeof (packet) - 1, 0,
10792 &srcaddr.addr, &srclen);
10793 if (got == -1) {
10794 debug_printf ("cupsd-browsed: error receiving browse packet: %s\n",
10795 strerror (errno));
10796 /* Remove this I/O source */
10797 return FALSE;
10798 }
10799
10800 packet[got] = '\0';
10801 httpAddrString (&srcaddr, remote_host, sizeof (remote_host) - 1);
10802
10803 /* Check this packet is allowed */
10804 if (!allowed ((struct sockaddr *) &srcaddr)) {
10805 debug_printf("browse packet from %s disallowed\n",
10806 remote_host);
10807 return TRUE;
10808 }
10809
10810 debug_printf("browse packet received from %s\n",
10811 remote_host);
10812
10813 if (sscanf (packet, "%x%x%1023s", &type, &state, uri) < 3) {
10814 debug_printf("incorrect browse packet format\n");
10815 return TRUE;
10816 }
10817
10818 info[0] = '\0';
10819
10820 /* do not read OOB */
10821 end = packet + sizeof(packet);
10822 c = strchr (packet, '\"');
10823 if (c >= end)
10824 return TRUE;
10825
10826 if (c) {
10827 /* Extract location field */
10828 {
10829 int i;
10830 c++;
10831 for (i = 0;
10832 i < sizeof (location) - 1 && *c != '\"' && c < end;
10833 i++, c++)
10834 location[i] = *c;
10835 location[i] = '\0';
10836 debug_printf("process_browse_data: location: |%s|\n", location); /* !! */
10837 }
10838 for (; c < end && *c != '\"'; c++)
10839 ;
10840
10841 if (c >= end)
10842 return TRUE;
10843
10844 if (*c == '\"') {
10845 for (c++; c < end && isspace(*c); c++)
10846 ;
10847 }
10848
10849 if (c >= end)
10850 return TRUE;
10851
10852 /* Is there an info field? */
10853 if (*c == '\"') {
10854 int i;
10855 c++;
10856 for (i = 0;
10857 i < sizeof (info) - 1 && *c != '\"' && c < end;
10858 i++, c++)
10859 info[i] = *c;
10860 info[i] = '\0';
10861 debug_printf("process_browse_data: info: |%s|\n", info); /* !! */
10862 }
10863 }
10864 if (c >= end)
10865 return TRUE;
10866
10867 if (!(type & CUPS_PRINTER_DELETE))
10868 found_cups_printer (remote_host, uri, location, info);
10869
10870 if (in_shutdown == 0)
10871 recheck_timer ();
10872
10873 /* Don't remove this I/O source */
10874 return TRUE;
10875 }
10876
10877 static void
broadcast_browse_packets(gpointer data,gpointer user_data)10878 broadcast_browse_packets (gpointer data, gpointer user_data)
10879 {
10880 browse_data_t *bdata = data;
10881 netif_t *browse;
10882 char packet[2048];
10883 char uri[HTTP_MAX_URI];
10884 char scheme[32];
10885 char username[64];
10886 char host[HTTP_MAX_HOST];
10887 int port;
10888 char resource[HTTP_MAX_URI];
10889
10890 debug_printf("broadcast_browse_packets() in THREAD %ld\n", pthread_self());
10891
10892 for (browse = (netif_t *)cupsArrayFirst (netifs);
10893 browse != NULL;
10894 browse = (netif_t *)cupsArrayNext (netifs)) {
10895 /* Replace 'localhost' with our IP address on this interface */
10896 httpSeparateURI(HTTP_URI_CODING_ALL, bdata->uri,
10897 scheme, sizeof(scheme),
10898 username, sizeof(username),
10899 host, sizeof(host),
10900 &port,
10901 resource, sizeof(resource));
10902 httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof (uri),
10903 scheme, username, browse->address, port, resource);
10904
10905 if (snprintf (packet, sizeof (packet),
10906 "%x " /* type */
10907 "%x " /* state */
10908 "%s " /* uri */
10909 "\"%s\" " /* location */
10910 "\"%s\" " /* info */
10911 "\"%s\" " /* make-and-model */
10912 "lease-duration=%d" /* BrowseTimeout */
10913 "%s%s" /* other browse options */
10914 "\n",
10915 bdata->type,
10916 bdata->state,
10917 uri,
10918 bdata->location,
10919 bdata->info,
10920 bdata->make_model,
10921 BrowseTimeout,
10922 bdata->browse_options ? " " : "",
10923 bdata->browse_options ? bdata->browse_options : "")
10924 >= sizeof (packet)) {
10925 debug_printf ("oversize packet not sent\n");
10926 continue;
10927 }
10928
10929 debug_printf("packet to send:\n%s", packet);
10930
10931 int err = sendto (browsesocket, packet,
10932 strlen (packet), 0,
10933 &browse->broadcast.addr,
10934 httpAddrLength (&browse->broadcast));
10935 if (err == -1)
10936 debug_printf("cupsd-browsed: sendto returned %d: %s\n",
10937 err, strerror (errno));
10938 }
10939 }
10940
10941 gboolean
send_browse_data(gpointer data)10942 send_browse_data (gpointer data)
10943 {
10944 debug_printf("send_browse_data() in THREAD %ld\n", pthread_self());
10945 update_netifs (NULL);
10946 res_init ();
10947 update_local_printers ();
10948 g_list_foreach (browse_data, broadcast_browse_packets, NULL);
10949 g_timeout_add_seconds (BrowseInterval, send_browse_data, NULL);
10950
10951 /* Stop this timeout handler, we called a new one */
10952 return FALSE;
10953 }
10954
10955 static browsepoll_printer_t *
new_browsepoll_printer(const char * uri_supported,const char * location,const char * info)10956 new_browsepoll_printer (const char *uri_supported,
10957 const char *location,
10958 const char *info)
10959 {
10960 browsepoll_printer_t *printer = g_malloc (sizeof (browsepoll_printer_t));
10961 printer->uri_supported = g_strdup (uri_supported);
10962 printer->location = g_strdup (location);
10963 printer->info = g_strdup (info);
10964 return printer;
10965 }
10966
10967 static void
browsepoll_printer_free(gpointer data)10968 browsepoll_printer_free (gpointer data)
10969 {
10970 browsepoll_printer_t *printer = data;
10971 debug_printf("browsepoll_printer_free() in THREAD %ld\n", pthread_self());
10972 free (printer->uri_supported);
10973 free (printer->location);
10974 free (printer->info);
10975 free (printer);
10976 }
10977
10978 static void
browse_poll_get_printers(browsepoll_t * context,http_t * conn)10979 browse_poll_get_printers (browsepoll_t *context, http_t *conn)
10980 {
10981 static const char * const rattrs[] = { "printer-uri-supported",
10982 "printer-location",
10983 "printer-info"};
10984 ipp_t *request, *response = NULL;
10985 ipp_attribute_t *attr;
10986 GList *printers = NULL;
10987
10988 debug_printf ("cups-browsed [BrowsePoll %s:%d]: CUPS-Get-Printers\n",
10989 context->server, context->port);
10990
10991 request = ippNewRequest(CUPS_GET_PRINTERS);
10992 if (context->major > 0) {
10993 debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
10994 context->server, context->port, context->major,
10995 context->minor);
10996 ippSetVersion (request, context->major, context->minor);
10997 }
10998
10999 ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
11000 "requested-attributes", sizeof (rattrs) / sizeof (rattrs[0]),
11001 NULL,
11002 rattrs);
11003
11004 /* Ask the server to exclude printers that are remote or not shared,
11005 or implicit classes. */
11006 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
11007 "printer-type-mask",
11008 CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT |
11009 CUPS_PRINTER_NOT_SHARED);
11010 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
11011 "printer-type", 0);
11012
11013 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
11014 "requesting-user-name", NULL, cupsUser ());
11015
11016 response = cupsDoRequest(conn, request, "/");
11017 if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE) {
11018 debug_printf("cups-browsed [BrowsePoll %s:%d]: failed: %s\n",
11019 context->server, context->port, cupsLastErrorString ());
11020 goto fail;
11021 }
11022
11023 for (attr = ippFirstAttribute(response); attr;
11024 attr = ippNextAttribute(response)) {
11025 browsepoll_printer_t *printer;
11026 const char *uri, *location, *info;
11027
11028 while (attr && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
11029 attr = ippNextAttribute(response);
11030
11031 if (!attr)
11032 break;
11033
11034 uri = NULL;
11035 info = NULL;
11036 location = NULL;
11037 while (attr && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
11038 if (!strcasecmp (ippGetName(attr), "printer-uri-supported") &&
11039 ippGetValueTag(attr) == IPP_TAG_URI)
11040 uri = ippGetString(attr, 0, NULL);
11041 else if (!strcasecmp (ippGetName(attr), "printer-location") &&
11042 ippGetValueTag(attr) == IPP_TAG_TEXT)
11043 location = ippGetString(attr, 0, NULL);
11044 else if (!strcasecmp (ippGetName(attr), "printer-info") &&
11045 ippGetValueTag(attr) == IPP_TAG_TEXT)
11046 info = ippGetString(attr, 0, NULL);
11047 attr = ippNextAttribute(response);
11048 }
11049
11050 if (uri) {
11051 found_cups_printer (context->server, uri, location, info);
11052 printer = new_browsepoll_printer (uri, location, info);
11053 printers = g_list_insert (printers, printer, 0);
11054 }
11055
11056 if (!attr)
11057 break;
11058 }
11059
11060 g_list_free_full (context->printers, browsepoll_printer_free);
11061 context->printers = printers;
11062
11063 fail:
11064 if (response)
11065 ippDelete(response);
11066 }
11067
11068 static void
browse_poll_create_subscription(browsepoll_t * context,http_t * conn)11069 browse_poll_create_subscription (browsepoll_t *context, http_t *conn)
11070 {
11071 static const char * const events[] = { "printer-added",
11072 "printer-changed",
11073 "printer-config-changed",
11074 "printer-modified",
11075 "printer-deleted",
11076 "printer-state-changed" };
11077 ipp_t *request, *response = NULL;
11078 ipp_attribute_t *attr;
11079
11080 debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Create-Subscription\n",
11081 context->server, context->port);
11082
11083 request = ippNewRequest(IPP_CREATE_PRINTER_SUBSCRIPTION);
11084 if (context->major > 0) {
11085 debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
11086 context->server, context->port, context->major,
11087 context->minor);
11088 ippSetVersion (request, context->major, context->minor);
11089 }
11090
11091 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
11092 "printer-uri", NULL, "/");
11093 ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
11094 "notify-pull-method", NULL, "ippget");
11095 ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_CHARSET,
11096 "notify-charset", NULL, "utf-8");
11097 ippAddString (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
11098 "requesting-user-name", NULL, cupsUser ());
11099 ippAddStrings (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
11100 "notify-events", sizeof (events) / sizeof (events[0]),
11101 NULL, events);
11102 ippAddInteger (request, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
11103 "notify-time-interval", BrowseInterval);
11104
11105 response = cupsDoRequest (conn, request, "/");
11106 if (!response ||
11107 ippGetStatusCode (response) > IPP_STATUS_OK_EVENTS_COMPLETE) {
11108 debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n",
11109 context->server, context->port, cupsLastErrorString ());
11110 context->subscription_id = -1;
11111 context->can_subscribe = FALSE;
11112 goto fail;
11113 }
11114
11115 for (attr = ippFirstAttribute(response); attr;
11116 attr = ippNextAttribute(response)) {
11117 if (ippGetGroupTag (attr) == IPP_TAG_SUBSCRIPTION) {
11118 if (ippGetValueTag (attr) == IPP_TAG_INTEGER &&
11119 !strcasecmp (ippGetName (attr), "notify-subscription-id")) {
11120 context->subscription_id = ippGetInteger (attr, 0);
11121 debug_printf("cups-browsed [BrowsePoll %s:%d]: subscription ID=%d\n",
11122 context->server, context->port, context->subscription_id);
11123 break;
11124 }
11125 }
11126 }
11127
11128 if (!attr) {
11129 debug_printf("cups-browsed [BrowsePoll %s:%d]: no ID returned\n",
11130 context->server, context->port);
11131 context->subscription_id = -1;
11132 context->can_subscribe = FALSE;
11133 }
11134
11135 fail:
11136 if (response)
11137 ippDelete(response);
11138 }
11139
11140 static void
browse_poll_cancel_subscription(browsepoll_t * context)11141 browse_poll_cancel_subscription (browsepoll_t *context)
11142 {
11143 ipp_t *request, *response = NULL;
11144 http_t *conn = httpConnectEncryptShortTimeout (context->server, context->port,
11145 HTTP_ENCRYPT_IF_REQUESTED);
11146
11147 if (conn == NULL) {
11148 debug_printf("cups-browsed [BrowsePoll %s:%d]: connection failure "
11149 "attempting to cancel\n", context->server, context->port);
11150 return;
11151 }
11152
11153 httpSetTimeout(conn, HttpRemoteTimeout, http_timeout_cb, NULL);
11154
11155 debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Cancel-Subscription\n",
11156 context->server, context->port);
11157
11158 request = ippNewRequest(IPP_CANCEL_SUBSCRIPTION);
11159 if (context->major > 0) {
11160 debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
11161 context->server, context->port, context->major,
11162 context->minor);
11163 ippSetVersion (request, context->major, context->minor);
11164 }
11165
11166 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
11167 "printer-uri", NULL, "/");
11168 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
11169 "requesting-user-name", NULL, cupsUser ());
11170 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
11171 "notify-subscription-id", context->subscription_id);
11172
11173 response = cupsDoRequest (conn, request, "/");
11174 if (!response ||
11175 ippGetStatusCode (response) > IPP_STATUS_OK_EVENTS_COMPLETE)
11176 debug_printf("cupsd-browsed [BrowsePoll %s:%d]: failed: %s\n",
11177 context->server, context->port, cupsLastErrorString ());
11178
11179 if (response)
11180 ippDelete(response);
11181 if (conn)
11182 httpClose (conn);
11183 }
11184
11185 static gboolean
browse_poll_get_notifications(browsepoll_t * context,http_t * conn)11186 browse_poll_get_notifications (browsepoll_t *context, http_t *conn)
11187 {
11188 ipp_t *request, *response = NULL;
11189 ipp_status_t status;
11190 gboolean get_printers = FALSE;
11191
11192 debug_printf ("cups-browsed [BrowsePoll %s:%d]: IPP-Get-Notifications\n",
11193 context->server, context->port);
11194
11195 request = ippNewRequest(IPP_GET_NOTIFICATIONS);
11196 if (context->major > 0) {
11197 debug_printf("cups-browsed [BrowsePoll %s:%d]: setting IPP version %d.%d\n",
11198 context->server, context->port, context->major,
11199 context->minor);
11200 ippSetVersion (request, context->major, context->minor);
11201 }
11202
11203 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
11204 "printer-uri", NULL, "/");
11205 ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME,
11206 "requesting-user-name", NULL, cupsUser ());
11207 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
11208 "notify-subscription-ids", context->subscription_id);
11209 ippAddInteger (request, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
11210 "notify-sequence-numbers", context->sequence_number + 1);
11211
11212 response = cupsDoRequest (conn, request, "/");
11213 if (!response)
11214 status = cupsLastError ();
11215 else
11216 status = ippGetStatusCode (response);
11217
11218 if (status == IPP_STATUS_ERROR_NOT_FOUND) {
11219 /* Subscription lease has expired. */
11220 debug_printf ("cups-browsed [BrowsePoll %s:%d]: Lease expired\n",
11221 context->server, context->port);
11222 browse_poll_create_subscription (context, conn);
11223 get_printers = TRUE;
11224 } else if (status > IPP_STATUS_OK_EVENTS_COMPLETE) {
11225 debug_printf("cups-browsed [BrowsePoll %s:%d]: failed: %s\n",
11226 context->server, context->port, cupsLastErrorString ());
11227 context->can_subscribe = FALSE;
11228 browse_poll_cancel_subscription (context);
11229 context->subscription_id = -1;
11230 context->sequence_number = 0;
11231 get_printers = TRUE;
11232 }
11233
11234 if (!get_printers) {
11235 ipp_attribute_t *attr;
11236 gboolean seen_event = FALSE;
11237 int last_seq = context->sequence_number;
11238 if (response == NULL)
11239 return FALSE;
11240 for (attr = ippFirstAttribute(response); attr;
11241 attr = ippNextAttribute(response))
11242 if (ippGetGroupTag (attr) == IPP_TAG_EVENT_NOTIFICATION) {
11243 /* There is a printer-* event here. */
11244 seen_event = TRUE;
11245
11246 if (!strcmp (ippGetName (attr), "notify-sequence-number") &&
11247 ippGetValueTag (attr) == IPP_TAG_INTEGER)
11248 last_seq = ippGetInteger (attr, 0);
11249 }
11250
11251 if (seen_event) {
11252 debug_printf("cups-browsed [BrowsePoll %s:%d]: printer-* event\n",
11253 context->server, context->port);
11254 context->sequence_number = last_seq;
11255 get_printers = TRUE;
11256 } else
11257 debug_printf("cups-browsed [BrowsePoll %s:%d]: no events\n",
11258 context->server, context->port);
11259 }
11260
11261 if (response)
11262 ippDelete (response);
11263
11264 return get_printers;
11265 }
11266
11267 static void
browsepoll_printer_keepalive(gpointer data,gpointer user_data)11268 browsepoll_printer_keepalive (gpointer data, gpointer user_data)
11269 {
11270 browsepoll_printer_t *printer = data;
11271 const char *server = user_data;
11272 debug_printf("browsepoll_printer_keepalive() in THREAD %ld\n",
11273 pthread_self());
11274 found_cups_printer (server, printer->uri_supported, printer->location,
11275 printer->info);
11276 }
11277
11278 gboolean
browse_poll(gpointer data)11279 browse_poll (gpointer data)
11280 {
11281 browsepoll_t *context = data;
11282 http_t *conn = NULL;
11283 gboolean get_printers = FALSE;
11284
11285 debug_printf("browse_poll() in THREAD %ld\n", pthread_self());
11286
11287 debug_printf ("browse polling %s:%d\n",
11288 context->server, context->port);
11289
11290 res_init ();
11291
11292 conn = httpConnectEncryptShortTimeout (context->server, context->port,
11293 HTTP_ENCRYPT_IF_REQUESTED);
11294 if (conn == NULL) {
11295 debug_printf("cups-browsed [BrowsePoll %s:%d]: failed to connect\n",
11296 context->server, context->port);
11297 goto fail;
11298 }
11299
11300 httpSetTimeout(conn, HttpRemoteTimeout, http_timeout_cb, NULL);
11301
11302 if (context->can_subscribe) {
11303 if (context->subscription_id == -1) {
11304 /* The first time this callback is run we need to create the IPP
11305 * subscription to watch to printer-* events. */
11306 browse_poll_create_subscription (context, conn);
11307 get_printers = TRUE;
11308 } else
11309 /* On subsequent runs, check for notifications using our
11310 * subscription. */
11311 get_printers = browse_poll_get_notifications (context, conn);
11312 }
11313 else
11314 get_printers = TRUE;
11315
11316 update_local_printers ();
11317 inhibit_local_printers_update = TRUE;
11318 if (get_printers)
11319 browse_poll_get_printers (context, conn);
11320 else
11321 g_list_foreach (context->printers, browsepoll_printer_keepalive,
11322 context->server);
11323
11324 inhibit_local_printers_update = FALSE;
11325
11326 if (in_shutdown == 0)
11327 recheck_timer ();
11328
11329 fail:
11330
11331 if (conn)
11332 httpClose (conn);
11333
11334 /* Call a new timeout handler so that we run again */
11335 g_timeout_add_seconds (BrowseInterval, browse_poll, data);
11336
11337 /* Stop this timeout handler, we called a new one */
11338 return FALSE;
11339 }
11340
11341 #ifdef HAVE_LDAP
11342 gboolean
browse_ldap_poll(gpointer data)11343 browse_ldap_poll (gpointer data)
11344 {
11345 char *tmpFilter; /* Query filter */
11346 int filterLen;
11347
11348 debug_printf("browse_ldap_poll() in THREAD %ld\n", pthread_self());
11349
11350 /* do real stuff here */
11351 if (!BrowseLDAPDN) {
11352 debug_printf("Need to set BrowseLDAPDN to use LDAP browsing!\n");
11353 BrowseLocalProtocols &= ~BROWSE_LDAP;
11354 BrowseRemoteProtocols &= ~BROWSE_LDAP;
11355
11356 return FALSE;
11357 } else {
11358 if (!BrowseLDAPInitialised) {
11359 BrowseLDAPInitialised = TRUE;
11360 /*
11361 * Query filter string
11362 */
11363 if (BrowseLDAPFilter)
11364 filterLen = snprintf(NULL, 0, "(&%s%s)", LDAP_BROWSE_FILTER,
11365 BrowseLDAPFilter);
11366 else
11367 filterLen = strlen(LDAP_BROWSE_FILTER);
11368
11369 tmpFilter = (char *)malloc(filterLen + 1);
11370 if (!tmpFilter) {
11371 debug_printf("Could not allocate memory for LDAP browse query filter!\n");
11372 BrowseLocalProtocols &= ~BROWSE_LDAP;
11373 BrowseRemoteProtocols &= ~BROWSE_LDAP;
11374 return FALSE;
11375 }
11376
11377 if (BrowseLDAPFilter) {
11378 snprintf(tmpFilter, filterLen + 1, "(&%s%s)", LDAP_BROWSE_FILTER,
11379 BrowseLDAPFilter);
11380 free(BrowseLDAPFilter);
11381 BrowseLDAPFilter = NULL;
11382 } else
11383 strcpy(tmpFilter, LDAP_BROWSE_FILTER);
11384
11385 BrowseLDAPFilter = tmpFilter;
11386
11387 /*
11388 * Open LDAP handle...
11389 */
11390
11391 BrowseLDAPHandle = ldap_new_connection();
11392 }
11393
11394 cupsdUpdateLDAPBrowse();
11395 if (in_shutdown == 0)
11396 recheck_timer();
11397 }
11398
11399 /* Call a new timeout handler so that we run again */
11400 g_timeout_add_seconds (BrowseInterval, browse_ldap_poll, data);
11401
11402 /* Stop this timeout handler, we called a new one */
11403 return FALSE;
11404 }
11405 #endif /* HAVE_LDAP */
11406
11407 static void
sigterm_handler(int sig)11408 sigterm_handler(int sig) {
11409 (void)sig; /* remove compiler warnings... */
11410
11411 if (terminating) {
11412 debug_printf("Caught signal %d while already terminating.\n", sig);
11413 return;
11414 }
11415 terminating = 1; /* ignore any further callbacks and break loops */
11416 /* Flag that we should stop and return... */
11417 g_main_loop_quit(gmainloop);
11418 g_main_context_wakeup(NULL);
11419 debug_printf("Caught signal %d, shutting down ...\n", sig);
11420 }
11421
11422 static void
sigusr1_handler(int sig)11423 sigusr1_handler(int sig) {
11424 (void)sig; /* remove compiler warnings... */
11425
11426 /* Turn off auto shutdown mode... */
11427 autoshutdown = 0;
11428 debug_printf("Caught signal %d, switching to permanent mode ...\n", sig);
11429 /* If there is still an active auto shutdown timer, kill it */
11430 if (autoshutdown_exec_id) {
11431 debug_printf ("We have left auto shutdown mode, killing auto shutdown timer.\n");
11432 g_source_remove(autoshutdown_exec_id);
11433 autoshutdown_exec_id = 0;
11434 }
11435 }
11436
11437 static void
sigusr2_handler(int sig)11438 sigusr2_handler(int sig) {
11439 (void)sig; /* remove compiler warnings... */
11440
11441 /* Turn on auto shutdown mode... */
11442 autoshutdown = 1;
11443 debug_printf("Caught signal %d, switching to auto shutdown mode ...\n", sig);
11444 /* If there are no printers or no jobs schedule the shutdown in
11445 autoshutdown_timeout seconds */
11446 if (!autoshutdown_exec_id &&
11447 (cupsArrayCount(remote_printers) == 0 ||
11448 (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
11449 debug_printf ("We entered auto shutdown mode and no printers are there to make available or no jobs on them, shutting down in %d sec...\n", autoshutdown_timeout);
11450 autoshutdown_exec_id =
11451 g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
11452 NULL);
11453 }
11454 }
11455
11456 static int
read_browseallow_value(const char * value,allow_sense_t sense)11457 read_browseallow_value (const char *value, allow_sense_t sense)
11458 {
11459 char *p;
11460 struct in_addr addr;
11461 allow_t *allow;
11462
11463 if (value && !strcasecmp (value, "all")) {
11464 if (sense == ALLOW_ALLOW) {
11465 browseallow_all = TRUE;
11466 return 0;
11467 } else if (sense == ALLOW_DENY) {
11468 browsedeny_all = TRUE;
11469 return 0;
11470 } else
11471 return 1;
11472 }
11473
11474 allow = calloc (1, sizeof (allow_t));
11475 allow->sense = sense;
11476 if (value == NULL)
11477 goto fail;
11478 p = strchr (value, '/');
11479 if (p) {
11480 char *s = strdup (value);
11481 s[p - value] = '\0';
11482
11483 if (!inet_aton (s, &addr)) {
11484 free (s);
11485 goto fail;
11486 }
11487
11488 free (s);
11489 allow->type = ALLOW_NET;
11490 allow->addr.ipv4.sin_addr.s_addr = addr.s_addr;
11491
11492 p++;
11493 if (strchr (p, '.')) {
11494 if (inet_aton (p, &addr))
11495 allow->mask.ipv4.sin_addr.s_addr = addr.s_addr;
11496 else
11497 goto fail;
11498 } else {
11499 char *endptr;
11500 unsigned long bits = strtoul (p, &endptr, 10);
11501 if (p == endptr)
11502 goto fail;
11503
11504 if (bits > 32)
11505 goto fail;
11506
11507 allow->mask.ipv4.sin_addr.s_addr = htonl (((0xffffffff << (32 - bits)) &
11508 0xffffffff));
11509 }
11510 } else if (inet_aton (value, &addr)) {
11511 allow->type = ALLOW_IP;
11512 allow->addr.ipv4.sin_addr.s_addr = addr.s_addr;
11513 } else
11514 goto fail;
11515
11516 cupsArrayAdd (browseallow, allow);
11517 return 0;
11518
11519 fail:
11520 allow->type = ALLOW_INVALID;
11521 cupsArrayAdd (browseallow, allow);
11522 return 1;
11523 }
11524
11525 void
read_configuration(const char * filename)11526 read_configuration (const char *filename)
11527 {
11528 cups_file_t *fp;
11529 int i, linenum = 0;
11530 char line[HTTP_MAX_BUFFER];
11531 char *value = NULL, *ptr, *ptr2, *start;
11532 const char *delim = " \t,";
11533 int browse_allow_line_found = 0;
11534 int browse_deny_line_found = 0;
11535 int browse_order_line_found = 0;
11536 int browse_line_found = 0;
11537 browse_filter_t *filter = NULL;
11538 int browse_filter_options, exact_match, err;
11539 char errbuf[1024];
11540 cluster_t *cluster = NULL;
11541
11542 if (!filename)
11543 filename = CUPS_SERVERROOT "/cups-browsed.conf";
11544
11545 if ((fp = cupsFileOpen(filename, "r")) == NULL) {
11546 debug_printf("unable to open configuration file; "
11547 "using defaults\n");
11548 return;
11549 }
11550
11551 i = 0;
11552 linenum = -1;
11553 /* First, we read the option settings supplied on the command line via
11554 "-o ..." in the order given on the command line, then we read the lines
11555 of the configuration file. This means that if there are contradicting
11556 settings on the command line and in the configuration file, the setting
11557 in the configuration file is used. */
11558 while ((i < cupsArrayCount(command_line_config) &&
11559 (value = cupsArrayIndex(command_line_config, i++)) &&
11560 strncpy(line, value, sizeof(line) - 1) &&
11561 ((strlen(value) > HTTP_MAX_BUFFER-1) ?
11562 line[HTTP_MAX_BUFFER-1] = '\0': 1)) ||
11563 cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) {
11564 if (linenum < 0) {
11565 /* We are still reading options from the command line ("-o ..."),
11566 separate key (line) and value (value) */
11567 value = line;
11568 while (*value && !isspace(*value) && !(*value == '='))
11569 value ++;
11570 if (*value) {
11571 *value = '\0';
11572 value ++;
11573 while (*value && (isspace(*value) || (*value == '=')))
11574 value ++;
11575 }
11576 }
11577
11578 debug_printf("Reading config%s: %s %s\n",
11579 (linenum < 0 ? " (from command line)" : ""), line, value);
11580 if (!strcasecmp(line, "DebugLogging") && value) {
11581 char *p, *saveptr;
11582 p = strtok_r (value, delim, &saveptr);
11583 while (p) {
11584 if (!strcasecmp(p, "file")) {
11585 if (debug_logfile == 0) {
11586 debug_logfile = 1;
11587 start_debug_logging();
11588 }
11589 } else if (!strcasecmp(p, "stderr"))
11590 debug_stderr = 1;
11591 else if (strcasecmp(p, "none"))
11592 debug_printf("Unknown debug logging mode '%s'\n", p);
11593
11594 p = strtok_r (NULL, delim, &saveptr);
11595 }
11596 } else if (!strcasecmp(line, "CacheDir") && value) {
11597 if (value[0] != '\0')
11598 strncpy(cachedir, value, sizeof(cachedir) - 1);
11599 } else if (!strcasecmp(line, "LogDir") && value) {
11600 if (value[0] != '\0')
11601 strncpy(logdir, value, sizeof(logdir) - 1);
11602 } else if ((!strcasecmp(line, "BrowseProtocols") ||
11603 !strcasecmp(line, "BrowseLocalProtocols") ||
11604 !strcasecmp(line, "BrowseRemoteProtocols")) && value) {
11605 int protocols = 0;
11606 char *p, *saveptr;
11607 p = strtok_r (value, delim, &saveptr);
11608 while (p) {
11609 if (!strcasecmp(p, "dnssd"))
11610 protocols |= BROWSE_DNSSD;
11611 else if (!strcasecmp(p, "cups"))
11612 protocols |= BROWSE_CUPS;
11613 else if (!strcasecmp(p, "ldap"))
11614 protocols |= BROWSE_LDAP;
11615 else if (strcasecmp(p, "none"))
11616 debug_printf("Unknown protocol '%s'\n", p);
11617
11618 p = strtok_r (NULL, delim, &saveptr);
11619 }
11620
11621 if (!strcasecmp(line, "BrowseLocalProtocols"))
11622 BrowseLocalProtocols = protocols;
11623 else if (!strcasecmp(line, "BrowseRemoteProtocols"))
11624 BrowseRemoteProtocols = protocols;
11625 else
11626 BrowseLocalProtocols = BrowseRemoteProtocols = protocols;
11627 } else if (!strcasecmp(line, "BrowsePoll") && value) {
11628 browsepoll_t **old = BrowsePoll;
11629 BrowsePoll = realloc (BrowsePoll,
11630 (NumBrowsePoll + 1) *
11631 sizeof (browsepoll_t *));
11632 if (!BrowsePoll) {
11633 debug_printf("unable to realloc: ignoring BrowsePoll line\n");
11634 BrowsePoll = old;
11635 } else {
11636 char *colon, *slash;
11637 browsepoll_t *b = g_malloc0 (sizeof (browsepoll_t));
11638 debug_printf("Adding BrowsePoll server: %s\n", value);
11639 b->server = strdup (value);
11640 b->port = BrowsePort;
11641 b->can_subscribe = TRUE; /* first assume subscriptions work */
11642 b->subscription_id = -1;
11643 slash = strchr (b->server, '/');
11644 if (slash) {
11645 *slash++ = '\0';
11646 if (!strcasecmp (slash, "version=1.0")) {
11647 b->major = 1;
11648 b->minor = 0;
11649 } else if (!strcasecmp (slash, "version=1.1")) {
11650 b->major = 1;
11651 b->minor = 1;
11652 } else if (!strcasecmp (slash, "version=2.0")) {
11653 b->major = 2;
11654 b->minor = 0;
11655 } else if (!strcasecmp (slash, "version=2.1")) {
11656 b->major = 2;
11657 b->minor = 1;
11658 } else if (!strcasecmp (slash, "version=2.2")) {
11659 b->major = 2;
11660 b->minor = 2;
11661 } else {
11662 debug_printf ("ignoring unknown server option: %s\n", slash);
11663 }
11664 } else
11665 b->major = 0;
11666
11667 colon = strchr (b->server, ':');
11668 if (colon) {
11669 char *endptr;
11670 unsigned long n;
11671 *colon++ = '\0';
11672 n = strtoul (colon, &endptr, 10);
11673 if (endptr != colon && n < INT_MAX)
11674 b->port = (int) n;
11675 }
11676
11677 BrowsePoll[NumBrowsePoll++] = b;
11678 }
11679 } else if (!strcasecmp(line, "BrowseAllow")) {
11680 if (read_browseallow_value (value, ALLOW_ALLOW))
11681 debug_printf ("BrowseAllow value \"%s\" not understood\n",
11682 value);
11683 else {
11684 browse_allow_line_found = 1;
11685 browse_line_found = 1;
11686 }
11687 } else if (!strcasecmp(line, "BrowseDeny")) {
11688 if (read_browseallow_value (value, ALLOW_DENY))
11689 debug_printf ("BrowseDeny value \"%s\" not understood\n",
11690 value);
11691 else {
11692 browse_deny_line_found = 1;
11693 browse_line_found = 1;
11694 }
11695 } else if (!strcasecmp(line, "BrowseOrder") && value) {
11696 if (!strncasecmp(value, "Allow", 5) &&
11697 strcasestr(value, "Deny")) { /* Allow,Deny */
11698 browse_order = ORDER_ALLOW_DENY;
11699 browse_order_line_found = 1;
11700 browse_line_found = 1;
11701 } else if (!strncasecmp(value, "Deny", 4) &&
11702 strcasestr(value, "Allow")) { /* Deny,Allow */
11703 browse_order = ORDER_DENY_ALLOW;
11704 browse_order_line_found = 1;
11705 browse_line_found = 1;
11706 } else
11707 debug_printf ("BrowseOrder value \"%s\" not understood\n",
11708 value);
11709 } else if (!strcasecmp(line, "BrowseFilter") && value) {
11710 ptr = value;
11711 /* Skip white space */
11712 while (*ptr && isspace(*ptr)) ptr ++;
11713 /* Premature line end */
11714 if (!*ptr) goto browse_filter_fail;
11715 filter = calloc (1, sizeof (browse_filter_t));
11716 if (!filter) goto browse_filter_fail;
11717 browse_filter_options = 1;
11718 filter->sense = FILTER_MATCH;
11719 exact_match = 0;
11720 while (browse_filter_options) {
11721 if (!strncasecmp(ptr, "NOT", 3) && *(ptr + 3) &&
11722 isspace(*(ptr + 3))) {
11723 /* Accept remote printers where regexp does NOT match or where
11724 the boolean field is false */
11725 filter->sense = FILTER_NOT_MATCH;
11726 ptr += 4;
11727 /* Skip white space until next word */
11728 while (*ptr && isspace(*ptr)) ptr ++;
11729 /* Premature line end without field name */
11730 if (!*ptr) goto browse_filter_fail;
11731 } else if (!strncasecmp(ptr, "EXACT", 5) && *(ptr + 5) &&
11732 isspace(*(ptr + 5))) {
11733 /* Consider the rest of the line after the field name a string which
11734 has to match the field exactly */
11735 exact_match = 1;
11736 ptr += 6;
11737 /* Skip white space until next word */
11738 while (*ptr && isspace(*ptr)) ptr ++;
11739 /* Premature line end without field name */
11740 if (!*ptr) goto browse_filter_fail;
11741 } else
11742 /* No more options, consider next word the name of the field which
11743 should match the regexp */
11744 browse_filter_options = 0;
11745 }
11746 start = ptr;
11747 while (*ptr && !isspace(*ptr)) ptr ++;
11748 if (*ptr) {
11749 /* Mark end of the field name */
11750 *ptr = '\0';
11751 /* Skip white space until regexp or line end */
11752 ptr ++;
11753 while (*ptr && isspace(*ptr)) ptr ++;
11754 }
11755 filter->field = strdup(start);
11756 if (!*ptr) {
11757 /* Only field name and no regexp is given, so this rule is
11758 about matching a boolean value */
11759 filter->regexp = NULL;
11760 filter->cregexp = NULL;
11761 } else {
11762 /* The rest of the line is the regexp, store and compile it */
11763 filter->regexp = strdup(ptr);
11764 if (!exact_match) {
11765 /* Compile the regexp only if the line does not require an exact
11766 match (using the EXACT option */
11767 filter->cregexp = calloc(1, sizeof (regex_t));
11768 if ((err = regcomp(filter->cregexp, filter->regexp,
11769 REG_EXTENDED | REG_ICASE)) != 0) {
11770 regerror(err, filter->cregexp, errbuf, sizeof(errbuf));
11771 debug_printf ("BrowseFilter line with error in regular expression \"%s\": %s\n",
11772 filter->regexp, errbuf);
11773 goto browse_filter_fail;
11774 }
11775 } else
11776 filter->cregexp = NULL;
11777 }
11778 cupsArrayAdd (browsefilter, filter);
11779 continue;
11780 browse_filter_fail:
11781 if (filter) {
11782 if (filter->field)
11783 free(filter->field);
11784 if (filter->regexp)
11785 free(filter->regexp);
11786 if (filter->cregexp)
11787 regfree(filter->cregexp);
11788 free(filter);
11789 filter = NULL;
11790 }
11791 } else if ((!strcasecmp(line, "BrowseInterval") || !strcasecmp(line, "BrowseTimeout")) && value) {
11792 int t = atoi(value);
11793 if (t >= 0) {
11794 if (!strcasecmp(line, "BrowseInterval"))
11795 BrowseInterval = t;
11796 else if (!strcasecmp(line, "BrowseTimeout"))
11797 BrowseTimeout = t;
11798
11799 debug_printf("Set %s to %d sec.\n",
11800 line, t);
11801 } else
11802 debug_printf("Invalid %s value: %d\n",
11803 line, t);
11804 } else if (!strcasecmp(line, "DomainSocket") && value) {
11805 if (value[0] != '\0') {
11806 if (DomainSocket != NULL)
11807 free(DomainSocket);
11808 DomainSocket = strdup(value);
11809 }
11810 } else if ((!strcasecmp(line, "HttpLocalTimeout") || !strcasecmp(line, "HttpRemoteTimeout")) && value) {
11811 int t = atoi(value);
11812 if (t >= 0) {
11813 if (!strcasecmp(line, "HttpLocalTimeout"))
11814 HttpLocalTimeout = t;
11815 else if (!strcasecmp(line, "HttpRemoteTimeout"))
11816 HttpRemoteTimeout = t;
11817
11818 debug_printf("Set %s to %d sec.\n",
11819 line, t);
11820 } else
11821 debug_printf("Invalid %s value: %d\n",
11822 line, t);
11823 } else if (!strcasecmp(line, "NotifLeaseDuration") && value) {
11824 int t = atoi(value);
11825 if (t >= 300) {
11826 notify_lease_duration = t;
11827 debug_printf("Set %s to %d sec.\n",
11828 line, t);
11829 } else
11830 debug_printf("Invalid %s value: %d\n",
11831 line, t);
11832 } else if (!strcasecmp(line, "HttpMaxRetries") && value) {
11833 int t = atoi(value);
11834 if (t > 0) {
11835 HttpMaxRetries = t;
11836
11837 debug_printf("Set %s to %d retries.\n",
11838 line, t);
11839 } else
11840 debug_printf("Invalid %s value: %d\n",
11841 line, t);
11842 } else if (!strcasecmp(line, "DNSSDBasedDeviceURIs") && value) {
11843 if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11844 !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11845 DNSSDBasedDeviceURIs = 1;
11846 else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11847 !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11848 DNSSDBasedDeviceURIs = 0;
11849 } else if (!strcasecmp(line, "IPBasedDeviceURIs") && value) {
11850 if (!strcasecmp(value, "IPv4") || !strcasecmp(value, "IPv4Only"))
11851 IPBasedDeviceURIs = IP_BASED_URIS_IPV4_ONLY;
11852 else if (!strcasecmp(value, "IPv6") || !strcasecmp(value, "IPv6Only"))
11853 IPBasedDeviceURIs = IP_BASED_URIS_IPV6_ONLY;
11854 else if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11855 !strcasecmp(value, "on") || !strcasecmp(value, "1") ||
11856 !strcasecmp(value, "IP") || !strcasecmp(value, "IPAddress"))
11857 IPBasedDeviceURIs = IP_BASED_URIS_ANY;
11858 else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11859 !strcasecmp(value, "off") || !strcasecmp(value, "0") ||
11860 !strcasecmp(value, "Name") || !strcasecmp(value, "HostName"))
11861 IPBasedDeviceURIs = IP_BASED_URIS_NO;
11862 } else if (!strcasecmp(line, "LocalQueueNamingRemoteCUPS") && value) {
11863 if (strcasestr(value, "DNSSD") || strcasestr(value, "DNS-SD"))
11864 LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_DNSSD;
11865 else if (strcasestr(value, "Make") && strcasestr(value, "Model"))
11866 LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_MAKE_MODEL;
11867 else if (strcasestr(value, "Remote") || strcasestr(value, "Name"))
11868 LocalQueueNamingRemoteCUPS = LOCAL_QUEUE_NAMING_REMOTE_NAME;
11869 } else if (!strcasecmp(line, "LocalQueueNamingIPPPrinter") && value) {
11870 if (strcasestr(value, "DNSSD") || strcasestr(value, "DNS-SD"))
11871 LocalQueueNamingIPPPrinter = LOCAL_QUEUE_NAMING_DNSSD;
11872 else if (strcasestr(value, "Make") && strcasestr(value, "Model"))
11873 LocalQueueNamingIPPPrinter = LOCAL_QUEUE_NAMING_MAKE_MODEL;
11874 } else if (!strcasecmp(line, "OnlyUnsupportedByCUPS") && value) {
11875 if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11876 !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11877 OnlyUnsupportedByCUPS = 1;
11878 else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11879 !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11880 OnlyUnsupportedByCUPS = 0;
11881 } else if (!strcasecmp(line, "UseCUPSGeneratedPPDs") && value) {
11882 if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11883 !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11884 UseCUPSGeneratedPPDs = 1;
11885 else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11886 !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11887 UseCUPSGeneratedPPDs = 0;
11888 } else if (!strcasecmp(line, "CreateRemoteRawPrinterQueues") && value) {
11889 if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11890 !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11891 CreateRemoteRawPrinterQueues = 1;
11892 else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11893 !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11894 CreateRemoteRawPrinterQueues = 0;
11895 } else if (!strcasecmp(line, "CreateRemoteCUPSPrinterQueues") && value) {
11896 if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11897 !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11898 CreateRemoteCUPSPrinterQueues = 1;
11899 else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11900 !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11901 CreateRemoteCUPSPrinterQueues = 0;
11902 } else if (!strcasecmp(line, "CreateIPPPrinterQueues") && value) {
11903 if (!strcasecmp(value, "all") ||
11904 !strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11905 !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11906 CreateIPPPrinterQueues = IPP_PRINTERS_ALL;
11907 else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11908 !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11909 CreateIPPPrinterQueues = IPP_PRINTERS_NO;
11910 else if (strcasestr(value, "local") || strcasestr(value, "usb"))
11911 CreateIPPPrinterQueues = IPP_PRINTERS_LOCAL_ONLY;
11912 else if (strcasestr(value, "driver") && strcasestr(value, "less"))
11913 CreateIPPPrinterQueues = IPP_PRINTERS_DRIVERLESS;
11914 else if (strcasestr(value, "every") || strcasestr(value, "pwg"))
11915 CreateIPPPrinterQueues = IPP_PRINTERS_PWGRASTER;
11916 else if (strcasestr(value, "apple") || strcasestr(value, "air"))
11917 CreateIPPPrinterQueues = IPP_PRINTERS_APPLERASTER;
11918 else if (strcasestr(value, "pclm") || strcasestr(value, "pcl-m"))
11919 CreateIPPPrinterQueues = IPP_PRINTERS_PCLM;
11920 else if (strcasestr(value, "pdf"))
11921 CreateIPPPrinterQueues = IPP_PRINTERS_PDF;
11922 } else if (!strcasecmp(line, "IPPPrinterQueueType") && value) {
11923 if (!strncasecmp(value, "Auto", 4))
11924 IPPPrinterQueueType = PPD_YES;
11925 else if (!strncasecmp(value, "PPD", 3))
11926 IPPPrinterQueueType = PPD_YES;
11927 else if (!strncasecmp(value, "NoPPD", 5))
11928 IPPPrinterQueueType = PPD_NO;
11929 else if (!strncasecmp(value, "Interface", 9))
11930 IPPPrinterQueueType = PPD_NO;
11931 } else if (!strcasecmp(line, "NewIPPPrinterQueuesShared") && value) {
11932 if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11933 !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11934 NewIPPPrinterQueuesShared = 1;
11935 else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11936 !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11937 NewIPPPrinterQueuesShared = 0;
11938 } else if(!strcasecmp(line, "DebugLogFileSize") && value) {
11939 int val = atoi(value);
11940 if(val<=0){
11941 DebugLogFileSize=0;
11942 }
11943 else DebugLogFileSize=val;
11944 } else if (!strcasecmp(line, "AllowResharingRemoteCUPSPrinters") && value) {
11945 if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11946 !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11947 AllowResharingRemoteCUPSPrinters = 1;
11948 else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11949 !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11950 AllowResharingRemoteCUPSPrinters = 0;
11951 } else if (!strcasecmp(line, "NewBrowsePollQueuesShared") && value) {
11952 if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11953 !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11954 NewBrowsePollQueuesShared = 1;
11955 else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11956 !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11957 NewBrowsePollQueuesShared = 0;
11958 } else if (!strcasecmp(line, "KeepGeneratedQueuesOnShutdown") && value) {
11959 if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11960 !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11961 KeepGeneratedQueuesOnShutdown = 1;
11962 else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11963 !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11964 KeepGeneratedQueuesOnShutdown = 0;
11965 } else if (!strcasecmp(line, "AutoClustering") && value) {
11966 if (!strcasecmp(value, "yes") || !strcasecmp(value, "true") ||
11967 !strcasecmp(value, "on") || !strcasecmp(value, "1"))
11968 AutoClustering = 1;
11969 else if (!strcasecmp(value, "no") || !strcasecmp(value, "false") ||
11970 !strcasecmp(value, "off") || !strcasecmp(value, "0"))
11971 AutoClustering = 0;
11972 } else if (!strcasecmp(line, "Cluster") && value) {
11973 ptr = value;
11974 ptr2 = NULL;
11975 /* Skip white space */
11976 while (*ptr && isspace(*ptr)) ptr ++;
11977 /* Premature line end */
11978 if (!*ptr) goto cluster_fail;
11979 /* Find the local queue name for the cluster */
11980 start = ptr;
11981 while (*ptr && !isspace(*ptr) && *ptr != ':') ptr ++;
11982 if (*ptr) {
11983 /* Mark end of the local queue name */
11984 *ptr = '\0';
11985 /* Skip colon and white space until next word or line end */
11986 ptr ++;
11987 while (*ptr && (isspace(*ptr) || *ptr == ':')) ptr ++;
11988 }
11989 /* Empty queue name */
11990 if (strlen(start) <= 0)
11991 goto cluster_fail;
11992 /* Clean queue name */
11993 ptr2 = remove_bad_chars(start, 0);
11994 /* Check whether we have already a cluster with this name */
11995 for (cluster = cupsArrayFirst(clusters);
11996 cluster;
11997 cluster = cupsArrayNext(clusters))
11998 if (!strcasecmp(ptr2, cluster->local_queue_name)) {
11999 debug_printf("Duplicate cluster with queue name \"%s\".\n",
12000 ptr2);
12001 cluster = NULL;
12002 goto cluster_fail;
12003 }
12004 /* Create the new cluster definition */
12005 cluster = calloc (1, sizeof (cluster_t));
12006 if (!cluster) goto cluster_fail;
12007 cluster->local_queue_name = ptr2;
12008 cluster->members = cupsArrayNew(NULL, NULL);
12009 ptr2 = NULL;
12010 if (!*ptr) {
12011 /* Only local queue name given, so assume this name as the only
12012 member name (only remote queues with this name match) */
12013 cupsArrayAdd(cluster->members, remove_bad_chars(ptr2, 2));
12014 } else {
12015 /* The rest of the line lists one or more member queue names */
12016 while (*ptr) {
12017 start = ptr;
12018 while (*ptr && !isspace(*ptr)) ptr ++;
12019 if (*ptr) {
12020 /* Mark end of the current word */
12021 *ptr = '\0';
12022 /* Skip white space until next word or line end */
12023 ptr ++;
12024 while (*ptr && isspace(*ptr)) ptr ++;
12025 }
12026 /* Add member queue name to the list */
12027 if (strlen(start) > 0)
12028 cupsArrayAdd(cluster->members, remove_bad_chars(start, 2));
12029 }
12030 }
12031 cupsArrayAdd (clusters, cluster);
12032 if (ptr2 != NULL) {
12033 free(ptr2);
12034 ptr2 = NULL;
12035 }
12036 continue;
12037 cluster_fail:
12038 if (cluster) {
12039 if (cluster->local_queue_name)
12040 free(cluster->local_queue_name);
12041 if (cluster->members) {
12042 while ((ptr = cupsArrayFirst (cluster->members)) != NULL) {
12043 cupsArrayRemove (cluster->members, ptr);
12044 free (ptr);
12045 }
12046 cupsArrayDelete (cluster->members);
12047 }
12048 free(cluster);
12049 cluster = NULL;
12050 }
12051 if (ptr2 != NULL) {
12052 free(ptr2);
12053 ptr2 = NULL;
12054 }
12055 } else if (!strcasecmp(line, "LoadBalancing") && value) {
12056 if (!strncasecmp(value, "QueueOnClient", 13))
12057 LoadBalancingType = QUEUE_ON_CLIENT;
12058 else if (!strncasecmp(value, "QueueOnServers", 14))
12059 LoadBalancingType = QUEUE_ON_SERVERS;
12060 } else if (!strcasecmp(line, "DefaultOptions") && value) {
12061 if (DefaultOptions == NULL && strlen(value) > 0)
12062 DefaultOptions = strdup(value);
12063 } else if (!strcasecmp(line, "AutoShutdown") && value) {
12064 char *p, *saveptr;
12065 p = strtok_r (value, delim, &saveptr);
12066 while (p) {
12067 if (!strcasecmp(p, "On") || !strcasecmp(p, "Yes") ||
12068 !strcasecmp(p, "True") || !strcasecmp(p, "1")) {
12069 autoshutdown = 1;
12070 debug_printf("Turning on auto shutdown mode.\n");
12071 } else if (!strcasecmp(p, "Off") || !strcasecmp(p, "No") ||
12072 !strcasecmp(p, "False") || !strcasecmp(p, "0")) {
12073 autoshutdown = 0;
12074 debug_printf("Turning off auto shutdown mode (permanent mode).\n");
12075 } else if (!strcasecmp(p, "avahi")) {
12076 autoshutdown_avahi = 1;
12077 debug_printf("Turning on auto shutdown control by appearing and disappearing of the Avahi server.\n");
12078 } else if (strcasecmp(p, "none"))
12079 debug_printf("Unknown mode '%s'\n", p);
12080 p = strtok_r (NULL, delim, &saveptr);
12081 }
12082 } else if (!strcasecmp(line, "AutoShutdownTimeout") && value) {
12083 int t = atoi(value);
12084 if (t >= 0) {
12085 autoshutdown_timeout = t;
12086 debug_printf("Set auto shutdown timeout to %d sec.\n",
12087 t);
12088 } else
12089 debug_printf("Invalid auto shutdown timeout value: %d\n",
12090 t);
12091 } else if (!strcasecmp(line, "AutoShutdownOn") && value) {
12092 int success = 0;
12093 if (!strncasecmp(value, "no", 2)) {
12094 if (strcasestr(value + 2, "queue")) {
12095 autoshutdown_on = NO_QUEUES;
12096 success = 1;
12097 } else if (strcasestr(value + 2, "job")) {
12098 autoshutdown_on = NO_JOBS;
12099 success = 1;
12100 }
12101 }
12102 if (success)
12103 debug_printf("Set auto shutdown inactivity type to no %s.\n",
12104 autoshutdown_on == NO_QUEUES ? "queues" : "jobs");
12105 else
12106 debug_printf("Invalid auto shutdown inactivity type value: %s\n",
12107 value);
12108 } else if (!strcasecmp(line, "UpdateCUPSQueuesMaxPerCall") && value) {
12109 int n = atoi(value);
12110 if (n >= 0) {
12111 update_cups_queues_max_per_call = n;
12112 if (n > 0)
12113 debug_printf("Set maximum of CUPS queue updates per call of update_cups_queues() to %d.\n",
12114 n);
12115 else
12116 debug_printf("Do not limit the number of CUPS queue updates per call of update_cups_queues().\n");
12117 } else
12118 debug_printf("Invalid value for maximum number of CUPS queue updates per call of update_cups_queues(): %d\n",
12119 n);
12120 } else if (!strcasecmp(line, "PauseBetweenCUPSQueueUpdates") && value) {
12121 int t = atoi(value);
12122 if (t >= 0) {
12123 pause_between_cups_queue_updates = t;
12124 debug_printf("Set pause between calls of update_cups_queues() to %d sec.\n",
12125 t);
12126 } else
12127 debug_printf("Invalid value for pause between calls of update_cups_queues(): %d\n",
12128 t);
12129 }
12130 #ifdef HAVE_LDAP
12131 else if (!strcasecmp(line, "BrowseLDAPBindDN") && value) {
12132 if (value[0] != '\0')
12133 BrowseLDAPBindDN = strdup(value);
12134 }
12135 # ifdef HAVE_LDAP_SSL
12136 else if (!strcasecmp(line, "BrowseLDAPCACertFile") && value) {
12137 if (value[0] != '\0')
12138 BrowseLDAPCACertFile = strdup(value);
12139 }
12140 # endif /* HAVE_LDAP_SSL */
12141 else if (!strcasecmp(line, "BrowseLDAPDN") && value) {
12142 if (value[0] != '\0')
12143 BrowseLDAPDN = strdup(value);
12144 } else if (!strcasecmp(line, "BrowseLDAPPassword") && value) {
12145 if (value[0] != '\0')
12146 BrowseLDAPPassword = strdup(value);
12147 } else if (!strcasecmp(line, "BrowseLDAPServer") && value) {
12148 if (value[0] != '\0')
12149 BrowseLDAPServer = strdup(value);
12150 } else if (!strcasecmp(line, "BrowseLDAPFilter") && value) {
12151 if (value[0] != '\0')
12152 BrowseLDAPFilter = strdup(value);
12153 }
12154 #endif /* HAVE_LDAP */
12155 }
12156
12157 if (browse_line_found == 0) {
12158 /* No "Browse..." lines at all */
12159 browseallow_all = 1;
12160 browse_order = ORDER_DENY_ALLOW;
12161 debug_printf("No \"Browse...\" line at all, accept all servers (\"BrowseOrder Deny,Allow\").\n");
12162 } else if (browse_order_line_found == 0) {
12163 /* No "BrowseOrder" line */
12164 if (browse_allow_line_found == 0) {
12165 /* Only "BrowseDeny" lines */
12166 browse_order = ORDER_DENY_ALLOW;
12167 debug_printf("No \"BrowseOrder\" line and only \"BrowseDeny\" lines, accept all except what matches the \"BrowseDeny\" lines (\"BrowseOrder Deny,Allow\").\n");
12168 } else if (browse_deny_line_found == 0) {
12169 /* Only "BrowseAllow" lines */
12170 browse_order = ORDER_ALLOW_DENY;
12171 debug_printf("No \"BrowseOrder\" line and only \"BrowseAllow\" lines, deny all except what matches the \"BrowseAllow\" lines (\"BrowseOrder Allow,Deny\").\n");
12172 } else {
12173 /* Default for "BrowseOrder" */
12174 browse_order = ORDER_DENY_ALLOW;
12175 debug_printf("No \"BrowseOrder\" line, use \"BrowseOrder Deny,Allow\" as default.\n");
12176 }
12177 }
12178
12179 cupsFileClose(fp);
12180 }
12181
12182 static void
defer_update_netifs(void)12183 defer_update_netifs (void)
12184 {
12185 if (update_netifs_sourceid)
12186 g_source_remove (update_netifs_sourceid);
12187
12188 update_netifs_sourceid = g_timeout_add_seconds (10, update_netifs, NULL);
12189 }
12190
12191 static void
nm_properties_changed(GDBusProxy * proxy,GVariant * changed_properties,const gchar * const * invalidated_properties,gpointer user_data)12192 nm_properties_changed (GDBusProxy *proxy,
12193 GVariant *changed_properties,
12194 const gchar *const *invalidated_properties,
12195 gpointer user_data)
12196 {
12197 GVariantIter *iter;
12198 const gchar *key;
12199 GVariant *value;
12200 debug_printf("nm_properties_changed() in THREAD %ld\n", pthread_self());
12201 g_variant_get (changed_properties, "a{sv}", &iter);
12202 while (g_variant_iter_loop (iter, "{&sv}", &key, &value)) {
12203 if (!strcmp (key, "ActiveConnections")) {
12204 debug_printf ("NetworkManager ActiveConnections changed\n");
12205 defer_update_netifs ();
12206 break;
12207 }
12208 }
12209
12210 g_variant_iter_free (iter);
12211 }
12212
12213 static void
find_previous_queue(gpointer key,gpointer value,gpointer user_data)12214 find_previous_queue (gpointer key,
12215 gpointer value,
12216 gpointer user_data)
12217 {
12218 const char *name = key;
12219 const local_printer_t *printer = value;
12220 remote_printer_t *p;
12221 debug_printf("find_previous_queue() in THREAD %ld\n", pthread_self());
12222 if (printer->cups_browsed_controlled) {
12223 /* Queue found, add to our list */
12224 p = create_remote_printer_entry (name, "", "", "", "", "",
12225 0, "", "", "", "", "", 0, NULL, 0, 0, NULL,
12226 -1);
12227 if (p) {
12228 /* Mark as unconfirmed, if no Avahi report of this queue appears
12229 in a certain time frame, we will remove the queue */
12230 p->status = STATUS_UNCONFIRMED;
12231
12232 if (BrowseRemoteProtocols & BROWSE_CUPS)
12233 p->timeout = time(NULL) + BrowseInterval * 3 / 2;
12234 else
12235 p->timeout = time(NULL) + TIMEOUT_CONFIRM;
12236
12237 p->slave_of = NULL;
12238 debug_printf("Found CUPS queue %s (URI: %s) from previous session.\n",
12239 p->queue_name, p->uri);
12240 } else
12241 debug_printf("ERROR: Unable to create print queue entry for printer of previous session: %s (%s).\n",
12242 name, printer->device_uri);
12243 }
12244 }
12245
main(int argc,char * argv[])12246 int main(int argc, char*argv[]) {
12247 int ret = 1;
12248 http_t *http;
12249 int i;
12250 char *val;
12251 remote_printer_t *p;
12252 GDBusProxy *proxy = NULL;
12253 GError *error = NULL;
12254 int subscription_id = 0;
12255
12256 /* Initialise the command_line_config array */
12257 command_line_config = cupsArrayNew(NULL, NULL);
12258
12259 /* Initialise the browseallow array */
12260 browseallow = cupsArrayNew(NULL, NULL);
12261
12262 /* Initialise the browsefilter array */
12263 browsefilter = cupsArrayNew(NULL, NULL);
12264
12265 /* Initialise the clusters array */
12266 clusters = cupsArrayNew(NULL, NULL);
12267
12268 /* Read command line options */
12269 if (argc >= 2) {
12270 for (i = 1; i < argc; i++)
12271 if (!strcasecmp(argv[i], "--debug") || !strcasecmp(argv[i], "-d") ||
12272 !strncasecmp(argv[i], "-v", 2)) {
12273 /* Turn on debug output mode if requested */
12274 debug_stderr = 1;
12275 debug_printf("Reading command line option %s, turning on debug mode (Log on standard error).\n",
12276 argv[i]);
12277 } else if (!strcasecmp(argv[i], "--logfile") ||
12278 !strcasecmp(argv[i], "-l")) {
12279 /* Turn on debug log file mode if requested */
12280 if (debug_logfile == 0) {
12281 debug_logfile = 1;
12282 start_debug_logging();
12283 debug_printf("Reading command line option %s, turning on debug mode (Log into log file %s).\n",
12284 argv[i], debug_log_file);
12285 }
12286 } else if (!strncasecmp(argv[i], "-c", 2)) {
12287 /* Alternative configuration file */
12288 val = argv[i] + 2;
12289 if (strlen(val) == 0) {
12290 i ++;
12291 if (i < argc && *argv[i] != '-')
12292 val = argv[i];
12293 else
12294 val = NULL;
12295 }
12296 if (val) {
12297 alt_config_file = strdup(val);
12298 debug_printf("Reading command line option -c %s, using alternative configuration file.\n",
12299 alt_config_file);
12300 } else {
12301 fprintf(stderr,
12302 "Reading command line option -c, no alternative configuration file name supplied.\n\n");
12303 goto help;
12304 }
12305 } else if (!strncasecmp(argv[i], "-o", 2)) {
12306 /* Configuration option via command line */
12307 val = argv[i] + 2;
12308 if (strlen(val) == 0) {
12309 i ++;
12310 if (i < argc && *argv[i] != '-')
12311 val = argv[i];
12312 else
12313 val = NULL;
12314 }
12315 if (val) {
12316 cupsArrayAdd (command_line_config, strdup(val));
12317 debug_printf("Reading command line option -o %s, applying extra configuration option.\n",
12318 val);
12319 } else {
12320 fprintf(stderr,
12321 "Reading command line option -o, no extra configuration option supplied.\n\n");
12322 goto help;
12323 }
12324 } else if (!strncasecmp(argv[i], "--autoshutdown-timeout", 22)) {
12325 debug_printf("Reading command line: %s\n", argv[i]);
12326 if (argv[i][22] == '=' && argv[i][23])
12327 val = argv[i] + 23;
12328 else if (!argv[i][22] && i < argc -1) {
12329 i++;
12330 debug_printf("Reading command line: %s\n", argv[i]);
12331 val = argv[i];
12332 } else {
12333 fprintf(stderr, "Expected auto shutdown timeout setting after \"--autoshutdown-timeout\" option.\n\n");
12334 goto help;
12335 }
12336 int t = atoi(val);
12337 if (t >= 0) {
12338 autoshutdown_timeout = t;
12339 debug_printf("Set auto shutdown timeout to %d sec.\n",
12340 t);
12341 } else {
12342 fprintf(stderr, "Invalid auto shutdown timeout value: %d\n\n",
12343 t);
12344 goto help;
12345 }
12346 } else if (!strncasecmp(argv[i], "--autoshutdown-on", 17)) {
12347 debug_printf("Reading command line: %s\n", argv[i]);
12348 if (argv[i][17] == '=' && argv[i][18])
12349 val = argv[i] + 18;
12350 else if (!argv[i][17] && i < argc - 1) {
12351 i++;
12352 debug_printf("Reading command line: %s\n", argv[i]);
12353 val = argv[i];
12354 } else {
12355 fprintf(stderr, "Expected auto shutdown inactivity type (\"no-queues\" or \"no-jobs\") after \"--autoshutdown-on\" option.\n\n");
12356 goto help;
12357 }
12358 int success = 0;
12359 if (!strncasecmp(val, "no", 2)) {
12360 if (strcasestr(val + 2, "queue")) {
12361 autoshutdown_on = NO_QUEUES;
12362 success = 1;
12363 } else if (strcasestr(val + 2, "job")) {
12364 autoshutdown_on = NO_JOBS;
12365 success = 1;
12366 }
12367 }
12368 if (success)
12369 debug_printf("Set auto shutdown inactivity type to no %s.\n",
12370 autoshutdown_on == NO_QUEUES ? "queues" : "jobs");
12371 else
12372 debug_printf("Invalid auto shutdown inactivity type value: %s\n",
12373 val);
12374 } else if (!strncasecmp(argv[i], "--autoshutdown", 14)) {
12375 debug_printf("Reading command line: %s\n", argv[i]);
12376 if (argv[i][14] == '=' && argv[i][15])
12377 val = argv[i] + 15;
12378 else if (!argv[i][14] && i < argc -1) {
12379 i++;
12380 debug_printf("Reading command line: %s\n", argv[i]);
12381 val = argv[i];
12382 } else {
12383 fprintf(stderr, "Expected auto shutdown setting after \"--autoshutdown\" option.\n\n");
12384 goto help;
12385 }
12386 if (!strcasecmp(val, "On") || !strcasecmp(val, "Yes") ||
12387 !strcasecmp(val, "True") || !strcasecmp(val, "1")) {
12388 autoshutdown = 1;
12389 debug_printf("Turning on auto shutdown mode.\n");
12390 } else if (!strcasecmp(val, "Off") || !strcasecmp(val, "No") ||
12391 !strcasecmp(val, "False") || !strcasecmp(val, "0")) {
12392 autoshutdown = 0;
12393 debug_printf("Turning off auto shutdown mode (permanent mode).\n");
12394 } else if (!strcasecmp(val, "avahi")) {
12395 autoshutdown_avahi = 1;
12396 debug_printf("Turning on auto shutdown control by appearing and disappearing of the Avahi server.\n");
12397 } else if (strcasecmp(val, "none")) {
12398 fprintf(stderr, "Unknown mode '%s'\n\n", val);
12399 goto help;
12400 }
12401 } else if (!strcasecmp(argv[i], "--version") ||
12402 !strcasecmp(argv[i], "--help") || !strcasecmp(argv[i], "-h")) {
12403 /* Help!! */
12404 goto help;
12405 } else {
12406 /* Unknown option */
12407 fprintf(stderr,
12408 "Reading command line option %s, unknown command line option.\n\n",
12409 argv[i]);
12410 goto help;
12411 }
12412 }
12413
12414 debug_printf("cups-browsed of cups-filters version "VERSION" starting.\n");
12415
12416 /* Read in cups-browsed.conf */
12417 read_configuration (alt_config_file);
12418
12419 /* Set the paths of the auxiliary files */
12420 if (cachedir[0] == '\0')
12421 strncpy(cachedir, DEFAULT_CACHEDIR, sizeof(cachedir) - 1);
12422 if (logdir[0] == '\0')
12423 strncpy(logdir, DEFAULT_LOGDIR, sizeof(logdir) - 1);
12424 strncpy(local_default_printer_file, cachedir,
12425 sizeof(local_default_printer_file) - 1);
12426 strncpy(local_default_printer_file + strlen(cachedir),
12427 LOCAL_DEFAULT_PRINTER_FILE,
12428 sizeof(local_default_printer_file) - strlen(cachedir) - 1);
12429 strncpy(remote_default_printer_file, cachedir,
12430 sizeof(remote_default_printer_file) - 1);
12431 strncpy(remote_default_printer_file + strlen(cachedir),
12432 REMOTE_DEFAULT_PRINTER_FILE,
12433 sizeof(remote_default_printer_file) - strlen(cachedir) - 1);
12434 strncpy(save_options_file, cachedir,
12435 sizeof(save_options_file) - 1);
12436 strncpy(save_options_file + strlen(cachedir),
12437 SAVE_OPTIONS_FILE,
12438 sizeof(save_options_file) - strlen(cachedir) - 1);
12439 strncpy(debug_log_file, logdir,
12440 sizeof(debug_log_file) - 1);
12441 strncpy(debug_log_file + strlen(logdir),
12442 DEBUG_LOG_FILE,
12443 sizeof(debug_log_file) - strlen(logdir) - 1);
12444
12445 strncpy(debug_log_file_bckp, logdir,
12446 sizeof(debug_log_file_bckp) - 1);
12447 strncpy(debug_log_file_bckp + strlen(logdir),
12448 DEBUG_LOG_FILE_2,
12449 sizeof(debug_log_file_bckp) - strlen(logdir) - 1);
12450
12451 if (debug_logfile == 1)
12452 start_debug_logging();
12453
12454 debug_printf("main() in THREAD %ld\n", pthread_self());
12455
12456 /* If a port is selected via the IPP_PORT environment variable,
12457 set this first */
12458 if (getenv("IPP_PORT") != NULL) {
12459 snprintf(local_server_str, sizeof(local_server_str) - 1,
12460 "localhost:%s", getenv("IPP_PORT"));
12461 local_server_str[sizeof(local_server_str) - 1] = '\0';
12462 cupsSetServer(local_server_str);
12463 debug_printf("Set port on which CUPS is listening via env variable: IPP_PORT=%s\n",
12464 getenv("IPP_PORT"));
12465 }
12466
12467 /* Point to selected CUPS server or domain socket via the CUPS_SERVER
12468 environment variable or DomainSocket configuration file option.
12469 Default to localhost:631 (and not to CUPS default to override
12470 client.conf files as cups-browsed works only with a local CUPS
12471 daemon, not with remote ones. */
12472 local_server_str[0] = '\0';
12473 if (getenv("CUPS_SERVER") != NULL) {
12474 strncpy(local_server_str, getenv("CUPS_SERVER"),
12475 sizeof(local_server_str) - 1);
12476 local_server_str[sizeof(local_server_str) - 1] = '\0';
12477 cupsSetServer(local_server_str);
12478 debug_printf("Set host/port/domain socket which CUPS is listening via env variable: CUPS_SERVER=%s\n",
12479 getenv("CUPS_SERVER"));
12480 } else {
12481 if (DomainSocket != NULL) {
12482 debug_printf("Set host/port/domain socket on which CUPS is listening via cups-browsed directive DomainSocket: %s\n",
12483 DomainSocket);
12484 struct stat sockinfo; /* Domain socket information */
12485 if (strcasecmp(DomainSocket, "None") != 0 &&
12486 strcasecmp(DomainSocket, "Off") != 0 &&
12487 !stat(DomainSocket, &sockinfo) &&
12488 (sockinfo.st_mode & S_IROTH) != 0 &&
12489 (sockinfo.st_mode & S_IWOTH) != 0) {
12490 strncpy(local_server_str, DomainSocket,
12491 sizeof(local_server_str) - 1);
12492 local_server_str[sizeof(local_server_str) - 1] = '\0';
12493 cupsSetServer(local_server_str);
12494 } else
12495 debug_printf("DomainSocket %s not accessible: %s\n",
12496 DomainSocket, strerror(errno));
12497 }
12498 }
12499 if (local_server_str[0])
12500 setenv("CUPS_SERVER", local_server_str, 1);
12501
12502 if (BrowseLocalProtocols & BROWSE_DNSSD) {
12503 debug_printf("Local support for DNSSD not implemented\n");
12504 BrowseLocalProtocols &= ~BROWSE_DNSSD;
12505 }
12506
12507 if (BrowseLocalProtocols & BROWSE_LDAP) {
12508 debug_printf("Local support for LDAP not implemented\n");
12509 BrowseLocalProtocols &= ~BROWSE_LDAP;
12510 }
12511
12512 #ifndef HAVE_AVAHI
12513 if (BrowseRemoteProtocols & BROWSE_DNSSD) {
12514 debug_printf("Remote support for DNSSD not supported\n");
12515 BrowseRemoteProtocols &= ~BROWSE_DNSSD;
12516 }
12517 #endif /* HAVE_AVAHI */
12518
12519 #ifndef HAVE_LDAP
12520 if (BrowseRemoteProtocols & BROWSE_LDAP) {
12521 debug_printf("Remote support for LDAP not supported\n");
12522 BrowseRemoteProtocols &= ~BROWSE_LDAP;
12523 }
12524 #endif /* HAVE_LDAP */
12525
12526 /* Wait for CUPS daemon to start */
12527 while ((http = http_connect_local ()) == NULL)
12528 sleep(1);
12529
12530 /* Initialise the array of network interfaces */
12531 netifs = cupsArrayNew(NULL, NULL);
12532 local_hostnames = cupsArrayNew(NULL, NULL);
12533 update_netifs (NULL);
12534
12535 local_printers = g_hash_table_new_full (g_str_hash,
12536 g_str_equal,
12537 g_free,
12538 free_local_printer);
12539 cups_supported_remote_printers = g_hash_table_new_full (g_str_hash,
12540 g_str_equal,
12541 g_free,
12542 free_local_printer);
12543
12544 /* Read out the currently defined CUPS queues and find the ones which we
12545 have added in an earlier session */
12546 update_local_printers ();
12547 if ((val = get_cups_default_printer()) != NULL) {
12548 default_printer = strdup(val);
12549 free(val);
12550 }
12551 remote_printers = cupsArrayNew(NULL, NULL);
12552 g_hash_table_foreach (local_printers, find_previous_queue, NULL);
12553
12554 /* Redirect SIGINT and SIGTERM so that we do a proper shutdown, removing
12555 the CUPS queues which we have created
12556 Use SIGUSR1 and SIGUSR2 to turn off and turn on auto shutdown mode
12557 resp. */
12558 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
12559 sigset(SIGTERM, sigterm_handler);
12560 sigset(SIGINT, sigterm_handler);
12561 sigset(SIGUSR1, sigusr1_handler);
12562 sigset(SIGUSR2, sigusr2_handler);
12563 debug_printf("Using signal handler SIGSET\n");
12564 #elif defined(HAVE_SIGACTION)
12565 struct sigaction action; /* Actions for POSIX signals */
12566 memset(&action, 0, sizeof(action));
12567 sigemptyset(&action.sa_mask);
12568 sigaddset(&action.sa_mask, SIGTERM);
12569 action.sa_handler = sigterm_handler;
12570 sigaction(SIGTERM, &action, NULL);
12571 sigemptyset(&action.sa_mask);
12572 sigaddset(&action.sa_mask, SIGINT);
12573 action.sa_handler = sigterm_handler;
12574 sigaction(SIGINT, &action, NULL);
12575 sigemptyset(&action.sa_mask);
12576 sigaddset(&action.sa_mask, SIGUSR1);
12577 action.sa_handler = sigusr1_handler;
12578 sigaction(SIGUSR1, &action, NULL);
12579 sigemptyset(&action.sa_mask);
12580 sigaddset(&action.sa_mask, SIGUSR2);
12581 action.sa_handler = sigusr2_handler;
12582 sigaction(SIGUSR2, &action, NULL);
12583 debug_printf("Using signal handler SIGACTION\n");
12584 #else
12585 signal(SIGTERM, sigterm_handler);
12586 signal(SIGINT, sigterm_handler);
12587 signal(SIGUSR1, sigusr1_handler);
12588 signal(SIGUSR2, sigusr2_handler);
12589 debug_printf("Using signal handler SIGNAL\n");
12590 #endif /* HAVE_SIGSET */
12591
12592 #ifdef HAVE_AVAHI
12593 if (autoshutdown_avahi)
12594 autoshutdown = 1;
12595 avahi_init();
12596 #endif /* HAVE_AVAHI */
12597
12598 if (autoshutdown == 1) {
12599 /* If there are no printers or no jobs schedule the shutdown in
12600 autoshutdown_timeout seconds */
12601 if (!autoshutdown_exec_id &&
12602 (cupsArrayCount(remote_printers) == 0 ||
12603 (autoshutdown_on == NO_JOBS && check_jobs() == 0))) {
12604 debug_printf ("We set auto shutdown mode and no printers are there to make available or no jobs on them, shutting down in %d sec...\n", autoshutdown_timeout);
12605 autoshutdown_exec_id =
12606 g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute,
12607 NULL);
12608 }
12609 }
12610
12611 if (BrowseLocalProtocols & BROWSE_CUPS ||
12612 BrowseRemoteProtocols & BROWSE_CUPS) {
12613 /* Set up our CUPS Browsing socket */
12614 browsesocket = socket (AF_INET, SOCK_DGRAM, 0);
12615 if (browsesocket == -1) {
12616 debug_printf("failed to create CUPS Browsing socket: %s\n",
12617 strerror (errno));
12618 } else {
12619 struct sockaddr_in addr;
12620 memset (&addr, 0, sizeof (addr));
12621 addr.sin_addr.s_addr = htonl (INADDR_ANY);
12622 addr.sin_family = AF_INET;
12623 addr.sin_port = htons (BrowsePort);
12624 if (bind (browsesocket, (struct sockaddr *)&addr, sizeof (addr))) {
12625 debug_printf("failed to bind CUPS Browsing socket: %s\n",
12626 strerror (errno));
12627 close (browsesocket);
12628 browsesocket = -1;
12629 } else {
12630 int on = 1;
12631 if (setsockopt (browsesocket, SOL_SOCKET, SO_BROADCAST,
12632 &on, sizeof (on))) {
12633 debug_printf("failed to allow broadcast: %s\n",
12634 strerror (errno));
12635 BrowseLocalProtocols &= ~BROWSE_CUPS;
12636 }
12637 }
12638 }
12639
12640 if (browsesocket == -1) {
12641 BrowseLocalProtocols &= ~BROWSE_CUPS;
12642 BrowseRemoteProtocols &= ~BROWSE_CUPS;
12643 }
12644 }
12645
12646 if (BrowseLocalProtocols == 0 &&
12647 BrowseRemoteProtocols == 0 &&
12648 !BrowsePoll) {
12649 debug_printf("nothing left to do\n");
12650 ret = 0;
12651 goto fail;
12652 }
12653
12654 /* Override the default password callback so we don't end up
12655 * prompting for it. */
12656 cupsSetPasswordCB2 (password_callback, NULL);
12657
12658 /* Watch NetworkManager for network interface changes */
12659 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
12660 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
12661 NULL, /* GDBusInterfaceInfo */
12662 "org.freedesktop.NetworkManager",
12663 "/org/freedesktop/NetworkManager",
12664 "org.freedesktop.NetworkManager",
12665 NULL, /* GCancellable */
12666 NULL); /* GError */
12667
12668 if (proxy)
12669 g_signal_connect (proxy,
12670 "g-properties-changed",
12671 G_CALLBACK (nm_properties_changed),
12672 NULL);
12673
12674 /* Run the main loop */
12675 gmainloop = g_main_loop_new (NULL, FALSE);
12676 recheck_timer ();
12677
12678 if (BrowseRemoteProtocols & BROWSE_CUPS) {
12679 GIOChannel *browse_channel = g_io_channel_unix_new (browsesocket);
12680 g_io_channel_set_close_on_unref (browse_channel, FALSE);
12681 g_io_add_watch (browse_channel, G_IO_IN, process_browse_data, NULL);
12682 }
12683
12684 if (BrowseLocalProtocols & BROWSE_CUPS) {
12685 debug_printf ("will send browse data every %ds\n",
12686 BrowseInterval);
12687 g_idle_add (send_browse_data, NULL);
12688 }
12689
12690 #ifdef HAVE_LDAP
12691 if (BrowseRemoteProtocols & BROWSE_LDAP) {
12692 debug_printf ("will browse poll LDAP every %ds\n",
12693 BrowseInterval);
12694 g_idle_add (browse_ldap_poll, NULL);
12695 }
12696 #endif /* HAVE_LDAP */
12697
12698 if (BrowsePoll) {
12699 size_t index;
12700 for (index = 0;
12701 index < NumBrowsePoll;
12702 index++) {
12703 debug_printf ("will browse poll %s every %ds\n",
12704 BrowsePoll[index]->server, BrowseInterval);
12705 g_idle_add (browse_poll, BrowsePoll[index]);
12706 }
12707 }
12708
12709 /* Subscribe to CUPS' D-Bus notifications and create a proxy to receive
12710 the notifications */
12711 subscription_id = create_subscription ();
12712 g_timeout_add_seconds (notify_lease_duration / 2,
12713 renew_subscription_timeout,
12714 &subscription_id);
12715 cups_notifier = cups_notifier_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
12716 0,
12717 NULL,
12718 CUPS_DBUS_PATH,
12719 NULL,
12720 &error);
12721 if (error) {
12722 fprintf (stderr, "Error creating cups notify handler: %s", error->message);
12723 g_error_free (error);
12724 cups_notifier = NULL;
12725 }
12726 if (cups_notifier != NULL) {
12727 g_signal_connect (cups_notifier, "printer-state-changed",
12728 G_CALLBACK (on_printer_state_changed), NULL);
12729 g_signal_connect (cups_notifier, "job-state",
12730 G_CALLBACK (on_job_state), NULL);
12731 g_signal_connect (cups_notifier, "printer-deleted",
12732 G_CALLBACK (on_printer_deleted), NULL);
12733 g_signal_connect (cups_notifier, "printer-modified",
12734 G_CALLBACK (on_printer_modified), NULL);
12735 }
12736
12737 /* If auto shutdown is active and we do not find any printers initially,
12738 schedule the shutdown in autoshutdown_timeout seconds */
12739 if (autoshutdown && !autoshutdown_exec_id &&
12740 cupsArrayCount(remote_printers) == 0) {
12741 debug_printf ("No printers found to make available, shutting down in %d sec...\n",
12742 autoshutdown_timeout);
12743 autoshutdown_exec_id =
12744 g_timeout_add_seconds (autoshutdown_timeout, autoshutdown_execute, NULL);
12745 }
12746
12747 g_main_loop_run (gmainloop);
12748
12749 debug_printf("main loop exited\n");
12750 g_main_loop_unref (gmainloop);
12751 gmainloop = NULL;
12752 ret = 0;
12753
12754 fail:
12755
12756 /* Clean up things */
12757
12758 in_shutdown = 1;
12759
12760 if (proxy)
12761 g_object_unref (proxy);
12762
12763 /* Remove all queues which we have set up */
12764 if (KeepGeneratedQueuesOnShutdown == 0)
12765 for (p = (remote_printer_t *)cupsArrayFirst(remote_printers);
12766 p; p = (remote_printer_t *)cupsArrayNext(remote_printers)) {
12767 if (p->status != STATUS_TO_BE_RELEASED)
12768 p->status = STATUS_DISAPPEARED;
12769 p->timeout = time(NULL) + TIMEOUT_IMMEDIATELY;
12770 }
12771 update_cups_queues(NULL);
12772
12773 cancel_subscription (subscription_id);
12774 if (cups_notifier)
12775 g_object_unref (cups_notifier);
12776
12777 if (BrowsePoll) {
12778 size_t index;
12779 for (index = 0;
12780 index < NumBrowsePoll;
12781 index++) {
12782 if (BrowsePoll[index]->can_subscribe &&
12783 BrowsePoll[index]->subscription_id != -1)
12784 browse_poll_cancel_subscription (BrowsePoll[index]);
12785
12786 free (BrowsePoll[index]->server);
12787 g_list_free_full (BrowsePoll[index]->printers,
12788 browsepoll_printer_free);
12789 free (BrowsePoll[index]);
12790 }
12791
12792 free (BrowsePoll);
12793 }
12794
12795 if (local_printers_context) {
12796 browse_poll_cancel_subscription (local_printers_context);
12797 #ifdef HAVE_CUPS_2_0
12798 free(local_printers_context->server);
12799 #endif
12800 g_list_free_full (local_printers_context->printers,
12801 browsepoll_printer_free);
12802 free (local_printers_context);
12803 }
12804
12805 http_close_local ();
12806
12807 #ifdef HAVE_AVAHI
12808 avahi_shutdown();
12809 #endif /* HAVE_AVAHI */
12810
12811 #ifdef HAVE_LDAP
12812 if (((BrowseLocalProtocols | BrowseRemoteProtocols) & BROWSE_LDAP) &&
12813 BrowseLDAPHandle) {
12814 ldap_disconnect(BrowseLDAPHandle);
12815 BrowseLDAPHandle = NULL;
12816 }
12817 #endif /* HAVE_LDAP */
12818
12819 if (browsesocket != -1)
12820 close (browsesocket);
12821
12822 g_hash_table_destroy (local_printers);
12823 g_hash_table_destroy (cups_supported_remote_printers);
12824
12825 if (BrowseLocalProtocols & BROWSE_CUPS)
12826 g_list_free_full (browse_data, browse_data_free);
12827
12828 /* Close log file if we have one */
12829 if (debug_logfile == 1)
12830 stop_debug_logging();
12831
12832 if (deleted_master != NULL)
12833 free(deleted_master);
12834 if (DefaultOptions != NULL)
12835 free(DefaultOptions);
12836 if (DomainSocket != NULL)
12837 free(DomainSocket);
12838
12839 return ret;
12840
12841 help:
12842
12843 fprintf(stderr,
12844 "cups-browsed of cups-filters version "VERSION"\n\n"
12845 "Usage: cups-browsed [options]\n"
12846 "Options:\n"
12847 " -c cups-browsed.conf Set alternative cups-browsed.conf file to use.\n"
12848 " -d\n"
12849 " -v\n"
12850 " --debug Run in debug mode (logging to stderr).\n"
12851 " -l\n"
12852 " --logfile Run in debug mode (logging into file).\n"
12853 " -h\n"
12854 " --help\n"
12855 " --version Show this usage message.\n"
12856 " -o Option=Value Supply configuration option via command line,\n"
12857 " options are the same as in cups-browsed.conf.\n"
12858 " --autoshutdown=<mode> Automatically shut down cups-browsed when inactive:\n"
12859 " <mode> can be set to Off, On, or avahi, where Off\n"
12860 " means that cups-browsed stays running permanently\n"
12861 " (default), On means that it shuts down after 30\n"
12862 " seconds (or any given timeout) of inactivity, and\n"
12863 " avahi means that cups-browsed shuts down when\n"
12864 " avahi-daemon shuts down.\n"
12865 " --autoshutdown-timout=<time> Timeout (in seconds) for auto-shutdown.\n"
12866 " --autoshutdown-on=<type> Type of inactivity which leads to an auto-\n"
12867 " shutdown: If <type> is \"no-queues\", the shutdown\n"
12868 " is triggered by not having any cups-browsed-created\n"
12869 " print queue any more. With <type> being \"no-jobs\"\n"
12870 " shutdown is initiated by no job being printed\n"
12871 " on any cups-browsed-generated print queue any more.\n"
12872 " \"no-queues\" is the default.\n"
12873 );
12874
12875 return 1;
12876 }
12877