]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/http-support.c
_cupsEncodeOption should return the attribute that has been encoded.
[thirdparty/cups.git] / cups / http-support.c
1 /*
2 * HTTP support routines for CUPS.
3 *
4 * Copyright 2007-2017 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
8 */
9
10 /*
11 * Include necessary headers...
12 */
13
14 #include "cups-private.h"
15 #ifdef HAVE_DNSSD
16 # include <dns_sd.h>
17 # ifdef WIN32
18 # include <io.h>
19 # elif defined(HAVE_POLL)
20 # include <poll.h>
21 # else
22 # include <sys/select.h>
23 # endif /* WIN32 */
24 #elif defined(HAVE_AVAHI)
25 # include <avahi-client/client.h>
26 # include <avahi-client/lookup.h>
27 # include <avahi-common/simple-watch.h>
28 #endif /* HAVE_DNSSD */
29
30
31 /*
32 * Local types...
33 */
34
35 typedef struct _http_uribuf_s /* URI buffer */
36 {
37 #ifdef HAVE_AVAHI
38 AvahiSimplePoll *poll; /* Poll state */
39 #endif /* HAVE_AVAHI */
40 char *buffer; /* Pointer to buffer */
41 size_t bufsize; /* Size of buffer */
42 int options; /* Options passed to _httpResolveURI */
43 const char *resource; /* Resource from URI */
44 const char *uuid; /* UUID from URI */
45 } _http_uribuf_t;
46
47
48 /*
49 * Local globals...
50 */
51
52 static const char * const http_days[7] =/* Days of the week */
53 {
54 "Sun",
55 "Mon",
56 "Tue",
57 "Wed",
58 "Thu",
59 "Fri",
60 "Sat"
61 };
62 static const char * const http_months[12] =
63 { /* Months of the year */
64 "Jan",
65 "Feb",
66 "Mar",
67 "Apr",
68 "May",
69 "Jun",
70 "Jul",
71 "Aug",
72 "Sep",
73 "Oct",
74 "Nov",
75 "Dec"
76 };
77 static const char * const http_states[] =
78 { /* HTTP state strings */
79 "HTTP_STATE_ERROR",
80 "HTTP_STATE_WAITING",
81 "HTTP_STATE_OPTIONS",
82 "HTTP_STATE_GET",
83 "HTTP_STATE_GET_SEND",
84 "HTTP_STATE_HEAD",
85 "HTTP_STATE_POST",
86 "HTTP_STATE_POST_RECV",
87 "HTTP_STATE_POST_SEND",
88 "HTTP_STATE_PUT",
89 "HTTP_STATE_PUT_RECV",
90 "HTTP_STATE_DELETE",
91 "HTTP_STATE_TRACE",
92 "HTTP_STATE_CONNECT",
93 "HTTP_STATE_STATUS",
94 "HTTP_STATE_UNKNOWN_METHOD",
95 "HTTP_STATE_UNKNOWN_VERSION"
96 };
97
98
99 /*
100 * Local functions...
101 */
102
103 static const char *http_copy_decode(char *dst, const char *src,
104 int dstsize, const char *term,
105 int decode);
106 static char *http_copy_encode(char *dst, const char *src,
107 char *dstend, const char *reserved,
108 const char *term, int encode);
109 #ifdef HAVE_DNSSD
110 static void DNSSD_API http_resolve_cb(DNSServiceRef sdRef,
111 DNSServiceFlags flags,
112 uint32_t interfaceIndex,
113 DNSServiceErrorType errorCode,
114 const char *fullName,
115 const char *hostTarget,
116 uint16_t port, uint16_t txtLen,
117 const unsigned char *txtRecord,
118 void *context);
119 #endif /* HAVE_DNSSD */
120
121 #ifdef HAVE_AVAHI
122 static void http_client_cb(AvahiClient *client,
123 AvahiClientState state, void *simple_poll);
124 static int http_poll_cb(struct pollfd *pollfds, unsigned int num_pollfds,
125 int timeout, void *context);
126 static void http_resolve_cb(AvahiServiceResolver *resolver,
127 AvahiIfIndex interface,
128 AvahiProtocol protocol,
129 AvahiResolverEvent event,
130 const char *name, const char *type,
131 const char *domain, const char *host_name,
132 const AvahiAddress *address, uint16_t port,
133 AvahiStringList *txt,
134 AvahiLookupResultFlags flags, void *context);
135 #endif /* HAVE_AVAHI */
136
137
138 /*
139 * 'httpAssembleURI()' - Assemble a uniform resource identifier from its
140 * components.
141 *
142 * This function escapes reserved characters in the URI depending on the
143 * value of the "encoding" argument. You should use this function in
144 * place of traditional string functions whenever you need to create a
145 * URI string.
146 *
147 * @since CUPS 1.2/macOS 10.5@
148 */
149
150 http_uri_status_t /* O - URI status */
151 httpAssembleURI(
152 http_uri_coding_t encoding, /* I - Encoding flags */
153 char *uri, /* I - URI buffer */
154 int urilen, /* I - Size of URI buffer */
155 const char *scheme, /* I - Scheme name */
156 const char *username, /* I - Username */
157 const char *host, /* I - Hostname or address */
158 int port, /* I - Port number */
159 const char *resource) /* I - Resource */
160 {
161 char *ptr, /* Pointer into URI buffer */
162 *end; /* End of URI buffer */
163
164
165 /*
166 * Range check input...
167 */
168
169 if (!uri || urilen < 1 || !scheme || port < 0)
170 {
171 if (uri)
172 *uri = '\0';
173
174 return (HTTP_URI_STATUS_BAD_ARGUMENTS);
175 }
176
177 /*
178 * Assemble the URI starting with the scheme...
179 */
180
181 end = uri + urilen - 1;
182 ptr = http_copy_encode(uri, scheme, end, NULL, NULL, 0);
183
184 if (!ptr)
185 goto assemble_overflow;
186
187 if (!strcmp(scheme, "geo") || !strcmp(scheme, "mailto") || !strcmp(scheme, "tel"))
188 {
189 /*
190 * geo:, mailto:, and tel: only have :, no //...
191 */
192
193 if (ptr < end)
194 *ptr++ = ':';
195 else
196 goto assemble_overflow;
197 }
198 else
199 {
200 /*
201 * Schemes other than geo:, mailto:, and tel: typically have //...
202 */
203
204 if ((ptr + 2) < end)
205 {
206 *ptr++ = ':';
207 *ptr++ = '/';
208 *ptr++ = '/';
209 }
210 else
211 goto assemble_overflow;
212 }
213
214 /*
215 * Next the username and hostname, if any...
216 */
217
218 if (host)
219 {
220 const char *hostptr; /* Pointer into hostname */
221 int have_ipv6; /* Do we have an IPv6 address? */
222
223 if (username && *username)
224 {
225 /*
226 * Add username@ first...
227 */
228
229 ptr = http_copy_encode(ptr, username, end, "/?#[]@", NULL,
230 encoding & HTTP_URI_CODING_USERNAME);
231
232 if (!ptr)
233 goto assemble_overflow;
234
235 if (ptr < end)
236 *ptr++ = '@';
237 else
238 goto assemble_overflow;
239 }
240
241 /*
242 * Then add the hostname. Since IPv6 is a particular pain to deal
243 * with, we have several special cases to deal with. If we get
244 * an IPv6 address with brackets around it, assume it is already in
245 * URI format. Since DNS-SD service names can sometimes look like
246 * raw IPv6 addresses, we specifically look for "._tcp" in the name,
247 * too...
248 */
249
250 for (hostptr = host,
251 have_ipv6 = strchr(host, ':') && !strstr(host, "._tcp");
252 *hostptr && have_ipv6;
253 hostptr ++)
254 if (*hostptr != ':' && !isxdigit(*hostptr & 255))
255 {
256 have_ipv6 = *hostptr == '%';
257 break;
258 }
259
260 if (have_ipv6)
261 {
262 /*
263 * We have a raw IPv6 address...
264 */
265
266 if (strchr(host, '%') && !(encoding & HTTP_URI_CODING_RFC6874))
267 {
268 /*
269 * We have a link-local address, add "[v1." prefix...
270 */
271
272 if ((ptr + 4) < end)
273 {
274 *ptr++ = '[';
275 *ptr++ = 'v';
276 *ptr++ = '1';
277 *ptr++ = '.';
278 }
279 else
280 goto assemble_overflow;
281 }
282 else
283 {
284 /*
285 * We have a normal (or RFC 6874 link-local) address, add "[" prefix...
286 */
287
288 if (ptr < end)
289 *ptr++ = '[';
290 else
291 goto assemble_overflow;
292 }
293
294 /*
295 * Copy the rest of the IPv6 address, and terminate with "]".
296 */
297
298 while (ptr < end && *host)
299 {
300 if (*host == '%')
301 {
302 /*
303 * Convert/encode zone separator
304 */
305
306 if (encoding & HTTP_URI_CODING_RFC6874)
307 {
308 if (ptr >= (end - 2))
309 goto assemble_overflow;
310
311 *ptr++ = '%';
312 *ptr++ = '2';
313 *ptr++ = '5';
314 }
315 else
316 *ptr++ = '+';
317
318 host ++;
319 }
320 else
321 *ptr++ = *host++;
322 }
323
324 if (*host)
325 goto assemble_overflow;
326
327 if (ptr < end)
328 *ptr++ = ']';
329 else
330 goto assemble_overflow;
331 }
332 else
333 {
334 /*
335 * Otherwise, just copy the host string (the extra chars are not in the
336 * "reg-name" ABNF rule; anything <= SP or >= DEL plus % gets automatically
337 * percent-encoded.
338 */
339
340 ptr = http_copy_encode(ptr, host, end, "\"#/:<>?@[\\]^`{|}", NULL,
341 encoding & HTTP_URI_CODING_HOSTNAME);
342
343 if (!ptr)
344 goto assemble_overflow;
345 }
346
347 /*
348 * Finish things off with the port number...
349 */
350
351 if (port > 0)
352 {
353 snprintf(ptr, (size_t)(end - ptr + 1), ":%d", port);
354 ptr += strlen(ptr);
355
356 if (ptr >= end)
357 goto assemble_overflow;
358 }
359 }
360
361 /*
362 * Last but not least, add the resource string...
363 */
364
365 if (resource)
366 {
367 char *query; /* Pointer to query string */
368
369
370 /*
371 * Copy the resource string up to the query string if present...
372 */
373
374 query = strchr(resource, '?');
375 ptr = http_copy_encode(ptr, resource, end, NULL, "?",
376 encoding & HTTP_URI_CODING_RESOURCE);
377 if (!ptr)
378 goto assemble_overflow;
379
380 if (query)
381 {
382 /*
383 * Copy query string without encoding...
384 */
385
386 ptr = http_copy_encode(ptr, query, end, NULL, NULL,
387 encoding & HTTP_URI_CODING_QUERY);
388 if (!ptr)
389 goto assemble_overflow;
390 }
391 }
392 else if (ptr < end)
393 *ptr++ = '/';
394 else
395 goto assemble_overflow;
396
397 /*
398 * Nul-terminate the URI buffer and return with no errors...
399 */
400
401 *ptr = '\0';
402
403 return (HTTP_URI_STATUS_OK);
404
405 /*
406 * Clear the URI string and return an overflow error; I don't usually
407 * like goto's, but in this case it makes sense...
408 */
409
410 assemble_overflow:
411
412 *uri = '\0';
413 return (HTTP_URI_STATUS_OVERFLOW);
414 }
415
416
417 /*
418 * 'httpAssembleURIf()' - Assemble a uniform resource identifier from its
419 * components with a formatted resource.
420 *
421 * This function creates a formatted version of the resource string
422 * argument "resourcef" and escapes reserved characters in the URI
423 * depending on the value of the "encoding" argument. You should use
424 * this function in place of traditional string functions whenever
425 * you need to create a URI string.
426 *
427 * @since CUPS 1.2/macOS 10.5@
428 */
429
430 http_uri_status_t /* O - URI status */
431 httpAssembleURIf(
432 http_uri_coding_t encoding, /* I - Encoding flags */
433 char *uri, /* I - URI buffer */
434 int urilen, /* I - Size of URI buffer */
435 const char *scheme, /* I - Scheme name */
436 const char *username, /* I - Username */
437 const char *host, /* I - Hostname or address */
438 int port, /* I - Port number */
439 const char *resourcef, /* I - Printf-style resource */
440 ...) /* I - Additional arguments as needed */
441 {
442 va_list ap; /* Pointer to additional arguments */
443 char resource[1024]; /* Formatted resource string */
444 int bytes; /* Bytes in formatted string */
445
446
447 /*
448 * Range check input...
449 */
450
451 if (!uri || urilen < 1 || !scheme || port < 0 || !resourcef)
452 {
453 if (uri)
454 *uri = '\0';
455
456 return (HTTP_URI_STATUS_BAD_ARGUMENTS);
457 }
458
459 /*
460 * Format the resource string and assemble the URI...
461 */
462
463 va_start(ap, resourcef);
464 bytes = vsnprintf(resource, sizeof(resource), resourcef, ap);
465 va_end(ap);
466
467 if ((size_t)bytes >= sizeof(resource))
468 {
469 *uri = '\0';
470 return (HTTP_URI_STATUS_OVERFLOW);
471 }
472 else
473 return (httpAssembleURI(encoding, uri, urilen, scheme, username, host,
474 port, resource));
475 }
476
477
478 /*
479 * 'httpAssembleUUID()' - Assemble a name-based UUID URN conforming to RFC 4122.
480 *
481 * This function creates a unique 128-bit identifying number using the server
482 * name, port number, random data, and optionally an object name and/or object
483 * number. The result is formatted as a UUID URN as defined in RFC 4122.
484 *
485 * The buffer needs to be at least 46 bytes in size.
486 *
487 * @since CUPS 1.7/macOS 10.9@
488 */
489
490 char * /* I - UUID string */
491 httpAssembleUUID(const char *server, /* I - Server name */
492 int port, /* I - Port number */
493 const char *name, /* I - Object name or NULL */
494 int number, /* I - Object number or 0 */
495 char *buffer, /* I - String buffer */
496 size_t bufsize) /* I - Size of buffer */
497 {
498 char data[1024]; /* Source string for MD5 */
499 unsigned char md5sum[16]; /* MD5 digest/sum */
500
501
502 /*
503 * Build a version 3 UUID conforming to RFC 4122.
504 *
505 * Start with the MD5 sum of the server, port, object name and
506 * number, and some random data on the end.
507 */
508
509 snprintf(data, sizeof(data), "%s:%d:%s:%d:%04x:%04x", server,
510 port, name ? name : server, number,
511 (unsigned)CUPS_RAND() & 0xffff, (unsigned)CUPS_RAND() & 0xffff);
512
513 cupsHashData("md5", (unsigned char *)data, strlen(data), md5sum, sizeof(md5sum));
514
515 /*
516 * Generate the UUID from the MD5...
517 */
518
519 snprintf(buffer, bufsize,
520 "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
521 "%02x%02x%02x%02x%02x%02x",
522 md5sum[0], md5sum[1], md5sum[2], md5sum[3], md5sum[4], md5sum[5],
523 (md5sum[6] & 15) | 0x30, md5sum[7], (md5sum[8] & 0x3f) | 0x40,
524 md5sum[9], md5sum[10], md5sum[11], md5sum[12], md5sum[13],
525 md5sum[14], md5sum[15]);
526
527 return (buffer);
528 }
529
530
531 /*
532 * 'httpDecode64()' - Base64-decode a string.
533 *
534 * This function is deprecated. Use the httpDecode64_2() function instead
535 * which provides buffer length arguments.
536 *
537 * @deprecated@ @exclude all@
538 */
539
540 char * /* O - Decoded string */
541 httpDecode64(char *out, /* I - String to write to */
542 const char *in) /* I - String to read from */
543 {
544 int outlen; /* Output buffer length */
545
546
547 /*
548 * Use the old maximum buffer size for binary compatibility...
549 */
550
551 outlen = 512;
552
553 return (httpDecode64_2(out, &outlen, in));
554 }
555
556
557 /*
558 * 'httpDecode64_2()' - Base64-decode a string.
559 *
560 * The caller must initialize "outlen" to the maximum size of the decoded
561 * string before calling @code httpDecode64_2@. On return "outlen" contains the
562 * decoded length of the string.
563 *
564 * @since CUPS 1.1.21/macOS 10.4@
565 */
566
567 char * /* O - Decoded string */
568 httpDecode64_2(char *out, /* I - String to write to */
569 int *outlen, /* IO - Size of output string */
570 const char *in) /* I - String to read from */
571 {
572 int pos; /* Bit position */
573 unsigned base64; /* Value of this character */
574 char *outptr, /* Output pointer */
575 *outend; /* End of output buffer */
576
577
578 /*
579 * Range check input...
580 */
581
582 if (!out || !outlen || *outlen < 1 || !in)
583 return (NULL);
584
585 if (!*in)
586 {
587 *out = '\0';
588 *outlen = 0;
589
590 return (out);
591 }
592
593 /*
594 * Convert from base-64 to bytes...
595 */
596
597 for (outptr = out, outend = out + *outlen - 1, pos = 0; *in != '\0'; in ++)
598 {
599 /*
600 * Decode this character into a number from 0 to 63...
601 */
602
603 if (*in >= 'A' && *in <= 'Z')
604 base64 = (unsigned)(*in - 'A');
605 else if (*in >= 'a' && *in <= 'z')
606 base64 = (unsigned)(*in - 'a' + 26);
607 else if (*in >= '0' && *in <= '9')
608 base64 = (unsigned)(*in - '0' + 52);
609 else if (*in == '+')
610 base64 = 62;
611 else if (*in == '/')
612 base64 = 63;
613 else if (*in == '=')
614 break;
615 else
616 continue;
617
618 /*
619 * Store the result in the appropriate chars...
620 */
621
622 switch (pos)
623 {
624 case 0 :
625 if (outptr < outend)
626 *outptr = (char)(base64 << 2);
627 pos ++;
628 break;
629 case 1 :
630 if (outptr < outend)
631 *outptr++ |= (char)((base64 >> 4) & 3);
632 if (outptr < outend)
633 *outptr = (char)((base64 << 4) & 255);
634 pos ++;
635 break;
636 case 2 :
637 if (outptr < outend)
638 *outptr++ |= (char)((base64 >> 2) & 15);
639 if (outptr < outend)
640 *outptr = (char)((base64 << 6) & 255);
641 pos ++;
642 break;
643 case 3 :
644 if (outptr < outend)
645 *outptr++ |= (char)base64;
646 pos = 0;
647 break;
648 }
649 }
650
651 *outptr = '\0';
652
653 /*
654 * Return the decoded string and size...
655 */
656
657 *outlen = (int)(outptr - out);
658
659 return (out);
660 }
661
662
663 /*
664 * 'httpEncode64()' - Base64-encode a string.
665 *
666 * This function is deprecated. Use the httpEncode64_2() function instead
667 * which provides buffer length arguments.
668 *
669 * @deprecated@ @exclude all@
670 */
671
672 char * /* O - Encoded string */
673 httpEncode64(char *out, /* I - String to write to */
674 const char *in) /* I - String to read from */
675 {
676 return (httpEncode64_2(out, 512, in, (int)strlen(in)));
677 }
678
679
680 /*
681 * 'httpEncode64_2()' - Base64-encode a string.
682 *
683 * @since CUPS 1.1.21/macOS 10.4@
684 */
685
686 char * /* O - Encoded string */
687 httpEncode64_2(char *out, /* I - String to write to */
688 int outlen, /* I - Maximum size of output string */
689 const char *in, /* I - String to read from */
690 int inlen) /* I - Size of input string */
691 {
692 char *outptr, /* Output pointer */
693 *outend; /* End of output buffer */
694 static const char base64[] = /* Base64 characters... */
695 {
696 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
697 "abcdefghijklmnopqrstuvwxyz"
698 "0123456789"
699 "+/"
700 };
701
702
703 /*
704 * Range check input...
705 */
706
707 if (!out || outlen < 1 || !in)
708 return (NULL);
709
710 /*
711 * Convert bytes to base-64...
712 */
713
714 for (outptr = out, outend = out + outlen - 1; inlen > 0; in ++, inlen --)
715 {
716 /*
717 * Encode the up to 3 characters as 4 Base64 numbers...
718 */
719
720 if (outptr < outend)
721 *outptr ++ = base64[(in[0] & 255) >> 2];
722
723 if (outptr < outend)
724 {
725 if (inlen > 1)
726 *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
727 else
728 *outptr ++ = base64[((in[0] & 255) << 4) & 63];
729 }
730
731 in ++;
732 inlen --;
733 if (inlen <= 0)
734 {
735 if (outptr < outend)
736 *outptr ++ = '=';
737 if (outptr < outend)
738 *outptr ++ = '=';
739 break;
740 }
741
742 if (outptr < outend)
743 {
744 if (inlen > 1)
745 *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
746 else
747 *outptr ++ = base64[((in[0] & 255) << 2) & 63];
748 }
749
750 in ++;
751 inlen --;
752 if (inlen <= 0)
753 {
754 if (outptr < outend)
755 *outptr ++ = '=';
756 break;
757 }
758
759 if (outptr < outend)
760 *outptr ++ = base64[in[0] & 63];
761 }
762
763 *outptr = '\0';
764
765 /*
766 * Return the encoded string...
767 */
768
769 return (out);
770 }
771
772
773 /*
774 * 'httpGetDateString()' - Get a formatted date/time string from a time value.
775 *
776 * @deprecated@ @exclude all@
777 */
778
779 const char * /* O - Date/time string */
780 httpGetDateString(time_t t) /* I - Time in seconds */
781 {
782 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
783
784
785 return (httpGetDateString2(t, cg->http_date, sizeof(cg->http_date)));
786 }
787
788
789 /*
790 * 'httpGetDateString2()' - Get a formatted date/time string from a time value.
791 *
792 * @since CUPS 1.2/macOS 10.5@
793 */
794
795 const char * /* O - Date/time string */
796 httpGetDateString2(time_t t, /* I - Time in seconds */
797 char *s, /* I - String buffer */
798 int slen) /* I - Size of string buffer */
799 {
800 struct tm *tdate; /* UNIX date/time data */
801
802
803 tdate = gmtime(&t);
804 if (tdate)
805 snprintf(s, (size_t)slen, "%s, %02d %s %d %02d:%02d:%02d GMT", http_days[tdate->tm_wday], tdate->tm_mday, http_months[tdate->tm_mon], tdate->tm_year + 1900, tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
806 else
807 s[0] = '\0';
808
809 return (s);
810 }
811
812
813 /*
814 * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
815 */
816
817 time_t /* O - Time in seconds */
818 httpGetDateTime(const char *s) /* I - Date/time string */
819 {
820 int i; /* Looping var */
821 char mon[16]; /* Abbreviated month name */
822 int day, year; /* Day of month and year */
823 int hour, min, sec; /* Time */
824 int days; /* Number of days since 1970 */
825 static const int normal_days[] = /* Days to a month, normal years */
826 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
827 static const int leap_days[] = /* Days to a month, leap years */
828 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
829
830
831 DEBUG_printf(("2httpGetDateTime(s=\"%s\")", s));
832
833 /*
834 * Extract the date and time from the formatted string...
835 */
836
837 if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
838 return (0);
839
840 DEBUG_printf(("4httpGetDateTime: day=%d, mon=\"%s\", year=%d, hour=%d, "
841 "min=%d, sec=%d", day, mon, year, hour, min, sec));
842
843 /*
844 * Convert the month name to a number from 0 to 11.
845 */
846
847 for (i = 0; i < 12; i ++)
848 if (!_cups_strcasecmp(mon, http_months[i]))
849 break;
850
851 if (i >= 12)
852 return (0);
853
854 DEBUG_printf(("4httpGetDateTime: i=%d", i));
855
856 /*
857 * Now convert the date and time to a UNIX time value in seconds since
858 * 1970. We can't use mktime() since the timezone may not be UTC but
859 * the date/time string *is* UTC.
860 */
861
862 if ((year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0))
863 days = leap_days[i] + day - 1;
864 else
865 days = normal_days[i] + day - 1;
866
867 DEBUG_printf(("4httpGetDateTime: days=%d", days));
868
869 days += (year - 1970) * 365 + /* 365 days per year (normally) */
870 ((year - 1) / 4 - 492) - /* + leap days */
871 ((year - 1) / 100 - 19) + /* - 100 year days */
872 ((year - 1) / 400 - 4); /* + 400 year days */
873
874 DEBUG_printf(("4httpGetDateTime: days=%d\n", days));
875
876 return (days * 86400 + hour * 3600 + min * 60 + sec);
877 }
878
879
880 /*
881 * 'httpSeparate()' - Separate a Universal Resource Identifier into its
882 * components.
883 *
884 * This function is deprecated; use the httpSeparateURI() function instead.
885 *
886 * @deprecated@ @exclude all@
887 */
888
889 void
890 httpSeparate(const char *uri, /* I - Universal Resource Identifier */
891 char *scheme, /* O - Scheme [32] (http, https, etc.) */
892 char *username, /* O - Username [1024] */
893 char *host, /* O - Hostname [1024] */
894 int *port, /* O - Port number to use */
895 char *resource) /* O - Resource/filename [1024] */
896 {
897 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username,
898 HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource,
899 HTTP_MAX_URI);
900 }
901
902
903 /*
904 * 'httpSeparate2()' - Separate a Universal Resource Identifier into its
905 * components.
906 *
907 * This function is deprecated; use the httpSeparateURI() function instead.
908 *
909 * @since CUPS 1.1.21/macOS 10.4@
910 * @deprecated@ @exclude all@
911 */
912
913 void
914 httpSeparate2(const char *uri, /* I - Universal Resource Identifier */
915 char *scheme, /* O - Scheme (http, https, etc.) */
916 int schemelen, /* I - Size of scheme buffer */
917 char *username, /* O - Username */
918 int usernamelen, /* I - Size of username buffer */
919 char *host, /* O - Hostname */
920 int hostlen, /* I - Size of hostname buffer */
921 int *port, /* O - Port number to use */
922 char *resource, /* O - Resource/filename */
923 int resourcelen) /* I - Size of resource buffer */
924 {
925 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username,
926 usernamelen, host, hostlen, port, resource, resourcelen);
927 }
928
929
930 /*
931 * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its
932 * components.
933 *
934 * @since CUPS 1.2/macOS 10.5@
935 */
936
937 http_uri_status_t /* O - Result of separation */
938 httpSeparateURI(
939 http_uri_coding_t decoding, /* I - Decoding flags */
940 const char *uri, /* I - Universal Resource Identifier */
941 char *scheme, /* O - Scheme (http, https, etc.) */
942 int schemelen, /* I - Size of scheme buffer */
943 char *username, /* O - Username */
944 int usernamelen, /* I - Size of username buffer */
945 char *host, /* O - Hostname */
946 int hostlen, /* I - Size of hostname buffer */
947 int *port, /* O - Port number to use */
948 char *resource, /* O - Resource/filename */
949 int resourcelen) /* I - Size of resource buffer */
950 {
951 char *ptr, /* Pointer into string... */
952 *end; /* End of string */
953 const char *sep; /* Separator character */
954 http_uri_status_t status; /* Result of separation */
955
956
957 /*
958 * Initialize everything to blank...
959 */
960
961 if (scheme && schemelen > 0)
962 *scheme = '\0';
963
964 if (username && usernamelen > 0)
965 *username = '\0';
966
967 if (host && hostlen > 0)
968 *host = '\0';
969
970 if (port)
971 *port = 0;
972
973 if (resource && resourcelen > 0)
974 *resource = '\0';
975
976 /*
977 * Range check input...
978 */
979
980 if (!uri || !port || !scheme || schemelen <= 0 || !username ||
981 usernamelen <= 0 || !host || hostlen <= 0 || !resource ||
982 resourcelen <= 0)
983 return (HTTP_URI_STATUS_BAD_ARGUMENTS);
984
985 if (!*uri)
986 return (HTTP_URI_STATUS_BAD_URI);
987
988 /*
989 * Grab the scheme portion of the URI...
990 */
991
992 status = HTTP_URI_STATUS_OK;
993
994 if (!strncmp(uri, "//", 2))
995 {
996 /*
997 * Workaround for HP IPP client bug...
998 */
999
1000 strlcpy(scheme, "ipp", (size_t)schemelen);
1001 status = HTTP_URI_STATUS_MISSING_SCHEME;
1002 }
1003 else if (*uri == '/')
1004 {
1005 /*
1006 * Filename...
1007 */
1008
1009 strlcpy(scheme, "file", (size_t)schemelen);
1010 status = HTTP_URI_STATUS_MISSING_SCHEME;
1011 }
1012 else
1013 {
1014 /*
1015 * Standard URI with scheme...
1016 */
1017
1018 for (ptr = scheme, end = scheme + schemelen - 1;
1019 *uri && *uri != ':' && ptr < end;)
1020 if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1021 "abcdefghijklmnopqrstuvwxyz"
1022 "0123456789-+.", *uri) != NULL)
1023 *ptr++ = *uri++;
1024 else
1025 break;
1026
1027 *ptr = '\0';
1028
1029 if (*uri != ':')
1030 {
1031 *scheme = '\0';
1032 return (HTTP_URI_STATUS_BAD_SCHEME);
1033 }
1034
1035 uri ++;
1036 }
1037
1038 /*
1039 * Set the default port number...
1040 */
1041
1042 if (!strcmp(scheme, "http"))
1043 *port = 80;
1044 else if (!strcmp(scheme, "https"))
1045 *port = 443;
1046 else if (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps"))
1047 *port = 631;
1048 else if (!_cups_strcasecmp(scheme, "lpd"))
1049 *port = 515;
1050 else if (!strcmp(scheme, "socket")) /* Not yet registered with IANA... */
1051 *port = 9100;
1052 else if (strcmp(scheme, "file") && strcmp(scheme, "mailto") && strcmp(scheme, "tel"))
1053 status = HTTP_URI_STATUS_UNKNOWN_SCHEME;
1054
1055 /*
1056 * Now see if we have a hostname...
1057 */
1058
1059 if (!strncmp(uri, "//", 2))
1060 {
1061 /*
1062 * Yes, extract it...
1063 */
1064
1065 uri += 2;
1066
1067 /*
1068 * Grab the username, if any...
1069 */
1070
1071 if ((sep = strpbrk(uri, "@/")) != NULL && *sep == '@')
1072 {
1073 /*
1074 * Get a username:password combo...
1075 */
1076
1077 uri = http_copy_decode(username, uri, usernamelen, "@",
1078 decoding & HTTP_URI_CODING_USERNAME);
1079
1080 if (!uri)
1081 {
1082 *username = '\0';
1083 return (HTTP_URI_STATUS_BAD_USERNAME);
1084 }
1085
1086 uri ++;
1087 }
1088
1089 /*
1090 * Then the hostname/IP address...
1091 */
1092
1093 if (*uri == '[')
1094 {
1095 /*
1096 * Grab IPv6 address...
1097 */
1098
1099 uri ++;
1100 if (*uri == 'v')
1101 {
1102 /*
1103 * Skip IPvFuture ("vXXXX.") prefix...
1104 */
1105
1106 uri ++;
1107
1108 while (isxdigit(*uri & 255))
1109 uri ++;
1110
1111 if (*uri != '.')
1112 {
1113 *host = '\0';
1114 return (HTTP_URI_STATUS_BAD_HOSTNAME);
1115 }
1116
1117 uri ++;
1118 }
1119
1120 uri = http_copy_decode(host, uri, hostlen, "]",
1121 decoding & HTTP_URI_CODING_HOSTNAME);
1122
1123 if (!uri)
1124 {
1125 *host = '\0';
1126 return (HTTP_URI_STATUS_BAD_HOSTNAME);
1127 }
1128
1129 /*
1130 * Validate value...
1131 */
1132
1133 if (*uri != ']')
1134 {
1135 *host = '\0';
1136 return (HTTP_URI_STATUS_BAD_HOSTNAME);
1137 }
1138
1139 uri ++;
1140
1141 for (ptr = host; *ptr; ptr ++)
1142 if (*ptr == '+')
1143 {
1144 /*
1145 * Convert zone separator to % and stop here...
1146 */
1147
1148 *ptr = '%';
1149 break;
1150 }
1151 else if (*ptr == '%')
1152 {
1153 /*
1154 * Stop at zone separator (RFC 6874)
1155 */
1156
1157 break;
1158 }
1159 else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255))
1160 {
1161 *host = '\0';
1162 return (HTTP_URI_STATUS_BAD_HOSTNAME);
1163 }
1164 }
1165 else
1166 {
1167 /*
1168 * Validate the hostname or IPv4 address first...
1169 */
1170
1171 for (ptr = (char *)uri; *ptr; ptr ++)
1172 if (strchr(":?/", *ptr))
1173 break;
1174 else if (!strchr("abcdefghijklmnopqrstuvwxyz" /* unreserved */
1175 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* unreserved */
1176 "0123456789" /* unreserved */
1177 "-._~" /* unreserved */
1178 "%" /* pct-encoded */
1179 "!$&'()*+,;=" /* sub-delims */
1180 "\\", *ptr)) /* SMB domain */
1181 {
1182 *host = '\0';
1183 return (HTTP_URI_STATUS_BAD_HOSTNAME);
1184 }
1185
1186 /*
1187 * Then copy the hostname or IPv4 address to the buffer...
1188 */
1189
1190 uri = http_copy_decode(host, uri, hostlen, ":?/",
1191 decoding & HTTP_URI_CODING_HOSTNAME);
1192
1193 if (!uri)
1194 {
1195 *host = '\0';
1196 return (HTTP_URI_STATUS_BAD_HOSTNAME);
1197 }
1198 }
1199
1200 /*
1201 * Validate hostname for file scheme - only empty and localhost are
1202 * acceptable.
1203 */
1204
1205 if (!strcmp(scheme, "file") && strcmp(host, "localhost") && host[0])
1206 {
1207 *host = '\0';
1208 return (HTTP_URI_STATUS_BAD_HOSTNAME);
1209 }
1210
1211 /*
1212 * See if we have a port number...
1213 */
1214
1215 if (*uri == ':')
1216 {
1217 /*
1218 * Yes, collect the port number...
1219 */
1220
1221 if (!isdigit(uri[1] & 255))
1222 {
1223 *port = 0;
1224 return (HTTP_URI_STATUS_BAD_PORT);
1225 }
1226
1227 *port = (int)strtol(uri + 1, (char **)&uri, 10);
1228
1229 if (*port <= 0 || *port > 65535)
1230 {
1231 *port = 0;
1232 return (HTTP_URI_STATUS_BAD_PORT);
1233 }
1234
1235 if (*uri != '/' && *uri)
1236 {
1237 *port = 0;
1238 return (HTTP_URI_STATUS_BAD_PORT);
1239 }
1240 }
1241 }
1242
1243 /*
1244 * The remaining portion is the resource string...
1245 */
1246
1247 if (*uri == '?' || !*uri)
1248 {
1249 /*
1250 * Hostname but no path...
1251 */
1252
1253 status = HTTP_URI_STATUS_MISSING_RESOURCE;
1254 *resource = '/';
1255
1256 /*
1257 * Copy any query string...
1258 */
1259
1260 if (*uri == '?')
1261 uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL,
1262 decoding & HTTP_URI_CODING_QUERY);
1263 else
1264 resource[1] = '\0';
1265 }
1266 else
1267 {
1268 uri = http_copy_decode(resource, uri, resourcelen, "?",
1269 decoding & HTTP_URI_CODING_RESOURCE);
1270
1271 if (uri && *uri == '?')
1272 {
1273 /*
1274 * Concatenate any query string...
1275 */
1276
1277 char *resptr = resource + strlen(resource);
1278
1279 uri = http_copy_decode(resptr, uri,
1280 resourcelen - (int)(resptr - resource), NULL,
1281 decoding & HTTP_URI_CODING_QUERY);
1282 }
1283 }
1284
1285 if (!uri)
1286 {
1287 *resource = '\0';
1288 return (HTTP_URI_STATUS_BAD_RESOURCE);
1289 }
1290
1291 /*
1292 * Return the URI separation status...
1293 */
1294
1295 return (status);
1296 }
1297
1298
1299 /*
1300 * 'httpStateString()' - Return the string describing a HTTP state value.
1301 *
1302 * @since CUPS 2.0/OS 10.10@
1303 */
1304
1305 const char * /* O - State string */
1306 httpStateString(http_state_t state) /* I - HTTP state value */
1307 {
1308 if (state < HTTP_STATE_ERROR || state > HTTP_STATE_UNKNOWN_VERSION)
1309 return ("HTTP_STATE_???");
1310 else
1311 return (http_states[state - HTTP_STATE_ERROR]);
1312 }
1313
1314
1315 /*
1316 * '_httpStatus()' - Return the localized string describing a HTTP status code.
1317 *
1318 * The returned string is localized using the passed message catalog.
1319 */
1320
1321 const char * /* O - Localized status string */
1322 _httpStatus(cups_lang_t *lang, /* I - Language */
1323 http_status_t status) /* I - HTTP status code */
1324 {
1325 const char *s; /* Status string */
1326
1327
1328 switch (status)
1329 {
1330 case HTTP_STATUS_ERROR :
1331 s = strerror(errno);
1332 break;
1333 case HTTP_STATUS_CONTINUE :
1334 s = _("Continue");
1335 break;
1336 case HTTP_STATUS_SWITCHING_PROTOCOLS :
1337 s = _("Switching Protocols");
1338 break;
1339 case HTTP_STATUS_OK :
1340 s = _("OK");
1341 break;
1342 case HTTP_STATUS_CREATED :
1343 s = _("Created");
1344 break;
1345 case HTTP_STATUS_ACCEPTED :
1346 s = _("Accepted");
1347 break;
1348 case HTTP_STATUS_NO_CONTENT :
1349 s = _("No Content");
1350 break;
1351 case HTTP_STATUS_MOVED_PERMANENTLY :
1352 s = _("Moved Permanently");
1353 break;
1354 case HTTP_STATUS_FOUND :
1355 s = _("Found");
1356 break;
1357 case HTTP_STATUS_SEE_OTHER :
1358 s = _("See Other");
1359 break;
1360 case HTTP_STATUS_NOT_MODIFIED :
1361 s = _("Not Modified");
1362 break;
1363 case HTTP_STATUS_BAD_REQUEST :
1364 s = _("Bad Request");
1365 break;
1366 case HTTP_STATUS_UNAUTHORIZED :
1367 case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED :
1368 s = _("Unauthorized");
1369 break;
1370 case HTTP_STATUS_FORBIDDEN :
1371 s = _("Forbidden");
1372 break;
1373 case HTTP_STATUS_NOT_FOUND :
1374 s = _("Not Found");
1375 break;
1376 case HTTP_STATUS_REQUEST_TOO_LARGE :
1377 s = _("Request Entity Too Large");
1378 break;
1379 case HTTP_STATUS_URI_TOO_LONG :
1380 s = _("URI Too Long");
1381 break;
1382 case HTTP_STATUS_UPGRADE_REQUIRED :
1383 s = _("Upgrade Required");
1384 break;
1385 case HTTP_STATUS_NOT_IMPLEMENTED :
1386 s = _("Not Implemented");
1387 break;
1388 case HTTP_STATUS_NOT_SUPPORTED :
1389 s = _("Not Supported");
1390 break;
1391 case HTTP_STATUS_EXPECTATION_FAILED :
1392 s = _("Expectation Failed");
1393 break;
1394 case HTTP_STATUS_SERVICE_UNAVAILABLE :
1395 s = _("Service Unavailable");
1396 break;
1397 case HTTP_STATUS_SERVER_ERROR :
1398 s = _("Internal Server Error");
1399 break;
1400 case HTTP_STATUS_CUPS_PKI_ERROR :
1401 s = _("SSL/TLS Negotiation Error");
1402 break;
1403 case HTTP_STATUS_CUPS_WEBIF_DISABLED :
1404 s = _("Web Interface is Disabled");
1405 break;
1406
1407 default :
1408 s = _("Unknown");
1409 break;
1410 }
1411
1412 return (_cupsLangString(lang, s));
1413 }
1414
1415
1416 /*
1417 * 'httpStatus()' - Return a short string describing a HTTP status code.
1418 *
1419 * The returned string is localized to the current POSIX locale and is based
1420 * on the status strings defined in RFC 7231.
1421 */
1422
1423 const char * /* O - Localized status string */
1424 httpStatus(http_status_t status) /* I - HTTP status code */
1425 {
1426 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
1427
1428
1429 if (!cg->lang_default)
1430 cg->lang_default = cupsLangDefault();
1431
1432 return (_httpStatus(cg->lang_default, status));
1433 }
1434
1435 /*
1436 * 'httpURIStatusString()' - Return a string describing a URI status code.
1437 *
1438 * @since CUPS 2.0/OS 10.10@
1439 */
1440
1441 const char * /* O - Localized status string */
1442 httpURIStatusString(
1443 http_uri_status_t status) /* I - URI status code */
1444 {
1445 const char *s; /* Status string */
1446 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
1447
1448
1449 if (!cg->lang_default)
1450 cg->lang_default = cupsLangDefault();
1451
1452 switch (status)
1453 {
1454 case HTTP_URI_STATUS_OVERFLOW :
1455 s = _("URI too large");
1456 break;
1457 case HTTP_URI_STATUS_BAD_ARGUMENTS :
1458 s = _("Bad arguments to function");
1459 break;
1460 case HTTP_URI_STATUS_BAD_RESOURCE :
1461 s = _("Bad resource in URI");
1462 break;
1463 case HTTP_URI_STATUS_BAD_PORT :
1464 s = _("Bad port number in URI");
1465 break;
1466 case HTTP_URI_STATUS_BAD_HOSTNAME :
1467 s = _("Bad hostname/address in URI");
1468 break;
1469 case HTTP_URI_STATUS_BAD_USERNAME :
1470 s = _("Bad username in URI");
1471 break;
1472 case HTTP_URI_STATUS_BAD_SCHEME :
1473 s = _("Bad scheme in URI");
1474 break;
1475 case HTTP_URI_STATUS_BAD_URI :
1476 s = _("Bad/empty URI");
1477 break;
1478 case HTTP_URI_STATUS_OK :
1479 s = _("OK");
1480 break;
1481 case HTTP_URI_STATUS_MISSING_SCHEME :
1482 s = _("Missing scheme in URI");
1483 break;
1484 case HTTP_URI_STATUS_UNKNOWN_SCHEME :
1485 s = _("Unknown scheme in URI");
1486 break;
1487 case HTTP_URI_STATUS_MISSING_RESOURCE :
1488 s = _("Missing resource in URI");
1489 break;
1490
1491 default:
1492 s = _("Unknown");
1493 break;
1494 }
1495
1496 return (_cupsLangString(cg->lang_default, s));
1497 }
1498
1499
1500 #ifndef HAVE_HSTRERROR
1501 /*
1502 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others.
1503 */
1504
1505 const char * /* O - Error string */
1506 _cups_hstrerror(int error) /* I - Error number */
1507 {
1508 static const char * const errors[] = /* Error strings */
1509 {
1510 "OK",
1511 "Host not found.",
1512 "Try again.",
1513 "Unrecoverable lookup error.",
1514 "No data associated with name."
1515 };
1516
1517
1518 if (error < 0 || error > 4)
1519 return ("Unknown hostname lookup error.");
1520 else
1521 return (errors[error]);
1522 }
1523 #endif /* !HAVE_HSTRERROR */
1524
1525
1526 /*
1527 * '_httpDecodeURI()' - Percent-decode a HTTP request URI.
1528 */
1529
1530 char * /* O - Decoded URI or NULL on error */
1531 _httpDecodeURI(char *dst, /* I - Destination buffer */
1532 const char *src, /* I - Source URI */
1533 size_t dstsize) /* I - Size of destination buffer */
1534 {
1535 if (http_copy_decode(dst, src, (int)dstsize, NULL, 1))
1536 return (dst);
1537 else
1538 return (NULL);
1539 }
1540
1541
1542 /*
1543 * '_httpEncodeURI()' - Percent-encode a HTTP request URI.
1544 */
1545
1546 char * /* O - Encoded URI */
1547 _httpEncodeURI(char *dst, /* I - Destination buffer */
1548 const char *src, /* I - Source URI */
1549 size_t dstsize) /* I - Size of destination buffer */
1550 {
1551 http_copy_encode(dst, src, dst + dstsize - 1, NULL, NULL, 1);
1552 return (dst);
1553 }
1554
1555
1556 /*
1557 * '_httpResolveURI()' - Resolve a DNS-SD URI.
1558 */
1559
1560 const char * /* O - Resolved URI */
1561 _httpResolveURI(
1562 const char *uri, /* I - DNS-SD URI */
1563 char *resolved_uri, /* I - Buffer for resolved URI */
1564 size_t resolved_size, /* I - Size of URI buffer */
1565 int options, /* I - Resolve options */
1566 int (*cb)(void *context), /* I - Continue callback function */
1567 void *context) /* I - Context pointer for callback */
1568 {
1569 char scheme[32], /* URI components... */
1570 userpass[256],
1571 hostname[1024],
1572 resource[1024];
1573 int port;
1574 #ifdef DEBUG
1575 http_uri_status_t status; /* URI decode status */
1576 #endif /* DEBUG */
1577
1578
1579 DEBUG_printf(("_httpResolveURI(uri=\"%s\", resolved_uri=%p, resolved_size=" CUPS_LLFMT ", options=0x%x, cb=%p, context=%p)", uri, (void *)resolved_uri, CUPS_LLCAST resolved_size, options, (void *)cb, context));
1580
1581 /*
1582 * Get the device URI...
1583 */
1584
1585 #ifdef DEBUG
1586 if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1587 sizeof(scheme), userpass, sizeof(userpass),
1588 hostname, sizeof(hostname), &port, resource,
1589 sizeof(resource))) < HTTP_URI_STATUS_OK)
1590 #else
1591 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1592 sizeof(scheme), userpass, sizeof(userpass),
1593 hostname, sizeof(hostname), &port, resource,
1594 sizeof(resource)) < HTTP_URI_STATUS_OK)
1595 #endif /* DEBUG */
1596 {
1597 if (options & _HTTP_RESOLVE_STDERR)
1598 _cupsLangPrintFilter(stderr, "ERROR", _("Bad device-uri \"%s\"."), uri);
1599
1600 DEBUG_printf(("2_httpResolveURI: httpSeparateURI returned %d!", status));
1601 DEBUG_puts("2_httpResolveURI: Returning NULL");
1602 return (NULL);
1603 }
1604
1605 /*
1606 * Resolve it as needed...
1607 */
1608
1609 if (strstr(hostname, "._tcp"))
1610 {
1611 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1612 char *regtype, /* Pointer to type in hostname */
1613 *domain, /* Pointer to domain in hostname */
1614 *uuid, /* Pointer to UUID in URI */
1615 *uuidend; /* Pointer to end of UUID in URI */
1616 _http_uribuf_t uribuf; /* URI buffer */
1617 int offline = 0; /* offline-report state set? */
1618 # ifdef HAVE_DNSSD
1619 # ifdef WIN32
1620 # pragma comment(lib, "dnssd.lib")
1621 # endif /* WIN32 */
1622 DNSServiceRef ref, /* DNS-SD master service reference */
1623 domainref = NULL,/* DNS-SD service reference for domain */
1624 ippref = NULL, /* DNS-SD service reference for network IPP */
1625 ippsref = NULL, /* DNS-SD service reference for network IPPS */
1626 localref; /* DNS-SD service reference for .local */
1627 int extrasent = 0; /* Send the domain/IPP/IPPS resolves? */
1628 # ifdef HAVE_POLL
1629 struct pollfd polldata; /* Polling data */
1630 # else /* select() */
1631 fd_set input_set; /* Input set for select() */
1632 struct timeval stimeout; /* Timeout value for select() */
1633 # endif /* HAVE_POLL */
1634 # elif defined(HAVE_AVAHI)
1635 AvahiClient *client; /* Client information */
1636 int error; /* Status */
1637 # endif /* HAVE_DNSSD */
1638
1639 if (options & _HTTP_RESOLVE_STDERR)
1640 fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname);
1641
1642 /*
1643 * Separate the hostname into service name, registration type, and domain...
1644 */
1645
1646 for (regtype = strstr(hostname, "._tcp") - 2;
1647 regtype > hostname;
1648 regtype --)
1649 if (regtype[0] == '.' && regtype[1] == '_')
1650 {
1651 /*
1652 * Found ._servicetype in front of ._tcp...
1653 */
1654
1655 *regtype++ = '\0';
1656 break;
1657 }
1658
1659 if (regtype <= hostname)
1660 {
1661 DEBUG_puts("2_httpResolveURI: Bad hostname, returning NULL");
1662 return (NULL);
1663 }
1664
1665 for (domain = strchr(regtype, '.');
1666 domain;
1667 domain = strchr(domain + 1, '.'))
1668 if (domain[1] != '_')
1669 break;
1670
1671 if (domain)
1672 *domain++ = '\0';
1673
1674 if ((uuid = strstr(resource, "?uuid=")) != NULL)
1675 {
1676 *uuid = '\0';
1677 uuid += 6;
1678 if ((uuidend = strchr(uuid, '&')) != NULL)
1679 *uuidend = '\0';
1680 }
1681
1682 resolved_uri[0] = '\0';
1683
1684 uribuf.buffer = resolved_uri;
1685 uribuf.bufsize = resolved_size;
1686 uribuf.options = options;
1687 uribuf.resource = resource;
1688 uribuf.uuid = uuid;
1689
1690 DEBUG_printf(("2_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", "
1691 "domain=\"%s\"\n", hostname, regtype, domain));
1692 if (options & _HTTP_RESOLVE_STDERR)
1693 {
1694 fputs("STATE: +connecting-to-device\n", stderr);
1695 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1696 "domain=\"local.\"...\n", hostname, regtype);
1697 }
1698
1699 uri = NULL;
1700
1701 # ifdef HAVE_DNSSD
1702 if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError)
1703 {
1704 uint32_t myinterface = kDNSServiceInterfaceIndexAny;
1705 /* Lookup on any interface */
1706
1707 if (!strcmp(scheme, "ippusb"))
1708 myinterface = kDNSServiceInterfaceIndexLocalOnly;
1709
1710 localref = ref;
1711 if (DNSServiceResolve(&localref,
1712 kDNSServiceFlagsShareConnection, myinterface,
1713 hostname, regtype, "local.", http_resolve_cb,
1714 &uribuf) == kDNSServiceErr_NoError)
1715 {
1716 int fds; /* Number of ready descriptors */
1717 time_t timeout, /* Poll timeout */
1718 start_time = time(NULL),/* Start time */
1719 end_time = start_time + 90;
1720 /* End time */
1721
1722 while (time(NULL) < end_time)
1723 {
1724 if (options & _HTTP_RESOLVE_STDERR)
1725 _cupsLangPrintFilter(stderr, "INFO", _("Looking for printer."));
1726
1727 if (cb && !(*cb)(context))
1728 {
1729 DEBUG_puts("2_httpResolveURI: callback returned 0 (stop)");
1730 break;
1731 }
1732
1733 /*
1734 * Wakeup every 2 seconds to emit a "looking for printer" message...
1735 */
1736
1737 if ((timeout = end_time - time(NULL)) > 2)
1738 timeout = 2;
1739
1740 # ifdef HAVE_POLL
1741 polldata.fd = DNSServiceRefSockFD(ref);
1742 polldata.events = POLLIN;
1743
1744 fds = poll(&polldata, 1, (int)(1000 * timeout));
1745
1746 # else /* select() */
1747 FD_ZERO(&input_set);
1748 FD_SET(DNSServiceRefSockFD(ref), &input_set);
1749
1750 # ifdef WIN32
1751 stimeout.tv_sec = (long)timeout;
1752 # else
1753 stimeout.tv_sec = timeout;
1754 # endif /* WIN32 */
1755 stimeout.tv_usec = 0;
1756
1757 fds = select(DNSServiceRefSockFD(ref)+1, &input_set, NULL, NULL,
1758 &stimeout);
1759 # endif /* HAVE_POLL */
1760
1761 if (fds < 0)
1762 {
1763 if (errno != EINTR && errno != EAGAIN)
1764 {
1765 DEBUG_printf(("2_httpResolveURI: poll error: %s", strerror(errno)));
1766 break;
1767 }
1768 }
1769 else if (fds == 0)
1770 {
1771 /*
1772 * Wait 2 seconds for a response to the local resolve; if nothing
1773 * comes in, do an additional domain resolution...
1774 */
1775
1776 if (extrasent == 0 && domain && _cups_strcasecmp(domain, "local."))
1777 {
1778 if (options & _HTTP_RESOLVE_STDERR)
1779 fprintf(stderr,
1780 "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1781 "domain=\"%s\"...\n", hostname, regtype,
1782 domain ? domain : "");
1783
1784 domainref = ref;
1785 if (DNSServiceResolve(&domainref,
1786 kDNSServiceFlagsShareConnection,
1787 myinterface, hostname, regtype, domain,
1788 http_resolve_cb,
1789 &uribuf) == kDNSServiceErr_NoError)
1790 extrasent = 1;
1791 }
1792 else if (extrasent == 0 && !strcmp(scheme, "ippusb"))
1793 {
1794 if (options & _HTTP_RESOLVE_STDERR)
1795 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"_ipps._tcp\", domain=\"local.\"...\n", hostname);
1796
1797 ippsref = ref;
1798 if (DNSServiceResolve(&ippsref,
1799 kDNSServiceFlagsShareConnection,
1800 kDNSServiceInterfaceIndexAny, hostname,
1801 "_ipps._tcp", domain, http_resolve_cb,
1802 &uribuf) == kDNSServiceErr_NoError)
1803 extrasent = 1;
1804 }
1805 else if (extrasent == 1 && !strcmp(scheme, "ippusb"))
1806 {
1807 if (options & _HTTP_RESOLVE_STDERR)
1808 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"_ipp._tcp\", domain=\"local.\"...\n", hostname);
1809
1810 ippref = ref;
1811 if (DNSServiceResolve(&ippref,
1812 kDNSServiceFlagsShareConnection,
1813 kDNSServiceInterfaceIndexAny, hostname,
1814 "_ipp._tcp", domain, http_resolve_cb,
1815 &uribuf) == kDNSServiceErr_NoError)
1816 extrasent = 2;
1817 }
1818
1819 /*
1820 * If it hasn't resolved within 5 seconds set the offline-report
1821 * printer-state-reason...
1822 */
1823
1824 if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 &&
1825 time(NULL) > (start_time + 5))
1826 {
1827 fputs("STATE: +offline-report\n", stderr);
1828 offline = 1;
1829 }
1830 }
1831 else
1832 {
1833 if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError &&
1834 resolved_uri[0])
1835 {
1836 uri = resolved_uri;
1837 break;
1838 }
1839 }
1840 }
1841
1842 if (extrasent)
1843 {
1844 if (domainref)
1845 DNSServiceRefDeallocate(domainref);
1846 if (ippref)
1847 DNSServiceRefDeallocate(ippref);
1848 if (ippsref)
1849 DNSServiceRefDeallocate(ippsref);
1850 }
1851
1852 DNSServiceRefDeallocate(localref);
1853 }
1854
1855 DNSServiceRefDeallocate(ref);
1856 }
1857 # else /* HAVE_AVAHI */
1858 if ((uribuf.poll = avahi_simple_poll_new()) != NULL)
1859 {
1860 avahi_simple_poll_set_func(uribuf.poll, http_poll_cb, NULL);
1861
1862 if ((client = avahi_client_new(avahi_simple_poll_get(uribuf.poll),
1863 0, http_client_cb,
1864 &uribuf, &error)) != NULL)
1865 {
1866 if (avahi_service_resolver_new(client, AVAHI_IF_UNSPEC,
1867 AVAHI_PROTO_UNSPEC, hostname,
1868 regtype, "local.", AVAHI_PROTO_UNSPEC, 0,
1869 http_resolve_cb, &uribuf) != NULL)
1870 {
1871 time_t start_time = time(NULL),
1872 /* Start time */
1873 end_time = start_time + 90;
1874 /* End time */
1875 int pstatus; /* Poll status */
1876
1877 pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000);
1878
1879 if (pstatus == 0 && !resolved_uri[0] && domain &&
1880 _cups_strcasecmp(domain, "local."))
1881 {
1882 /*
1883 * Resolve for .local hasn't returned anything, try the listed
1884 * domain...
1885 */
1886
1887 avahi_service_resolver_new(client, AVAHI_IF_UNSPEC,
1888 AVAHI_PROTO_UNSPEC, hostname,
1889 regtype, domain, AVAHI_PROTO_UNSPEC, 0,
1890 http_resolve_cb, &uribuf);
1891 }
1892
1893 while (!pstatus && !resolved_uri[0] && time(NULL) < end_time)
1894 {
1895 if ((pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000)) != 0)
1896 break;
1897
1898 /*
1899 * If it hasn't resolved within 5 seconds set the offline-report
1900 * printer-state-reason...
1901 */
1902
1903 if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 &&
1904 time(NULL) > (start_time + 5))
1905 {
1906 fputs("STATE: +offline-report\n", stderr);
1907 offline = 1;
1908 }
1909 }
1910
1911 /*
1912 * Collect the result (if we got one).
1913 */
1914
1915 if (resolved_uri[0])
1916 uri = resolved_uri;
1917 }
1918
1919 avahi_client_free(client);
1920 }
1921
1922 avahi_simple_poll_free(uribuf.poll);
1923 }
1924 # endif /* HAVE_DNSSD */
1925
1926 if (options & _HTTP_RESOLVE_STDERR)
1927 {
1928 if (uri)
1929 {
1930 fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri);
1931 fputs("STATE: -connecting-to-device,offline-report\n", stderr);
1932 }
1933 else
1934 {
1935 fputs("DEBUG: Unable to resolve URI\n", stderr);
1936 fputs("STATE: -connecting-to-device\n", stderr);
1937 }
1938 }
1939
1940 #else /* HAVE_DNSSD || HAVE_AVAHI */
1941 /*
1942 * No DNS-SD support...
1943 */
1944
1945 uri = NULL;
1946 #endif /* HAVE_DNSSD || HAVE_AVAHI */
1947
1948 if ((options & _HTTP_RESOLVE_STDERR) && !uri)
1949 _cupsLangPrintFilter(stderr, "INFO", _("Unable to find printer."));
1950 }
1951 else
1952 {
1953 /*
1954 * Nothing more to do...
1955 */
1956
1957 strlcpy(resolved_uri, uri, resolved_size);
1958 uri = resolved_uri;
1959 }
1960
1961 DEBUG_printf(("2_httpResolveURI: Returning \"%s\"", uri));
1962
1963 return (uri);
1964 }
1965
1966
1967 #ifdef HAVE_AVAHI
1968 /*
1969 * 'http_client_cb()' - Client callback for resolving URI.
1970 */
1971
1972 static void
1973 http_client_cb(
1974 AvahiClient *client, /* I - Client information */
1975 AvahiClientState state, /* I - Current state */
1976 void *context) /* I - Pointer to URI buffer */
1977 {
1978 DEBUG_printf(("7http_client_cb(client=%p, state=%d, context=%p)", client,
1979 state, context));
1980
1981 /*
1982 * If the connection drops, quit.
1983 */
1984
1985 if (state == AVAHI_CLIENT_FAILURE)
1986 {
1987 _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
1988 /* URI buffer */
1989
1990 avahi_simple_poll_quit(uribuf->poll);
1991 }
1992 }
1993 #endif /* HAVE_AVAHI */
1994
1995
1996 /*
1997 * 'http_copy_decode()' - Copy and decode a URI.
1998 */
1999
2000 static const char * /* O - New source pointer or NULL on error */
2001 http_copy_decode(char *dst, /* O - Destination buffer */
2002 const char *src, /* I - Source pointer */
2003 int dstsize, /* I - Destination size */
2004 const char *term, /* I - Terminating characters */
2005 int decode) /* I - Decode %-encoded values */
2006 {
2007 char *ptr, /* Pointer into buffer */
2008 *end; /* End of buffer */
2009 int quoted; /* Quoted character */
2010
2011
2012 /*
2013 * Copy the src to the destination until we hit a terminating character
2014 * or the end of the string.
2015 */
2016
2017 for (ptr = dst, end = dst + dstsize - 1;
2018 *src && (!term || !strchr(term, *src));
2019 src ++)
2020 if (ptr < end)
2021 {
2022 if (*src == '%' && decode)
2023 {
2024 if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255))
2025 {
2026 /*
2027 * Grab a hex-encoded character...
2028 */
2029
2030 src ++;
2031 if (isalpha(*src))
2032 quoted = (tolower(*src) - 'a' + 10) << 4;
2033 else
2034 quoted = (*src - '0') << 4;
2035
2036 src ++;
2037 if (isalpha(*src))
2038 quoted |= tolower(*src) - 'a' + 10;
2039 else
2040 quoted |= *src - '0';
2041
2042 *ptr++ = (char)quoted;
2043 }
2044 else
2045 {
2046 /*
2047 * Bad hex-encoded character...
2048 */
2049
2050 *ptr = '\0';
2051 return (NULL);
2052 }
2053 }
2054 else if ((*src & 255) <= 0x20 || (*src & 255) >= 0x7f)
2055 {
2056 *ptr = '\0';
2057 return (NULL);
2058 }
2059 else
2060 *ptr++ = *src;
2061 }
2062
2063 *ptr = '\0';
2064
2065 return (src);
2066 }
2067
2068
2069 /*
2070 * 'http_copy_encode()' - Copy and encode a URI.
2071 */
2072
2073 static char * /* O - End of current URI */
2074 http_copy_encode(char *dst, /* O - Destination buffer */
2075 const char *src, /* I - Source pointer */
2076 char *dstend, /* I - End of destination buffer */
2077 const char *reserved, /* I - Extra reserved characters */
2078 const char *term, /* I - Terminating characters */
2079 int encode) /* I - %-encode reserved chars? */
2080 {
2081 static const char hex[] = "0123456789ABCDEF";
2082
2083
2084 while (*src && dst < dstend)
2085 {
2086 if (term && *src == *term)
2087 return (dst);
2088
2089 if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
2090 (reserved && strchr(reserved, *src))))
2091 {
2092 /*
2093 * Hex encode reserved characters...
2094 */
2095
2096 if ((dst + 2) >= dstend)
2097 break;
2098
2099 *dst++ = '%';
2100 *dst++ = hex[(*src >> 4) & 15];
2101 *dst++ = hex[*src & 15];
2102
2103 src ++;
2104 }
2105 else
2106 *dst++ = *src++;
2107 }
2108
2109 *dst = '\0';
2110
2111 if (*src)
2112 return (NULL);
2113 else
2114 return (dst);
2115 }
2116
2117
2118 #ifdef HAVE_DNSSD
2119 /*
2120 * 'http_resolve_cb()' - Build a device URI for the given service name.
2121 */
2122
2123 static void DNSSD_API
2124 http_resolve_cb(
2125 DNSServiceRef sdRef, /* I - Service reference */
2126 DNSServiceFlags flags, /* I - Results flags */
2127 uint32_t interfaceIndex, /* I - Interface number */
2128 DNSServiceErrorType errorCode, /* I - Error, if any */
2129 const char *fullName, /* I - Full service name */
2130 const char *hostTarget, /* I - Hostname */
2131 uint16_t port, /* I - Port number */
2132 uint16_t txtLen, /* I - Length of TXT record */
2133 const unsigned char *txtRecord, /* I - TXT record data */
2134 void *context) /* I - Pointer to URI buffer */
2135 {
2136 _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
2137 /* URI buffer */
2138 const char *scheme, /* URI scheme */
2139 *hostptr, /* Pointer into hostTarget */
2140 *reskey, /* "rp" or "rfo" */
2141 *resdefault; /* Default path */
2142 char resource[257], /* Remote path */
2143 fqdn[256]; /* FQDN of the .local name */
2144 const void *value; /* Value from TXT record */
2145 uint8_t valueLen; /* Length of value */
2146
2147
2148 DEBUG_printf(("4http_resolve_cb(sdRef=%p, flags=%x, interfaceIndex=%u, errorCode=%d, fullName=\"%s\", hostTarget=\"%s\", port=%u, txtLen=%u, txtRecord=%p, context=%p)", (void *)sdRef, flags, interfaceIndex, errorCode, fullName, hostTarget, port, txtLen, (void *)txtRecord, context));
2149
2150 /*
2151 * If we have a UUID, compare it...
2152 */
2153
2154 if (uribuf->uuid &&
2155 (value = TXTRecordGetValuePtr(txtLen, txtRecord, "UUID",
2156 &valueLen)) != NULL)
2157 {
2158 char uuid[256]; /* UUID value */
2159
2160 memcpy(uuid, value, valueLen);
2161 uuid[valueLen] = '\0';
2162
2163 if (_cups_strcasecmp(uuid, uribuf->uuid))
2164 {
2165 if (uribuf->options & _HTTP_RESOLVE_STDERR)
2166 fprintf(stderr, "DEBUG: Found UUID %s, looking for %s.", uuid,
2167 uribuf->uuid);
2168
2169 DEBUG_printf(("5http_resolve_cb: Found UUID %s, looking for %s.", uuid,
2170 uribuf->uuid));
2171 return;
2172 }
2173 }
2174
2175 /*
2176 * Figure out the scheme from the full name...
2177 */
2178
2179 if (strstr(fullName, "._ipps") || strstr(fullName, "._ipp-tls"))
2180 scheme = "ipps";
2181 else if (strstr(fullName, "._ipp") || strstr(fullName, "._fax-ipp"))
2182 scheme = "ipp";
2183 else if (strstr(fullName, "._http."))
2184 scheme = "http";
2185 else if (strstr(fullName, "._https."))
2186 scheme = "https";
2187 else if (strstr(fullName, "._printer."))
2188 scheme = "lpd";
2189 else if (strstr(fullName, "._pdl-datastream."))
2190 scheme = "socket";
2191 else
2192 scheme = "riousbprint";
2193
2194 /*
2195 * Extract the "remote printer" key from the TXT record...
2196 */
2197
2198 if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) &&
2199 (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
2200 !TXTRecordGetValuePtr(txtLen, txtRecord, "printer-type", &valueLen))
2201 {
2202 reskey = "rfo";
2203 resdefault = "/ipp/faxout";
2204 }
2205 else
2206 {
2207 reskey = "rp";
2208 resdefault = "/";
2209 }
2210
2211 if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, reskey,
2212 &valueLen)) != NULL)
2213 {
2214 if (((char *)value)[0] == '/')
2215 {
2216 /*
2217 * Value (incorrectly) has a leading slash already...
2218 */
2219
2220 memcpy(resource, value, valueLen);
2221 resource[valueLen] = '\0';
2222 }
2223 else
2224 {
2225 /*
2226 * Convert to resource by concatenating with a leading "/"...
2227 */
2228
2229 resource[0] = '/';
2230 memcpy(resource + 1, value, valueLen);
2231 resource[valueLen + 1] = '\0';
2232 }
2233 }
2234 else
2235 {
2236 /*
2237 * Use the default value...
2238 */
2239
2240 strlcpy(resource, resdefault, sizeof(resource));
2241 }
2242
2243 /*
2244 * Lookup the FQDN if needed...
2245 */
2246
2247 if ((uribuf->options & _HTTP_RESOLVE_FQDN) &&
2248 (hostptr = hostTarget + strlen(hostTarget) - 7) > hostTarget &&
2249 !_cups_strcasecmp(hostptr, ".local."))
2250 {
2251 /*
2252 * OK, we got a .local name but the caller needs a real domain. Start by
2253 * getting the IP address of the .local name and then do reverse-lookups...
2254 */
2255
2256 http_addrlist_t *addrlist, /* List of addresses */
2257 *addr; /* Current address */
2258
2259 DEBUG_printf(("5http_resolve_cb: Looking up \"%s\".", hostTarget));
2260
2261 snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port));
2262 if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL)
2263 {
2264 for (addr = addrlist; addr; addr = addr->next)
2265 {
2266 int error = getnameinfo(&(addr->addr.addr), (socklen_t)httpAddrLength(&(addr->addr)), fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD);
2267
2268 if (!error)
2269 {
2270 DEBUG_printf(("5http_resolve_cb: Found \"%s\".", fqdn));
2271
2272 if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn ||
2273 _cups_strcasecmp(hostptr, ".local"))
2274 {
2275 hostTarget = fqdn;
2276 break;
2277 }
2278 }
2279 #ifdef DEBUG
2280 else
2281 DEBUG_printf(("5http_resolve_cb: \"%s\" did not resolve: %d",
2282 httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)),
2283 error));
2284 #endif /* DEBUG */
2285 }
2286
2287 httpAddrFreeList(addrlist);
2288 }
2289 }
2290
2291 /*
2292 * Assemble the final device URI...
2293 */
2294
2295 if ((!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
2296 !strcmp(uribuf->resource, "/cups"))
2297 httpAssembleURIf(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme, NULL, hostTarget, ntohs(port), "%s?snmp=false", resource);
2298 else
2299 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme, NULL, hostTarget, ntohs(port), resource);
2300
2301 DEBUG_printf(("5http_resolve_cb: Resolved URI is \"%s\"...", uribuf->buffer));
2302 }
2303
2304 #elif defined(HAVE_AVAHI)
2305 /*
2306 * 'http_poll_cb()' - Wait for input on the specified file descriptors.
2307 *
2308 * Note: This function is needed because avahi_simple_poll_iterate is broken
2309 * and always uses a timeout of 0 (!) milliseconds.
2310 * (Avahi Ticket #364)
2311 *
2312 * @private@
2313 */
2314
2315 static int /* O - Number of file descriptors matching */
2316 http_poll_cb(
2317 struct pollfd *pollfds, /* I - File descriptors */
2318 unsigned int num_pollfds, /* I - Number of file descriptors */
2319 int timeout, /* I - Timeout in milliseconds (used) */
2320 void *context) /* I - User data (unused) */
2321 {
2322 (void)timeout;
2323 (void)context;
2324
2325 return (poll(pollfds, num_pollfds, 2000));
2326 }
2327
2328
2329 /*
2330 * 'http_resolve_cb()' - Build a device URI for the given service name.
2331 */
2332
2333 static void
2334 http_resolve_cb(
2335 AvahiServiceResolver *resolver, /* I - Resolver (unused) */
2336 AvahiIfIndex interface, /* I - Interface index (unused) */
2337 AvahiProtocol protocol, /* I - Network protocol (unused) */
2338 AvahiResolverEvent event, /* I - Event (found, etc.) */
2339 const char *name, /* I - Service name */
2340 const char *type, /* I - Registration type */
2341 const char *domain, /* I - Domain (unused) */
2342 const char *hostTarget, /* I - Hostname */
2343 const AvahiAddress *address, /* I - Address (unused) */
2344 uint16_t port, /* I - Port number */
2345 AvahiStringList *txt, /* I - TXT record */
2346 AvahiLookupResultFlags flags, /* I - Lookup flags (unused) */
2347 void *context) /* I - Pointer to URI buffer */
2348 {
2349 _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
2350 /* URI buffer */
2351 const char *scheme, /* URI scheme */
2352 *hostptr, /* Pointer into hostTarget */
2353 *reskey, /* "rp" or "rfo" */
2354 *resdefault; /* Default path */
2355 char resource[257], /* Remote path */
2356 fqdn[256]; /* FQDN of the .local name */
2357 AvahiStringList *pair; /* Current TXT record key/value pair */
2358 char *value; /* Value for "rp" key */
2359 size_t valueLen = 0; /* Length of "rp" key */
2360
2361
2362 DEBUG_printf(("4http_resolve_cb(resolver=%p, "
2363 "interface=%d, protocol=%d, event=%d, name=\"%s\", "
2364 "type=\"%s\", domain=\"%s\", hostTarget=\"%s\", address=%p, "
2365 "port=%d, txt=%p, flags=%d, context=%p)",
2366 resolver, interface, protocol, event, name, type, domain,
2367 hostTarget, address, port, txt, flags, context));
2368
2369 if (event != AVAHI_RESOLVER_FOUND)
2370 {
2371 avahi_service_resolver_free(resolver);
2372 avahi_simple_poll_quit(uribuf->poll);
2373 return;
2374 }
2375
2376 /*
2377 * If we have a UUID, compare it...
2378 */
2379
2380 if (uribuf->uuid && (pair = avahi_string_list_find(txt, "UUID")) != NULL)
2381 {
2382 char uuid[256]; /* UUID value */
2383
2384 avahi_string_list_get_pair(pair, NULL, &value, &valueLen);
2385
2386 memcpy(uuid, value, valueLen);
2387 uuid[valueLen] = '\0';
2388
2389 if (_cups_strcasecmp(uuid, uribuf->uuid))
2390 {
2391 if (uribuf->options & _HTTP_RESOLVE_STDERR)
2392 fprintf(stderr, "DEBUG: Found UUID %s, looking for %s.", uuid,
2393 uribuf->uuid);
2394
2395 DEBUG_printf(("5http_resolve_cb: Found UUID %s, looking for %s.", uuid,
2396 uribuf->uuid));
2397 return;
2398 }
2399 }
2400
2401 /*
2402 * Figure out the scheme from the full name...
2403 */
2404
2405 if (strstr(type, "_ipp."))
2406 scheme = "ipp";
2407 else if (strstr(type, "_printer."))
2408 scheme = "lpd";
2409 else if (strstr(type, "_pdl-datastream."))
2410 scheme = "socket";
2411 else
2412 scheme = "riousbprint";
2413
2414 if (!strncmp(type, "_ipps.", 6) || !strncmp(type, "_ipp-tls.", 9))
2415 scheme = "ipps";
2416 else if (!strncmp(type, "_ipp.", 5) || !strncmp(type, "_fax-ipp.", 9))
2417 scheme = "ipp";
2418 else if (!strncmp(type, "_http.", 6))
2419 scheme = "http";
2420 else if (!strncmp(type, "_https.", 7))
2421 scheme = "https";
2422 else if (!strncmp(type, "_printer.", 9))
2423 scheme = "lpd";
2424 else if (!strncmp(type, "_pdl-datastream.", 16))
2425 scheme = "socket";
2426 else
2427 {
2428 avahi_service_resolver_free(resolver);
2429 avahi_simple_poll_quit(uribuf->poll);
2430 return;
2431 }
2432
2433 /*
2434 * Extract the remote resource key from the TXT record...
2435 */
2436
2437 if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) &&
2438 (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
2439 !avahi_string_list_find(txt, "printer-type"))
2440 {
2441 reskey = "rfo";
2442 resdefault = "/ipp/faxout";
2443 }
2444 else
2445 {
2446 reskey = "rp";
2447 resdefault = "/";
2448 }
2449
2450 if ((pair = avahi_string_list_find(txt, reskey)) != NULL)
2451 {
2452 avahi_string_list_get_pair(pair, NULL, &value, &valueLen);
2453
2454 if (value[0] == '/')
2455 {
2456 /*
2457 * Value (incorrectly) has a leading slash already...
2458 */
2459
2460 memcpy(resource, value, valueLen);
2461 resource[valueLen] = '\0';
2462 }
2463 else
2464 {
2465 /*
2466 * Convert to resource by concatenating with a leading "/"...
2467 */
2468
2469 resource[0] = '/';
2470 memcpy(resource + 1, value, valueLen);
2471 resource[valueLen + 1] = '\0';
2472 }
2473 }
2474 else
2475 {
2476 /*
2477 * Use the default value...
2478 */
2479
2480 strlcpy(resource, resdefault, sizeof(resource));
2481 }
2482
2483 /*
2484 * Lookup the FQDN if needed...
2485 */
2486
2487 if ((uribuf->options & _HTTP_RESOLVE_FQDN) &&
2488 (hostptr = hostTarget + strlen(hostTarget) - 6) > hostTarget &&
2489 !_cups_strcasecmp(hostptr, ".local"))
2490 {
2491 /*
2492 * OK, we got a .local name but the caller needs a real domain. Start by
2493 * getting the IP address of the .local name and then do reverse-lookups...
2494 */
2495
2496 http_addrlist_t *addrlist, /* List of addresses */
2497 *addr; /* Current address */
2498
2499 DEBUG_printf(("5http_resolve_cb: Looking up \"%s\".", hostTarget));
2500
2501 snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port));
2502 if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL)
2503 {
2504 for (addr = addrlist; addr; addr = addr->next)
2505 {
2506 int error = getnameinfo(&(addr->addr.addr), (socklen_t)httpAddrLength(&(addr->addr)), fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD);
2507
2508 if (!error)
2509 {
2510 DEBUG_printf(("5http_resolve_cb: Found \"%s\".", fqdn));
2511
2512 if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn ||
2513 _cups_strcasecmp(hostptr, ".local"))
2514 {
2515 hostTarget = fqdn;
2516 break;
2517 }
2518 }
2519 #ifdef DEBUG
2520 else
2521 DEBUG_printf(("5http_resolve_cb: \"%s\" did not resolve: %d",
2522 httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)),
2523 error));
2524 #endif /* DEBUG */
2525 }
2526
2527 httpAddrFreeList(addrlist);
2528 }
2529 }
2530
2531 /*
2532 * Assemble the final device URI using the resolved hostname...
2533 */
2534
2535 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme,
2536 NULL, hostTarget, port, resource);
2537 DEBUG_printf(("5http_resolve_cb: Resolved URI is \"%s\".", uribuf->buffer));
2538
2539 avahi_simple_poll_quit(uribuf->poll);
2540 }
2541 #endif /* HAVE_DNSSD */