2 * "$Id: dirsvc.c 5043 2006-02-01 18:55:16Z mike $"
4 * Directory services routines for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2005 by Easy Software Products, all rights reserved.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * cupsdLoadRemoteCache() - Load the remote printer cache.
27 * cupsdSaveRemoteCache() - Save the remote printer cache.
28 * cupsdSendBrowseDelete() - Send a "browse delete" message for a
30 * cupsdSendBrowseList() - Send new browsing information as necessary.
31 * cupsdSendCUPSBrowse() - Send new browsing information using the
33 * cupsdSendSLPBrowse() - Register the specified printer with SLP.
34 * cupsdStartBrowsing() - Start sending and receiving broadcast
36 * cupsdStartPolling() - Start polling servers as needed.
37 * cupsdStopBrowsing() - Stop sending and receiving broadcast
39 * cupsdStopPolling() - Stop polling servers as needed.
40 * cupsdUpdateCUPSBrowse() - Update the browse lists using the CUPS
42 * cupsdUpdatePolling() - Read status messages from the poll daemons.
43 * cupsdUpdateSLPBrowse() - Get browsing information via SLP.
44 * dequote() - Remote quotes from a string.
45 * process_browse_data() - Process new browse data.
46 * process_implicit_classes() - Create/update implicit classes as needed.
47 * slp_attr_callback() - SLP attribute callback
48 * slp_dereg_printer() - SLPDereg() the specified printer
49 * slp_get_attr() - Get an attribute from an SLP registration.
50 * slp_reg_callback() - Empty SLPRegReport.
51 * slp_url_callback() - SLP service url callback
55 * Include necessary headers...
66 static char *dequote(char *d
, const char *s
, int dlen
);
67 static void process_browse_data(const char *uri
, cups_ptype_t type
,
68 ipp_pstate_t state
, const char *location
,
69 const char *info
, const char *make_model
,
70 int num_attrs
, cups_option_t
*attrs
);
71 static void process_implicit_classes(void);
80 * SLP service name for CUPS...
83 # define SLP_CUPS_SRVTYPE "service:printer"
84 # define SLP_CUPS_SRVLEN 15
88 * Printer service URL structure
91 typedef struct _slpsrvurl_s
/**** SLP URL list ****/
93 struct _slpsrvurl_s
*next
; /* Next URL in list */
94 char url
[HTTP_MAX_URI
];
103 static SLPBoolean
slp_attr_callback(SLPHandle hslp
, const char *attrlist
,
104 SLPError errcode
, void *cookie
);
105 static void slp_dereg_printer(cupsd_printer_t
*p
);
106 static int slp_get_attr(const char *attrlist
, const char *tag
,
108 static void slp_reg_callback(SLPHandle hslp
, SLPError errcode
,
110 static SLPBoolean
slp_url_callback(SLPHandle hslp
, const char *srvurl
,
111 unsigned short lifetime
,
112 SLPError errcode
, void *cookie
);
113 #endif /* HAVE_LIBSLP */
117 * 'cupsdLoadRemoteCache()' - Load the remote printer cache.
121 cupsdLoadRemoteCache(void)
123 cups_file_t
*fp
; /* remote.cache file */
124 int linenum
; /* Current line number */
125 char line
[1024], /* Line from file */
126 *value
, /* Pointer to value */
127 *valueptr
; /* Pointer into value */
128 cupsd_printer_t
*p
; /* Current printer */
129 time_t now
; /* Current time */
133 * Open the remote.cache file...
136 snprintf(line
, sizeof(line
), "%s/remote.cache", CacheDir
);
137 if ((fp
= cupsFileOpen(line
, "r")) == NULL
)
141 * Read printer configurations until we hit EOF...
148 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
151 * Decode the directive...
154 if (!strcasecmp(line
, "<Printer") ||
155 !strcasecmp(line
, "<DefaultPrinter"))
158 * <Printer name> or <DefaultPrinter name>
161 if (p
== NULL
&& value
)
164 * Add the printer and a base file type...
167 cupsdLogMessage(CUPSD_LOG_DEBUG
,
168 "cupsdLoadRemoteCache: Loading printer %s...", value
);
170 p
= cupsdAddPrinter(value
);
172 p
->state
= IPP_PRINTER_IDLE
;
173 p
->type
|= CUPS_PRINTER_REMOTE
;
174 p
->browse_time
= now
+ BrowseTimeout
;
177 * Set the default printer as needed...
180 if (!strcasecmp(line
, "<DefaultPrinter"))
185 cupsdLogMessage(CUPSD_LOG_ERROR
,
186 "Syntax error on line %d of remote.cache.", linenum
);
190 else if (!strcasecmp(line
, "<Class") ||
191 !strcasecmp(line
, "<DefaultClass"))
194 * <Class name> or <DefaultClass name>
197 if (p
== NULL
&& value
)
200 * Add the printer and a base file type...
203 cupsdLogMessage(CUPSD_LOG_DEBUG
,
204 "cupsdLoadRemoteCache: Loading class %s...", value
);
206 p
= cupsdAddClass(value
);
208 p
->state
= IPP_PRINTER_IDLE
;
209 p
->type
|= CUPS_PRINTER_REMOTE
;
210 p
->browse_time
= now
+ BrowseTimeout
;
213 * Set the default printer as needed...
216 if (!strcasecmp(line
, "<DefaultClass"))
221 cupsdLogMessage(CUPSD_LOG_ERROR
,
222 "Syntax error on line %d of remote.cache.", linenum
);
226 else if (!strcasecmp(line
, "</Printer>") ||
227 !strcasecmp(line
, "</Class>"))
232 * Close out the current printer...
235 cupsdSetPrinterAttrs(p
);
241 cupsdLogMessage(CUPSD_LOG_ERROR
,
242 "Syntax error on line %d of remote.cache.", linenum
);
248 cupsdLogMessage(CUPSD_LOG_ERROR
,
249 "Syntax error on line %d of remote.cache.", linenum
);
252 else if (!strcasecmp(line
, "Info"))
255 cupsdSetString(&p
->info
, value
);
257 else if (!strcasecmp(line
, "MakeModel"))
260 cupsdSetString(&p
->make_model
, value
);
262 else if (!strcasecmp(line
, "Location"))
265 cupsdSetString(&p
->location
, value
);
267 else if (!strcasecmp(line
, "DeviceURI"))
270 cupsdSetString(&p
->device_uri
, value
);
273 cupsdLogMessage(CUPSD_LOG_ERROR
,
274 "Syntax error on line %d of remote.cache.", linenum
);
278 else if (!strcasecmp(line
, "State"))
281 * Set the initial queue state...
284 if (value
&& !strcasecmp(value
, "idle"))
285 p
->state
= IPP_PRINTER_IDLE
;
286 else if (value
&& !strcasecmp(value
, "stopped"))
287 p
->state
= IPP_PRINTER_STOPPED
;
290 cupsdLogMessage(CUPSD_LOG_ERROR
,
291 "Syntax error on line %d of remote.cache.", linenum
);
295 else if (!strcasecmp(line
, "StateMessage"))
298 * Set the initial queue state message...
302 strlcpy(p
->state_message
, value
, sizeof(p
->state_message
));
304 else if (!strcasecmp(line
, "Accepting"))
307 * Set the initial accepting state...
311 (!strcasecmp(value
, "yes") ||
312 !strcasecmp(value
, "on") ||
313 !strcasecmp(value
, "true")))
316 (!strcasecmp(value
, "no") ||
317 !strcasecmp(value
, "off") ||
318 !strcasecmp(value
, "false")))
322 cupsdLogMessage(CUPSD_LOG_ERROR
,
323 "Syntax error on line %d of remote.cache.", linenum
);
327 else if (!strcasecmp(line
, "Type"))
330 p
->type
= atoi(value
);
333 cupsdLogMessage(CUPSD_LOG_ERROR
,
334 "Syntax error on line %d of remote.cache.", linenum
);
338 else if (!strcasecmp(line
, "BrowseTime"))
342 time_t t
= atoi(value
);
344 if (t
> (now
+ BrowseInterval
))
349 cupsdLogMessage(CUPSD_LOG_ERROR
,
350 "Syntax error on line %d of remote.cache.", linenum
);
354 else if (!strcasecmp(line
, "JobSheets"))
357 * Set the initial job sheets...
362 for (valueptr
= value
; *valueptr
&& !isspace(*valueptr
& 255); valueptr
++);
367 cupsdSetString(&p
->job_sheets
[0], value
);
369 while (isspace(*valueptr
& 255))
374 for (value
= valueptr
; *valueptr
&& !isspace(*valueptr
& 255); valueptr
++);
379 cupsdSetString(&p
->job_sheets
[1], value
);
384 cupsdLogMessage(CUPSD_LOG_ERROR
,
385 "Syntax error on line %d of remote.cache.", linenum
);
389 else if (!strcasecmp(line
, "AllowUser"))
394 cupsdAddPrinterUser(p
, value
);
398 cupsdLogMessage(CUPSD_LOG_ERROR
,
399 "Syntax error on line %d of remote.cache.", linenum
);
403 else if (!strcasecmp(line
, "DenyUser"))
408 cupsdAddPrinterUser(p
, value
);
412 cupsdLogMessage(CUPSD_LOG_ERROR
,
413 "Syntax error on line %d of remote.cache.", linenum
);
420 * Something else we don't understand...
423 cupsdLogMessage(CUPSD_LOG_ERROR
,
424 "Unknown configuration directive %s on line %d of remote.cache.",
432 * Do auto-classing if needed...
435 process_implicit_classes();
440 * 'cupsdSaveRemoteCache()' - Save the remote printer cache.
444 cupsdSaveRemoteCache(void)
446 int i
; /* Looping var */
447 cups_file_t
*fp
; /* printers.conf file */
448 char temp
[1024]; /* Temporary string */
449 cupsd_printer_t
*printer
; /* Current printer class */
450 time_t curtime
; /* Current time */
451 struct tm
*curdate
; /* Current date */
455 * Create the remote.cache file...
458 snprintf(temp
, sizeof(temp
), "%s/remote.cache", CacheDir
);
460 if ((fp
= cupsFileOpen(temp
, "w")) == NULL
)
462 cupsdLogMessage(CUPSD_LOG_ERROR
,
463 "Unable to save remote.cache - %s", strerror(errno
));
467 cupsdLogMessage(CUPSD_LOG_INFO
, "Saving remote.cache...");
470 * Restrict access to the file...
473 fchown(cupsFileNumber(fp
), getuid(), Group
);
474 fchmod(cupsFileNumber(fp
), ConfigFilePerm
);
477 * Write a small header to the file...
480 curtime
= time(NULL
);
481 curdate
= localtime(&curtime
);
482 strftime(temp
, sizeof(temp
) - 1, "%Y-%m-%d %H:%M", curdate
);
484 cupsFilePuts(fp
, "# Remote cache file for " CUPS_SVERSION
"\n");
485 cupsFilePrintf(fp
, "# Written by cupsd on %s\n", temp
);
488 * Write each local printer known to the system...
491 for (printer
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
493 printer
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
496 * Skip local destinations...
499 if (!(printer
->type
& CUPS_PRINTER_REMOTE
))
503 * Write printers as needed...
506 if (printer
== DefaultPrinter
)
507 cupsFilePuts(fp
, "<Default");
509 cupsFilePutChar(fp
, '<');
511 if (printer
->type
& CUPS_PRINTER_CLASS
)
512 cupsFilePrintf(fp
, "Class %s>\n", printer
->name
);
514 cupsFilePrintf(fp
, "Printer %s>\n", printer
->name
);
516 cupsFilePrintf(fp
, "Type %d\n", printer
->type
);
518 cupsFilePrintf(fp
, "BrowseTime %d\n", (int)printer
->browse_time
);
521 cupsFilePrintf(fp
, "Info %s\n", printer
->info
);
523 if (printer
->make_model
)
524 cupsFilePrintf(fp
, "MakeModel %s\n", printer
->make_model
);
526 if (printer
->location
)
527 cupsFilePrintf(fp
, "Location %s\n", printer
->location
);
529 if (printer
->device_uri
)
530 cupsFilePrintf(fp
, "DeviceURI %s\n", printer
->device_uri
);
532 if (printer
->state
== IPP_PRINTER_STOPPED
)
534 cupsFilePuts(fp
, "State Stopped\n");
535 cupsFilePrintf(fp
, "StateMessage %s\n", printer
->state_message
);
538 cupsFilePuts(fp
, "State Idle\n");
540 if (printer
->accepting
)
541 cupsFilePuts(fp
, "Accepting Yes\n");
543 cupsFilePuts(fp
, "Accepting No\n");
545 cupsFilePrintf(fp
, "JobSheets %s %s\n", printer
->job_sheets
[0],
546 printer
->job_sheets
[1]);
548 for (i
= 0; i
< printer
->num_users
; i
++)
549 cupsFilePrintf(fp
, "%sUser %s\n", printer
->deny_users
? "Deny" : "Allow",
552 if (printer
->type
& CUPS_PRINTER_CLASS
)
553 cupsFilePuts(fp
, "</Class>\n");
555 cupsFilePuts(fp
, "</Printer>\n");
563 * 'cupsdSendBrowseDelete()' - Send a "browse delete" message for a printer.
567 cupsdSendBrowseDelete(
568 cupsd_printer_t
*p
) /* I - Printer to delete */
571 * Only announce if browsing is enabled...
574 if (!Browsing
|| !p
->shared
)
578 * First mark the printer for deletion...
581 p
->type
|= CUPS_PRINTER_DELETE
;
584 * Announce the deletion...
587 if (BrowseLocalProtocols
& BROWSE_CUPS
)
588 cupsdSendCUPSBrowse(p
);
590 if (BrowseLocalProtocols
& BROWSE_SLP
)
591 slp_dereg_printer(p
);
592 #endif /* HAVE_LIBSLP */
597 * 'cupsdSendBrowseList()' - Send new browsing information as necessary.
601 cupsdSendBrowseList(void)
603 int count
; /* Number of dests to update */
604 cupsd_printer_t
*p
; /* Current printer */
605 time_t ut
, /* Minimum update time */
606 to
; /* Timeout time */
609 if (!Browsing
|| !BrowseLocalProtocols
|| !Printers
)
613 * Compute the update and timeout times...
616 ut
= time(NULL
) - BrowseInterval
;
617 to
= time(NULL
) - BrowseTimeout
;
620 * Figure out how many printers need an update...
623 if (BrowseInterval
> 0)
625 int max_count
; /* Maximum number to update */
629 * Throttle the number of printers we'll be updating this time
630 * around based on the number of queues that need updating and
631 * the maximum number of queues to update each second...
634 max_count
= 2 * cupsArrayCount(Printers
) / BrowseInterval
+ 1;
636 for (count
= 0, p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
637 count
< max_count
&& p
!= NULL
;
638 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
639 if (!(p
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)) &&
640 p
->shared
&& p
->browse_time
< ut
)
644 * Loop through all of the printers and send local updates as needed...
648 p
= (cupsd_printer_t
*)cupsArrayFind(Printers
, BrowseNext
);
650 p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
654 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
657 * Check for wraparound...
661 p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
665 else if ((p
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
)) ||
668 else if (p
->browse_time
< ut
)
671 * Need to send an update...
676 p
->browse_time
= time(NULL
);
678 if (BrowseLocalProtocols
& BROWSE_CUPS
)
679 cupsdSendCUPSBrowse(p
);
682 if (BrowseLocalProtocols
& BROWSE_SLP
)
683 cupsdSendSLPBrowse(p
);
684 #endif /* HAVE_LIBSLP */
689 * Save where we left off so that all printers get updated...
696 * Loop through all of the printers and send local updates as needed...
699 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
701 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
704 * If this is a remote queue, see if it needs to be timed out...
707 if (p
->type
& CUPS_PRINTER_REMOTE
)
709 if (p
->browse_time
< to
)
711 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED
, p
, NULL
,
712 "%s \'%s\' deleted by directory services (timeout).",
713 (p
->type
& CUPS_PRINTER_CLASS
) ? "Class" : "Printer",
716 cupsdLogMessage(CUPSD_LOG_INFO
,
717 "Remote destination \"%s\" has timed out; deleting it...",
720 cupsArraySave(Printers
);
721 cupsdDeletePrinter(p
, 1);
722 cupsArrayRestore(Printers
);
730 * 'cupsdSendCUPSBrowse()' - Send new browsing information using the CUPS protocol.
734 cupsdSendCUPSBrowse(cupsd_printer_t
*p
) /* I - Printer to send */
736 int i
; /* Looping var */
737 cups_ptype_t type
; /* Printer type */
738 cupsd_dirsvc_addr_t
*b
; /* Browse address */
739 int bytes
; /* Length of packet */
740 char packet
[1453], /* Browse data packet */
741 uri
[1024], /* Printer URI */
742 options
[1024], /* Browse local options */
743 location
[1024], /* printer-location */
744 info
[1024], /* printer-info */
746 /* printer-make-and-model */
747 cupsd_netif_t
*iface
; /* Network interface */
751 * Figure out the printer type value...
754 type
= p
->type
| CUPS_PRINTER_REMOTE
;
757 type
|= CUPS_PRINTER_REJECTING
;
759 if (p
== DefaultPrinter
)
760 type
|= CUPS_PRINTER_DEFAULT
;
763 * Initialize the browse options...
766 if (BrowseLocalOptions
)
767 snprintf(options
, sizeof(options
), " ipp-options=%s", BrowseLocalOptions
);
772 * Remove quotes from printer-info, printer-location, and
773 * printer-make-and-model attributes...
776 dequote(location
, p
->location
, sizeof(p
->location
));
777 dequote(info
, p
->info
, sizeof(p
->info
));
778 dequote(make_model
, p
->make_model
? p
->make_model
: "Unknown",
782 * Send a packet to each browse address...
785 for (i
= NumBrowsers
, b
= Browsers
; i
> 0; i
--, b
++)
789 * Send the browse packet to one or more interfaces...
792 if (!strcmp(b
->iface
, "*"))
795 * Send to all local interfaces...
800 for (iface
= (cupsd_netif_t
*)cupsArrayFirst(NetIFList
);
802 iface
= (cupsd_netif_t
*)cupsArrayNext(NetIFList
))
805 * Only send to local, IPv4 interfaces...
808 if (!iface
->is_local
|| !iface
->port
||
809 iface
->address
.addr
.sa_family
!= AF_INET
)
812 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
813 iface
->hostname
, iface
->port
,
814 (p
->type
& CUPS_PRINTER_CLASS
) ? "/classes/%s%s" :
817 snprintf(packet
, sizeof(packet
), "%x %x %s \"%s\" \"%s\" \"%s\"%s\n",
818 type
, p
->state
, uri
, location
, info
, make_model
, options
);
820 bytes
= strlen(packet
);
822 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
823 "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes
,
824 iface
->name
, packet
);
826 iface
->broadcast
.ipv4
.sin_port
= htons(BrowsePort
);
828 sendto(BrowseSocket
, packet
, bytes
, 0,
829 (struct sockaddr
*)&(iface
->broadcast
),
830 sizeof(struct sockaddr_in
));
833 else if ((iface
= cupsdNetIFFind(b
->iface
)) != NULL
)
836 * Send to the named interface using the IPv4 address...
840 if (strcmp(b
->iface
, iface
->name
))
845 else if (iface
->address
.addr
.sa_family
== AF_INET
&& iface
->port
)
848 iface
= (cupsd_netif_t
*)cupsArrayNext(NetIFList
);
852 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
853 iface
->hostname
, iface
->port
,
854 (p
->type
& CUPS_PRINTER_CLASS
) ? "/classes/%s%s" :
857 snprintf(packet
, sizeof(packet
), "%x %x %s \"%s\" \"%s\" \"%s\"%s\n",
858 type
, p
->state
, uri
, location
, info
, make_model
, options
);
860 bytes
= strlen(packet
);
862 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
863 "cupsdSendBrowseList: (%d bytes to \"%s\") %s", bytes
,
864 iface
->name
, packet
);
866 iface
->broadcast
.ipv4
.sin_port
= htons(BrowsePort
);
868 sendto(BrowseSocket
, packet
, bytes
, 0,
869 (struct sockaddr
*)&(iface
->broadcast
),
870 sizeof(struct sockaddr_in
));
877 * Send the browse packet to the indicated address using
878 * the default server name...
881 snprintf(packet
, sizeof(packet
), "%x %x %s \"%s\" \"%s\" \"%s\"%s\n",
882 type
, p
->state
, p
->uri
, location
, info
, make_model
, options
);
884 bytes
= strlen(packet
);
885 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
886 "cupsdSendBrowseList: (%d bytes) %s", bytes
, packet
);
888 if (sendto(BrowseSocket
, packet
, bytes
, 0,
889 (struct sockaddr
*)&(b
->to
),
890 sizeof(struct sockaddr_in
)) <= 0)
893 * Unable to send browse packet, so remove this address from the
897 cupsdLogMessage(CUPSD_LOG_ERROR
,
898 "cupsdSendBrowseList: sendto failed for browser %d - %s.",
899 b
- Browsers
+ 1, strerror(errno
));
902 memcpy(b
, b
+ 1, (i
- 1) * sizeof(cupsd_dirsvc_addr_t
));
913 * 'cupsdSendSLPBrowse()' - Register the specified printer with SLP.
917 cupsdSendSLPBrowse(cupsd_printer_t
*p
) /* I - Printer to register */
919 char srvurl
[HTTP_MAX_URI
], /* Printer service URI */
920 attrs
[8192], /* Printer attributes */
921 finishings
[1024], /* Finishings to support */
922 make_model
[IPP_MAX_NAME
* 2],
923 /* Make and model, quoted */
924 location
[IPP_MAX_NAME
* 2],
925 /* Location, quoted */
926 info
[IPP_MAX_NAME
* 2], /* Info, quoted */
927 *src
, /* Pointer to original string */
928 *dst
; /* Pointer to destination string */
929 ipp_attribute_t
*authentication
; /* uri-authentication-supported value */
930 SLPError error
; /* SLP error, if any */
933 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdSendSLPBrowse(%p = \"%s\")", p
,
937 * Make the SLP service URL that conforms to the IANA
938 * 'printer:' template.
941 snprintf(srvurl
, sizeof(srvurl
), SLP_CUPS_SRVTYPE
":%s", p
->uri
);
943 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "Service URL = \"%s\"", srvurl
);
946 * Figure out the finishings string...
949 if (p
->type
& CUPS_PRINTER_STAPLE
)
950 strcpy(finishings
, "staple");
952 finishings
[0] = '\0';
954 if (p
->type
& CUPS_PRINTER_BIND
)
957 strlcat(finishings
, ",bind", sizeof(finishings
));
959 strcpy(finishings
, "bind");
962 if (p
->type
& CUPS_PRINTER_PUNCH
)
965 strlcat(finishings
, ",punch", sizeof(finishings
));
967 strcpy(finishings
, "punch");
970 if (p
->type
& CUPS_PRINTER_COVER
)
973 strlcat(finishings
, ",cover", sizeof(finishings
));
975 strcpy(finishings
, "cover");
978 if (p
->type
& CUPS_PRINTER_SORT
)
981 strlcat(finishings
, ",sort", sizeof(finishings
));
983 strcpy(finishings
, "sort");
987 strcpy(finishings
, "none");
990 * Quote any commas in the make and model, location, and info strings...
993 for (src
= p
->make_model
, dst
= make_model
;
994 src
&& *src
&& dst
< (make_model
+ sizeof(make_model
) - 2);)
996 if (*src
== ',' || *src
== '\\' || *src
== ')')
1005 strcpy(make_model
, "Unknown");
1007 for (src
= p
->location
, dst
= location
;
1008 src
&& *src
&& dst
< (location
+ sizeof(location
) - 2);)
1010 if (*src
== ',' || *src
== '\\' || *src
== ')')
1019 strcpy(location
, "Unknown");
1021 for (src
= p
->info
, dst
= info
;
1022 src
&& *src
&& dst
< (info
+ sizeof(info
) - 2);)
1024 if (*src
== ',' || *src
== '\\' || *src
== ')')
1033 strcpy(info
, "Unknown");
1036 * Get the authentication value...
1039 authentication
= ippFindAttribute(p
->attrs
, "uri-authentication-supported",
1043 * Make the SLP attribute string list that conforms to
1044 * the IANA 'printer:' template.
1047 snprintf(attrs
, sizeof(attrs
),
1048 "(printer-uri-supported=%s),"
1049 "(uri-authentication-supported=%s>),"
1051 "(uri-security-supported=tls>),"
1053 "(uri-security-supported=none>),"
1054 #endif /* HAVE_SSL */
1055 "(printer-name=%s),"
1056 "(printer-location=%s),"
1057 "(printer-info=%s),"
1058 "(printer-more-info=%s),"
1059 "(printer-make-and-model=%s),"
1060 "(charset-supported=utf-8),"
1061 "(natural-language-configured=%s),"
1062 "(natural-language-supported=de,en,es,fr,it),"
1063 "(color-supported=%s),"
1064 "(finishings-supported=%s),"
1065 "(sides-supported=one-sided%s),"
1066 "(multiple-document-jobs-supported=true)"
1067 "(ipp-versions-supported=1.0,1.1)",
1068 p
->uri
, authentication
->values
[0].string
.text
, p
->name
, location
,
1069 info
, p
->uri
, make_model
, DefaultLanguage
,
1070 p
->type
& CUPS_PRINTER_COLOR
? "true" : "false",
1072 p
->type
& CUPS_PRINTER_DUPLEX
?
1073 ",two-sided-long-edge,two-sided-short-edge" : "");
1075 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "Attributes = \"%s\"", attrs
);
1078 * Register the printer with the SLP server...
1081 error
= SLPReg(BrowseSLPHandle
, srvurl
, BrowseTimeout
,
1082 SLP_CUPS_SRVTYPE
, attrs
, SLP_TRUE
, slp_reg_callback
, 0);
1084 if (error
!= SLP_OK
)
1085 cupsdLogMessage(CUPSD_LOG_ERROR
, "SLPReg of \"%s\" failed with status %d!", p
->name
,
1088 #endif /* HAVE_LIBSLP */
1092 * 'cupsdStartBrowsing()' - Start sending and receiving broadcast information.
1096 cupsdStartBrowsing(void)
1098 int val
; /* Socket option value */
1099 struct sockaddr_in addr
; /* Broadcast address */
1104 if (!Browsing
|| !(BrowseLocalProtocols
| BrowseRemoteProtocols
))
1107 if ((BrowseLocalProtocols
| BrowseRemoteProtocols
) & BROWSE_CUPS
)
1109 if (BrowseSocket
< 0)
1112 * Create the broadcast socket...
1115 if ((BrowseSocket
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0)
1117 cupsdLogMessage(CUPSD_LOG_ERROR
,
1118 "cupsdStartBrowsing: Unable to create broadcast "
1119 "socket - %s.", strerror(errno
));
1120 BrowseLocalProtocols
&= ~BROWSE_CUPS
;
1121 BrowseRemoteProtocols
&= ~BROWSE_CUPS
;
1126 * Bind the socket to browse port...
1129 memset(&addr
, 0, sizeof(addr
));
1130 addr
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
1131 addr
.sin_family
= AF_INET
;
1132 addr
.sin_port
= htons(BrowsePort
);
1134 if (bind(BrowseSocket
, (struct sockaddr
*)&addr
, sizeof(addr
)))
1136 cupsdLogMessage(CUPSD_LOG_ERROR
,
1137 "cupsdStartBrowsing: Unable to bind broadcast "
1138 "socket - %s.", strerror(errno
));
1141 closesocket(BrowseSocket
);
1143 close(BrowseSocket
);
1147 BrowseLocalProtocols
&= ~BROWSE_CUPS
;
1148 BrowseRemoteProtocols
&= ~BROWSE_CUPS
;
1154 * Set the "broadcast" flag...
1158 if (setsockopt(BrowseSocket
, SOL_SOCKET
, SO_BROADCAST
, &val
, sizeof(val
)))
1160 cupsdLogMessage(CUPSD_LOG_ERROR
,
1161 "cupsdStartBrowsing: Unable to set broadcast mode - %s.",
1165 closesocket(BrowseSocket
);
1167 close(BrowseSocket
);
1171 BrowseLocalProtocols
&= ~BROWSE_CUPS
;
1172 BrowseRemoteProtocols
&= ~BROWSE_CUPS
;
1177 * Close the socket on exec...
1180 fcntl(BrowseSocket
, F_SETFD
, fcntl(BrowseSocket
, F_GETFD
) | FD_CLOEXEC
);
1183 * Finally, add the socket to the input selection set...
1186 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1187 "cupsdStartBrowsing: Adding fd %d to InputSet...",
1190 FD_SET(BrowseSocket
, InputSet
);
1196 if ((BrowseLocalProtocols
| BrowseRemoteProtocols
) & BROWSE_SLP
)
1199 * Open SLP handle...
1202 if (SLPOpen("en", SLP_FALSE
, &BrowseSLPHandle
) != SLP_OK
)
1204 cupsdLogMessage(CUPSD_LOG_ERROR
,
1205 "Unable to open an SLP handle; disabling SLP browsing!");
1206 BrowseLocalProtocols
&= ~BROWSE_SLP
;
1207 BrowseRemoteProtocols
&= ~BROWSE_SLP
;
1210 BrowseSLPRefresh
= 0;
1212 #endif /* HAVE_LIBSLP */
1217 * 'cupsdStartPolling()' - Start polling servers as needed.
1221 cupsdStartPolling(void)
1223 int i
; /* Looping var */
1224 cupsd_dirsvc_poll_t
*pollp
; /* Current polling server */
1225 char polld
[1024]; /* Poll daemon path */
1226 char sport
[10]; /* Server port */
1227 char bport
[10]; /* Browser port */
1228 char interval
[10]; /* Poll interval */
1229 int statusfds
[2]; /* Status pipe */
1230 char *argv
[6]; /* Arguments */
1231 char *envp
[100]; /* Environment */
1235 * Don't do anything if we aren't polling...
1241 PollStatusBuffer
= NULL
;
1246 * Setup string arguments for polld, port and interval options.
1249 snprintf(polld
, sizeof(polld
), "%s/daemon/cups-polld", ServerBin
);
1251 sprintf(bport
, "%d", BrowsePort
);
1254 sprintf(interval
, "%d", BrowseInterval
);
1256 strcpy(interval
, "30");
1258 argv
[0] = "cups-polld";
1264 cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
1267 * Create a pipe that receives the status messages from each
1271 if (cupsdOpenPipe(statusfds
))
1273 cupsdLogMessage(CUPSD_LOG_ERROR
,
1274 "Unable to create polling status pipes - %s.",
1277 PollStatusBuffer
= NULL
;
1281 PollPipe
= statusfds
[0];
1282 PollStatusBuffer
= cupsdStatBufNew(PollPipe
, "[Poll]");
1285 * Run each polling daemon, redirecting stderr to the polling pipe...
1288 for (i
= 0, pollp
= Polled
; i
< NumPolled
; i
++, pollp
++)
1290 sprintf(sport
, "%d", pollp
->port
);
1292 argv
[1] = pollp
->hostname
;
1294 if (cupsdStartProcess(polld
, argv
, envp
, -1, -1, statusfds
[1], -1,
1295 0, &(pollp
->pid
)) < 0)
1297 cupsdLogMessage(CUPSD_LOG_ERROR
,
1298 "cupsdStartPolling: Unable to fork polling daemon - %s",
1304 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1305 "cupsdStartPolling: Started polling daemon for %s:%d, pid = %d",
1306 pollp
->hostname
, pollp
->port
, pollp
->pid
);
1309 close(statusfds
[1]);
1312 * Finally, add the pipe to the input selection set...
1315 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1316 "cupsdStartPolling: Adding fd %d to InputSet...", PollPipe
);
1318 FD_SET(PollPipe
, InputSet
);
1323 * 'cupsdStopBrowsing()' - Stop sending and receiving broadcast information.
1327 cupsdStopBrowsing(void)
1329 if (!Browsing
|| !(BrowseLocalProtocols
| BrowseRemoteProtocols
))
1332 if (((BrowseLocalProtocols
| BrowseRemoteProtocols
) & BROWSE_CUPS
) &&
1336 * Close the socket and remove it from the input selection set.
1340 closesocket(BrowseSocket
);
1342 close(BrowseSocket
);
1345 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1346 "cupsdStopBrowsing: Removing fd %d from InputSet...",
1349 FD_CLR(BrowseSocket
, InputSet
);
1354 if ((BrowseLocalProtocols
| BrowseRemoteProtocols
) & BROWSE_SLP
)
1357 * Close SLP handle...
1360 SLPClose(BrowseSLPHandle
);
1362 #endif /* HAVE_LIBSLP */
1367 * 'cupsdStopPolling()' - Stop polling servers as needed.
1371 cupsdStopPolling(void)
1373 int i
; /* Looping var */
1374 cupsd_dirsvc_poll_t
*pollp
; /* Current polling server */
1379 cupsdStatBufDelete(PollStatusBuffer
);
1382 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1383 "cupsdStopPolling: removing fd %d from InputSet.", PollPipe
);
1384 FD_CLR(PollPipe
, InputSet
);
1387 PollStatusBuffer
= NULL
;
1390 for (i
= 0, pollp
= Polled
; i
< NumPolled
; i
++, pollp
++)
1392 cupsdEndProcess(pollp
->pid
, 0);
1397 * 'cupsdUpdateCUPSBrowse()' - Update the browse lists using the CUPS protocol.
1401 cupsdUpdateCUPSBrowse(void)
1403 int i
; /* Looping var */
1404 int auth
; /* Authorization status */
1405 int len
; /* Length of name string */
1406 int bytes
; /* Number of bytes left */
1407 char packet
[1541], /* Broadcast packet */
1408 *pptr
; /* Pointer into packet */
1409 socklen_t srclen
; /* Length of source address */
1410 http_addr_t srcaddr
; /* Source address */
1411 char srcname
[1024]; /* Source hostname */
1412 unsigned address
[4]; /* Source address */
1413 unsigned type
; /* Printer type */
1414 unsigned state
; /* Printer state */
1415 char uri
[HTTP_MAX_URI
], /* Printer URI */
1416 method
[HTTP_MAX_URI
], /* Method portion of URI */
1417 username
[HTTP_MAX_URI
], /* Username portion of URI */
1418 host
[HTTP_MAX_URI
], /* Host portion of URI */
1419 resource
[HTTP_MAX_URI
], /* Resource portion of URI */
1420 info
[IPP_MAX_NAME
], /* Information string */
1421 location
[IPP_MAX_NAME
], /* Location string */
1422 make_model
[IPP_MAX_NAME
];/* Make and model string */
1423 int port
; /* Port portion of URI */
1424 cupsd_netif_t
*iface
; /* Network interface */
1425 int num_attrs
; /* Number of attributes */
1426 cups_option_t
*attrs
; /* Attributes */
1430 * Read a packet from the browse socket...
1433 srclen
= sizeof(srcaddr
);
1434 if ((bytes
= recvfrom(BrowseSocket
, packet
, sizeof(packet
) - 1, 0,
1435 (struct sockaddr
*)&srcaddr
, &srclen
)) < 0)
1438 * "Connection refused" is returned under Linux if the destination port
1439 * or address is unreachable from a previous sendto(); check for the
1440 * error here and ignore it for now...
1443 if (errno
!= ECONNREFUSED
&& errno
!= EAGAIN
)
1445 cupsdLogMessage(CUPSD_LOG_ERROR
, "Browse recv failed - %s.",
1447 cupsdLogMessage(CUPSD_LOG_ERROR
, "Browsing turned off.");
1449 cupsdStopBrowsing();
1456 packet
[bytes
] = '\0';
1459 * If we're about to sleep, ignore incoming browse packets.
1466 * Figure out where it came from...
1470 if (srcaddr
.addr
.sa_family
== AF_INET6
)
1472 address
[0] = ntohl(srcaddr
.ipv6
.sin6_addr
.s6_addr32
[0]);
1473 address
[1] = ntohl(srcaddr
.ipv6
.sin6_addr
.s6_addr32
[1]);
1474 address
[2] = ntohl(srcaddr
.ipv6
.sin6_addr
.s6_addr32
[2]);
1475 address
[3] = ntohl(srcaddr
.ipv6
.sin6_addr
.s6_addr32
[3]);
1478 #endif /* AF_INET6 */
1483 address
[3] = ntohl(srcaddr
.ipv4
.sin_addr
.s_addr
);
1486 if (HostNameLookups
)
1487 httpAddrLookup(&srcaddr
, srcname
, sizeof(srcname
));
1489 httpAddrString(&srcaddr
, srcname
, sizeof(srcname
));
1491 len
= strlen(srcname
);
1499 if (httpAddrLocalhost(&srcaddr
) || !strcasecmp(srcname
, "localhost"))
1502 * Access from localhost (127.0.0.1) is always allowed...
1510 * Do authorization checks on the domain/address...
1513 switch (BrowseACL
->order_type
)
1516 auth
= AUTH_DENY
; /* anti-compiler-warning-code */
1519 case AUTH_ALLOW
: /* Order Deny,Allow */
1522 if (cupsdCheckAuth(address
, srcname
, len
,
1523 BrowseACL
->num_deny
, BrowseACL
->deny
))
1526 if (cupsdCheckAuth(address
, srcname
, len
,
1527 BrowseACL
->num_allow
, BrowseACL
->allow
))
1531 case AUTH_DENY
: /* Order Allow,Deny */
1534 if (cupsdCheckAuth(address
, srcname
, len
,
1535 BrowseACL
->num_allow
, BrowseACL
->allow
))
1538 if (cupsdCheckAuth(address
, srcname
, len
,
1539 BrowseACL
->num_deny
, BrowseACL
->deny
))
1548 if (auth
== AUTH_DENY
)
1550 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1551 "cupsdUpdateCUPSBrowse: Refused %d bytes from %s", bytes
,
1556 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
1557 "cupsdUpdateCUPSBrowse: (%d bytes from %s) %s", bytes
,
1564 if (sscanf(packet
, "%x%x%1023s", &type
, &state
, uri
) < 3)
1566 cupsdLogMessage(CUPSD_LOG_WARN
,
1567 "cupsdUpdateCUPSBrowse: Garbled browse packet - %s", packet
);
1571 strcpy(location
, "Location Unknown");
1572 strcpy(info
, "No Information Available");
1573 make_model
[0] = '\0';
1577 if ((pptr
= strchr(packet
, '\"')) != NULL
)
1580 * Have extended information; can't use sscanf for it because not all
1581 * sscanf's allow empty strings with %[^\"]...
1584 for (i
= 0, pptr
++;
1585 i
< (sizeof(location
) - 1) && *pptr
&& *pptr
!= '\"';
1587 location
[i
] = *pptr
;
1595 while (*pptr
&& isspace(*pptr
& 255))
1600 for (i
= 0, pptr
++;
1601 i
< (sizeof(info
) - 1) && *pptr
&& *pptr
!= '\"';
1610 while (*pptr
&& isspace(*pptr
& 255))
1615 for (i
= 0, pptr
++;
1616 i
< (sizeof(make_model
) - 1) && *pptr
&& *pptr
!= '\"';
1618 make_model
[i
] = *pptr
;
1623 make_model
[i
] = '\0';
1626 num_attrs
= cupsParseOptions(pptr
, num_attrs
, &attrs
);
1632 DEBUG_printf(("type=%x, state=%x, uri=\"%s\"\n"
1633 "location=\"%s\", info=\"%s\", make_model=\"%s\"\n",
1634 type
, state
, uri
, location
, info
, make_model
));
1637 * Pull the URI apart to see if this is a local or remote printer...
1640 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
, method
, sizeof(method
), username
,
1641 sizeof(username
), host
, sizeof(host
), &port
, resource
,
1644 DEBUG_printf(("host=\"%s\", ServerName=\"%s\"\n", host
, ServerName
));
1647 * Check for packets from the local server...
1650 if (!strcasecmp(host
, ServerName
) && port
== LocalPort
)
1652 cupsFreeOptions(num_attrs
, attrs
);
1658 for (iface
= (cupsd_netif_t
*)cupsArrayFirst(NetIFList
);
1660 iface
= (cupsd_netif_t
*)cupsArrayNext(NetIFList
))
1661 if (!strcasecmp(host
, iface
->hostname
) && port
== iface
->port
)
1663 cupsFreeOptions(num_attrs
, attrs
);
1671 for (i
= 0; i
< NumRelays
; i
++)
1672 if (cupsdCheckAuth(address
, srcname
, len
, 1, &(Relays
[i
].from
)))
1673 if (sendto(BrowseSocket
, packet
, bytes
, 0,
1674 (struct sockaddr
*)&(Relays
[i
].to
),
1675 sizeof(http_addr_t
)) <= 0)
1677 cupsdLogMessage(CUPSD_LOG_ERROR
,
1678 "cupsdUpdateCUPSBrowse: sendto failed for relay %d - %s.",
1679 i
+ 1, strerror(errno
));
1680 cupsFreeOptions(num_attrs
, attrs
);
1685 * Process the browse data...
1688 process_browse_data(uri
, (cups_ptype_t
)type
, (ipp_pstate_t
)state
, location
,
1689 info
, make_model
, num_attrs
, attrs
);
1690 cupsFreeOptions(num_attrs
, attrs
);
1695 * 'cupsdUpdatePolling()' - Read status messages from the poll daemons.
1699 cupsdUpdatePolling(void)
1701 char *ptr
, /* Pointer to end of line in buffer */
1702 message
[1024]; /* Pointer to message text */
1703 int loglevel
; /* Log level for message */
1706 while ((ptr
= cupsdStatBufUpdate(PollStatusBuffer
, &loglevel
,
1707 message
, sizeof(message
))) != NULL
)
1708 if (!strchr(PollStatusBuffer
->buffer
, '\n'))
1714 * All polling processes have died; stop polling...
1717 cupsdLogMessage(CUPSD_LOG_ERROR
,
1718 "cupsdUpdatePolling: all polling processes have exited!");
1726 * 'cupsdUpdateSLPBrowse()' - Get browsing information via SLP.
1730 cupsdUpdateSLPBrowse(void)
1732 slpsrvurl_t
*s
, /* Temporary list of service URLs */
1733 *next
; /* Next service in list */
1734 cupsd_printer_t p
; /* Printer information */
1735 const char *uri
; /* Pointer to printer URI */
1736 char method
[HTTP_MAX_URI
],
1737 /* Method portion of URI */
1738 username
[HTTP_MAX_URI
],
1739 /* Username portion of URI */
1741 /* Host portion of URI */
1742 resource
[HTTP_MAX_URI
];
1743 /* Resource portion of URI */
1744 int port
; /* Port portion of URI */
1747 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdUpdateSLPBrowse() Start...");
1750 * Reset the refresh time...
1753 BrowseSLPRefresh
= time(NULL
) + BrowseInterval
;
1756 * Poll for remote printers using SLP...
1761 SLPFindSrvs(BrowseSLPHandle
, SLP_CUPS_SRVTYPE
, "", "",
1762 slp_url_callback
, &s
);
1765 * Loop through the list of available printers...
1771 * Save the "next" pointer...
1777 * Load a cupsd_printer_t structure with the SLP service attributes...
1780 SLPFindAttrs(BrowseSLPHandle
, s
->url
, "", "", slp_attr_callback
, &p
);
1783 * Process this printer entry...
1786 uri
= s
->url
+ SLP_CUPS_SRVLEN
+ 1;
1788 if (!strncmp(uri
, "http://", 7) || !strncmp(uri
, "ipp://", 6))
1791 * Pull the URI apart to see if this is a local or remote printer...
1794 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
, method
, sizeof(method
),
1795 username
, sizeof(username
), host
, sizeof(host
), &port
,
1796 resource
, sizeof(resource
));
1798 if (!strcasecmp(host
, ServerName
))
1802 * OK, at least an IPP printer, see if it is a CUPS printer or
1806 if (strstr(uri
, "/printers/") != NULL
)
1807 process_browse_data(uri
, p
.type
, IPP_PRINTER_IDLE
, p
.location
,
1808 p
.info
, p
.make_model
, 0, NULL
);
1809 else if (strstr(uri
, "/classes/") != NULL
)
1810 process_browse_data(uri
, p
.type
| CUPS_PRINTER_CLASS
, IPP_PRINTER_IDLE
,
1811 p
.location
, p
.info
, p
.make_model
, 0, NULL
);
1815 * Free this listing...
1821 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdUpdateSLPBrowse() End...");
1823 #endif /* HAVE_LIBSLP */
1827 * 'dequote()' - Remote quotes from a string.
1830 static char * /* O - Dequoted string */
1831 dequote(char *d
, /* I - Destination string */
1832 const char *s
, /* I - Source string */
1833 int dlen
) /* I - Destination length */
1835 char *dptr
; /* Pointer into destination */
1840 for (dptr
= d
, dlen
--; *s
&& dlen
> 0; s
++)
1857 * 'process_browse_data()' - Process new browse data.
1861 process_browse_data(
1862 const char *uri
, /* I - URI of printer/class */
1863 cups_ptype_t type
, /* I - Printer type */
1864 ipp_pstate_t state
, /* I - Printer state */
1865 const char *location
, /* I - Printer location */
1866 const char *info
, /* I - Printer information */
1867 const char *make_model
, /* I - Printer make and model */
1868 int num_attrs
, /* I - Number of attributes */
1869 cups_option_t
*attrs
) /* I - Attributes */
1871 int update
; /* Update printer attributes? */
1872 char finaluri
[HTTP_MAX_URI
], /* Final URI for printer */
1873 method
[HTTP_MAX_URI
], /* Method portion of URI */
1874 username
[HTTP_MAX_URI
], /* Username portion of URI */
1875 host
[HTTP_MAX_URI
], /* Host portion of URI */
1876 resource
[HTTP_MAX_URI
]; /* Resource portion of URI */
1877 int port
; /* Port portion of URI */
1878 char name
[IPP_MAX_NAME
], /* Name of printer */
1879 *hptr
, /* Pointer into hostname */
1880 *sptr
; /* Pointer into ServerName */
1881 char local_make_model
[IPP_MAX_NAME
];
1882 /* Local make and model */
1883 cupsd_printer_t
*p
; /* Printer information */
1884 const char *ipp_options
; /* ipp-options value */
1888 * Pull the URI apart to see if this is a local or remote printer...
1891 httpSeparateURI(HTTP_URI_CODING_ALL
, uri
, method
, sizeof(method
), username
,
1892 sizeof(username
), host
, sizeof(host
), &port
, resource
,
1896 * Determine if the URI contains any illegal characters in it...
1899 if (strncmp(uri
, "ipp://", 6) || !host
[0] ||
1900 (strncmp(resource
, "/printers/", 10) &&
1901 strncmp(resource
, "/classes/", 9)))
1903 cupsdLogMessage(CUPSD_LOG_ERROR
,
1904 "process_browse_data: Bad printer URI in browse data: %s",
1909 if (strchr(resource
, '?') ||
1910 (!strncmp(resource
, "/printers/", 10) && strchr(resource
+ 10, '/')) ||
1911 (!strncmp(resource
, "/classes/", 9) && strchr(resource
+ 9, '/')))
1913 cupsdLogMessage(CUPSD_LOG_ERROR
,
1914 "process_browse_data: Bad resource in browse data: %s",
1920 * OK, this isn't a local printer; add any remote options...
1923 ipp_options
= cupsGetOption("ipp-options", num_attrs
, attrs
);
1925 if (BrowseRemoteOptions
)
1927 if (BrowseRemoteOptions
[0] == '?')
1930 * Override server-supplied options...
1933 snprintf(finaluri
, sizeof(finaluri
), "%s%s", uri
, BrowseRemoteOptions
);
1935 else if (ipp_options
)
1938 * Combine the server and local options...
1941 snprintf(finaluri
, sizeof(finaluri
), "%s?%s+%s", uri
, ipp_options
,
1942 BrowseRemoteOptions
);
1947 * Just use the local options...
1950 snprintf(finaluri
, sizeof(finaluri
), "%s?%s", uri
, BrowseRemoteOptions
);
1955 else if (ipp_options
)
1958 * Just use the server-supplied options...
1961 snprintf(finaluri
, sizeof(finaluri
), "%s?%s", uri
, ipp_options
);
1966 * See if we already have it listed in the Printers list, and add it if not...
1969 type
|= CUPS_PRINTER_REMOTE
;
1970 type
&= ~CUPS_PRINTER_IMPLICIT
;
1972 hptr
= strchr(host
, '.');
1973 sptr
= strchr(ServerName
, '.');
1975 if (sptr
!= NULL
&& hptr
!= NULL
)
1978 * Strip the common domain name components...
1981 while (hptr
!= NULL
)
1983 if (!strcasecmp(hptr
, sptr
))
1989 hptr
= strchr(hptr
+ 1, '.');
1993 if (type
& CUPS_PRINTER_CLASS
)
1996 * Remote destination is a class...
1999 if (!strncmp(resource
, "/classes/", 9))
2000 snprintf(name
, sizeof(name
), "%s@%s", resource
+ 9, host
);
2004 if ((p
= cupsdFindClass(name
)) == NULL
&& BrowseShortNames
)
2006 if ((p
= cupsdFindClass(resource
+ 9)) != NULL
)
2008 if (p
->hostname
&& strcasecmp(p
->hostname
, host
))
2011 * Nope, this isn't the same host; if the hostname isn't the local host,
2012 * add it to the other class and then find a class using the full host
2016 if (p
->type
& CUPS_PRINTER_REMOTE
)
2018 cupsdLogMessage(CUPSD_LOG_INFO
,
2019 "Renamed remote class \"%s\" to \"%s@%s\"...",
2020 p
->name
, p
->name
, p
->hostname
);
2021 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED
, p
, NULL
,
2022 "Class \'%s\' deleted by directory services.",
2025 cupsArrayRemove(Printers
, p
);
2026 cupsdSetStringf(&p
->name
, "%s@%s", p
->name
, p
->hostname
);
2027 cupsdSetPrinterAttrs(p
);
2028 cupsArrayAdd(Printers
, p
);
2030 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED
, p
, NULL
,
2031 "Class \'%s\' added by directory services.",
2037 else if (!p
->hostname
)
2040 * Hostname not set, so this must be a cached remote printer
2041 * that was created for a pending print job...
2044 cupsdSetString(&p
->hostname
, host
);
2045 cupsdSetString(&p
->uri
, uri
);
2046 cupsdSetString(&p
->device_uri
, uri
);
2053 * Use the short name for this shared class.
2056 strlcpy(name
, resource
+ 9, sizeof(name
));
2059 else if (p
&& !p
->hostname
)
2062 * Hostname not set, so this must be a cached remote printer
2063 * that was created for a pending print job...
2066 cupsdSetString(&p
->hostname
, host
);
2067 cupsdSetString(&p
->uri
, uri
);
2068 cupsdSetString(&p
->device_uri
, uri
);
2075 * Class doesn't exist; add it...
2078 p
= cupsdAddClass(name
);
2080 cupsdLogMessage(CUPSD_LOG_INFO
, "Added remote class \"%s\"...", name
);
2082 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED
, p
, NULL
,
2083 "Class \'%s\' added by directory services.", name
);
2086 * Force the URI to point to the real server...
2089 p
->type
= type
& ~CUPS_PRINTER_REJECTING
;
2091 cupsdSetString(&p
->uri
, uri
);
2092 cupsdSetString(&p
->device_uri
, uri
);
2093 cupsdSetString(&p
->hostname
, host
);
2101 * Remote destination is a printer...
2104 if (!strncmp(resource
, "/printers/", 10))
2105 snprintf(name
, sizeof(name
), "%s@%s", resource
+ 10, host
);
2109 if ((p
= cupsdFindPrinter(name
)) == NULL
&& BrowseShortNames
)
2111 if ((p
= cupsdFindPrinter(resource
+ 10)) != NULL
)
2113 if (p
->hostname
&& strcasecmp(p
->hostname
, host
))
2116 * Nope, this isn't the same host; if the hostname isn't the local host,
2117 * add it to the other printer and then find a printer using the full host
2121 if (p
->type
& CUPS_PRINTER_REMOTE
)
2123 cupsdLogMessage(CUPSD_LOG_INFO
,
2124 "Renamed remote printer \"%s\" to \"%s@%s\"...",
2125 p
->name
, p
->name
, p
->hostname
);
2126 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED
, p
, NULL
,
2127 "Printer \'%s\' deleted by directory services.",
2130 cupsArrayRemove(Printers
, p
);
2131 cupsdSetStringf(&p
->name
, "%s@%s", p
->name
, p
->hostname
);
2132 cupsdSetPrinterAttrs(p
);
2133 cupsArrayAdd(Printers
, p
);
2135 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED
, p
, NULL
,
2136 "Printer \'%s\' added by directory services.",
2142 else if (!p
->hostname
)
2145 * Hostname not set, so this must be a cached remote printer
2146 * that was created for a pending print job...
2149 cupsdSetString(&p
->hostname
, host
);
2150 cupsdSetString(&p
->uri
, uri
);
2151 cupsdSetString(&p
->device_uri
, uri
);
2158 * Use the short name for this shared printer.
2161 strlcpy(name
, resource
+ 10, sizeof(name
));
2164 else if (p
&& !p
->hostname
)
2167 * Hostname not set, so this must be a cached remote printer
2168 * that was created for a pending print job...
2171 cupsdSetString(&p
->hostname
, host
);
2172 cupsdSetString(&p
->uri
, uri
);
2173 cupsdSetString(&p
->device_uri
, uri
);
2180 * Printer doesn't exist; add it...
2183 p
= cupsdAddPrinter(name
);
2185 cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED
, p
, NULL
,
2186 "Printer \'%s\' added by directory services.", name
);
2188 cupsdLogMessage(CUPSD_LOG_INFO
, "Added remote printer \"%s\"...", name
);
2191 * Force the URI to point to the real server...
2194 p
->type
= type
& ~CUPS_PRINTER_REJECTING
;
2196 cupsdSetString(&p
->hostname
, host
);
2197 cupsdSetString(&p
->uri
, uri
);
2198 cupsdSetString(&p
->device_uri
, uri
);
2205 * Update the state...
2209 p
->browse_time
= time(NULL
);
2211 if (type
& CUPS_PRINTER_REJECTING
)
2213 type
&= ~CUPS_PRINTER_REJECTING
;
2221 else if (!p
->accepting
)
2227 if (p
->type
!= type
)
2233 if (location
&& (!p
->location
|| strcmp(p
->location
, location
)))
2235 cupsdSetString(&p
->location
, location
);
2239 if (info
&& (!p
->info
|| strcmp(p
->info
, info
)))
2241 cupsdSetString(&p
->info
, info
);
2245 if (!make_model
|| !make_model
[0])
2247 if (type
& CUPS_PRINTER_CLASS
)
2248 snprintf(local_make_model
, sizeof(local_make_model
),
2249 "Remote Class on %s", host
);
2251 snprintf(local_make_model
, sizeof(local_make_model
),
2252 "Remote Printer on %s", host
);
2255 snprintf(local_make_model
, sizeof(local_make_model
),
2256 "%s on %s", make_model
, host
);
2258 if (!p
->make_model
|| strcmp(p
->make_model
, local_make_model
))
2260 cupsdSetString(&p
->make_model
, local_make_model
);
2264 if (type
& CUPS_PRINTER_DELETE
)
2266 cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED
, p
, NULL
,
2267 "%s \'%s\' deleted by directory services.",
2268 (type
& CUPS_PRINTER_CLASS
) ? "Class" : "Printer", p
->name
);
2270 cupsdExpireSubscriptions(p
, NULL
);
2272 cupsdDeletePrinter(p
, 1);
2273 cupsdUpdateImplicitClasses();
2277 cupsdSetPrinterAttrs(p
);
2278 cupsdUpdateImplicitClasses();
2282 * See if we have a default printer... If not, make the first printer the
2286 if (DefaultPrinter
== NULL
&& Printers
!= NULL
&& UseNetworkDefault
)
2289 * Find the first network default printer and use it...
2292 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
2294 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
2295 if (p
->type
& CUPS_PRINTER_DEFAULT
)
2303 * Do auto-classing if needed...
2306 process_implicit_classes();
2309 * Update the printcap file...
2312 cupsdWritePrintcap();
2317 * 'process_implicit_classes()' - Create/update implicit classes as needed.
2321 process_implicit_classes(void)
2323 int i
; /* Looping var */
2324 int update
; /* Update printer attributes? */
2325 char name
[IPP_MAX_NAME
], /* Name of printer */
2326 *hptr
; /* Pointer into hostname */
2327 cupsd_printer_t
*p
, /* Printer information */
2328 *pclass
, /* Printer class */
2329 *first
; /* First printer in class */
2330 int offset
, /* Offset of name */
2331 len
; /* Length of name */
2334 if (!ImplicitClasses
|| !Printers
)
2338 * Loop through all available printers and create classes as needed...
2341 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
), len
= 0, offset
= 0,
2342 update
= 0, pclass
= NULL
, first
= NULL
;
2344 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
2347 * Skip implicit classes...
2350 if (p
->type
& CUPS_PRINTER_IMPLICIT
)
2357 * If len == 0, get the length of this printer name up to the "@"
2361 cupsArraySave(Printers
);
2364 !strncasecmp(p
->name
, name
+ offset
, len
) &&
2365 (p
->name
[len
] == '\0' || p
->name
[len
] == '@'))
2368 * We have more than one printer with the same name; see if
2369 * we have a class, and if this printer is a member...
2372 if (pclass
&& strcasecmp(pclass
->name
, name
))
2375 cupsdSetPrinterAttrs(pclass
);
2381 if (!pclass
&& (pclass
= cupsdFindDest(name
)) == NULL
)
2384 * Need to add the class...
2387 pclass
= cupsdAddPrinter(name
);
2388 cupsArrayAdd(ImplicitPrinters
, pclass
);
2390 pclass
->type
|= CUPS_PRINTER_IMPLICIT
;
2391 pclass
->accepting
= 1;
2392 pclass
->state
= IPP_PRINTER_IDLE
;
2394 cupsdSetString(&pclass
->location
, p
->location
);
2395 cupsdSetString(&pclass
->info
, p
->info
);
2399 cupsdLogMessage(CUPSD_LOG_INFO
, "Added implicit class \"%s\"...",
2405 for (i
= 0; i
< pclass
->num_printers
; i
++)
2406 if (pclass
->printers
[i
] == first
)
2409 if (i
>= pclass
->num_printers
)
2411 first
->in_implicit_class
= 1;
2412 cupsdAddPrinterToClass(pclass
, first
);
2418 for (i
= 0; i
< pclass
->num_printers
; i
++)
2419 if (pclass
->printers
[i
] == p
)
2422 if (i
>= pclass
->num_printers
)
2424 p
->in_implicit_class
= 1;
2425 cupsdAddPrinterToClass(pclass
, p
);
2432 * First time around; just get name length and mark it as first
2436 if ((hptr
= strchr(p
->name
, '@')) != NULL
)
2437 len
= hptr
- p
->name
;
2439 len
= strlen(p
->name
);
2441 strncpy(name
, p
->name
, len
);
2445 if ((first
= (hptr
? cupsdFindDest(name
) : p
)) != NULL
&&
2446 !(first
->type
& CUPS_PRINTER_IMPLICIT
))
2449 * Can't use same name as a local printer; add "Any" to the
2450 * front of the name, unless we have explicitly disabled
2451 * the "ImplicitAnyClasses"...
2454 if (ImplicitAnyClasses
&& len
< (sizeof(name
) - 4))
2457 * Add "Any" to the class name...
2460 strcpy(name
, "Any");
2461 strncpy(name
+ 3, p
->name
, len
);
2462 name
[len
+ 3] = '\0';
2468 * Don't create an implicit class if we have a local printer
2469 * with the same name...
2473 cupsArrayRestore(Printers
);
2481 cupsArrayRestore(Printers
);
2485 * Update the last printer class as needed...
2488 if (pclass
&& update
)
2489 cupsdSetPrinterAttrs(pclass
);
2495 * 'slp_attr_callback()' - SLP attribute callback
2498 static SLPBoolean
/* O - SLP_TRUE for success */
2500 SLPHandle hslp
, /* I - SLP handle */
2501 const char *attrlist
, /* I - Attribute list */
2502 SLPError errcode
, /* I - Parsing status for this attr */
2503 void *cookie
) /* I - Current printer */
2506 cupsd_printer_t
*p
= (cupsd_printer_t
*)cookie
;
2510 * Let the compiler know we won't be using these...
2516 * Bail if there was an error
2519 if (errcode
!= SLP_OK
)
2523 * Parse the attrlist to obtain things needed to build CUPS browse packet
2526 memset(p
, 0, sizeof(cupsd_printer_t
));
2528 p
->type
= CUPS_PRINTER_REMOTE
;
2530 if (slp_get_attr(attrlist
, "(printer-location=", &(p
->location
)))
2532 if (slp_get_attr(attrlist
, "(printer-info=", &(p
->info
)))
2534 if (slp_get_attr(attrlist
, "(printer-make-and-model=", &(p
->make_model
)))
2537 if (slp_get_attr(attrlist
, "(color-supported=", &tmp
))
2539 if (!strcasecmp(tmp
, "true"))
2540 p
->type
|= CUPS_PRINTER_COLOR
;
2542 if (slp_get_attr(attrlist
, "(finishings-supported=", &tmp
))
2544 if (strstr(tmp
, "staple"))
2545 p
->type
|= CUPS_PRINTER_STAPLE
;
2546 if (strstr(tmp
, "bind"))
2547 p
->type
|= CUPS_PRINTER_BIND
;
2548 if (strstr(tmp
, "punch"))
2549 p
->type
|= CUPS_PRINTER_PUNCH
;
2551 if (slp_get_attr(attrlist
, "(sides-supported=", &tmp
))
2553 if (strstr(tmp
,"two-sided"))
2554 p
->type
|= CUPS_PRINTER_DUPLEX
;
2556 cupsdClearString(&tmp
);
2563 * 'slp_dereg_printer()' - SLPDereg() the specified printer
2567 slp_dereg_printer(cupsd_printer_t
*p
) /* I - Printer */
2569 char srvurl
[HTTP_MAX_URI
]; /* Printer service URI */
2572 cupsdLogMessage(CUPSD_LOG_DEBUG
, "slp_dereg_printer: printer=\"%s\"", p
->name
);
2574 if (!(p
->type
& CUPS_PRINTER_REMOTE
))
2577 * Make the SLP service URL that conforms to the IANA
2578 * 'printer:' template.
2581 snprintf(srvurl
, sizeof(srvurl
), SLP_CUPS_SRVTYPE
":%s", p
->uri
);
2584 * Deregister the printer...
2587 SLPDereg(BrowseSLPHandle
, srvurl
, slp_reg_callback
, 0);
2593 * 'slp_get_attr()' - Get an attribute from an SLP registration.
2596 static int /* O - 0 on success */
2597 slp_get_attr(const char *attrlist
, /* I - Attribute list string */
2598 const char *tag
, /* I - Name of attribute */
2599 char **valbuf
) /* O - Value */
2601 char *ptr1
, /* Pointer into string */
2605 cupsdClearString(valbuf
);
2607 if ((ptr1
= strstr(attrlist
, tag
)) != NULL
)
2609 ptr1
+= strlen(tag
);
2611 if ((ptr2
= strchr(ptr1
,')')) != NULL
)
2617 *valbuf
= calloc(ptr2
- ptr1
+ 1, 1);
2618 strncpy(*valbuf
, ptr1
, ptr2
- ptr1
);
2621 * Dequote the value...
2624 for (ptr1
= *valbuf
; *ptr1
; ptr1
++)
2625 if (*ptr1
== '\\' && ptr1
[1])
2626 _cups_strcpy(ptr1
, ptr1
+ 1);
2637 * 'slp_reg_callback()' - Empty SLPRegReport.
2641 slp_reg_callback(SLPHandle hslp
, /* I - SLP handle */
2642 SLPError errcode
, /* I - Error code, if any */
2643 void *cookie
) /* I - App data */
2654 * 'slp_url_callback()' - SLP service url callback
2657 static SLPBoolean
/* O - TRUE = OK, FALSE = error */
2659 SLPHandle hslp
, /* I - SLP handle */
2660 const char *srvurl
, /* I - URL of service */
2661 unsigned short lifetime
, /* I - Life of service */
2662 SLPError errcode
, /* I - Existing error code */
2663 void *cookie
) /* I - Pointer to service list */
2665 slpsrvurl_t
*s
, /* New service entry */
2666 **head
; /* Pointer to head of entry */
2670 * Let the compiler know we won't be using these vars...
2677 * Bail if there was an error
2680 if (errcode
!= SLP_OK
)
2684 * Grab the head of the list...
2687 head
= (slpsrvurl_t
**)cookie
;
2690 * Allocate a *temporary* slpsrvurl_t to hold this entry.
2693 if ((s
= (slpsrvurl_t
*)calloc(1, sizeof(slpsrvurl_t
))) == NULL
)
2697 * Copy the SLP service URL...
2700 strlcpy(s
->url
, srvurl
, sizeof(s
->url
));
2703 * Link the SLP service URL into the head of the list
2713 #endif /* HAVE_LIBSLP */
2717 * End of "$Id: dirsvc.c 5043 2006-02-01 18:55:16Z mike $".