]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/http-support.c
License change: Apache License, Version 2.0.
[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_SEE_OTHER :
1355 s = _("See Other");
1356 break;
1357 case HTTP_STATUS_NOT_MODIFIED :
1358 s = _("Not Modified");
1359 break;
1360 case HTTP_STATUS_BAD_REQUEST :
1361 s = _("Bad Request");
1362 break;
1363 case HTTP_STATUS_UNAUTHORIZED :
1364 case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED :
1365 s = _("Unauthorized");
1366 break;
1367 case HTTP_STATUS_FORBIDDEN :
1368 s = _("Forbidden");
1369 break;
1370 case HTTP_STATUS_NOT_FOUND :
1371 s = _("Not Found");
1372 break;
1373 case HTTP_STATUS_REQUEST_TOO_LARGE :
1374 s = _("Request Entity Too Large");
1375 break;
1376 case HTTP_STATUS_URI_TOO_LONG :
1377 s = _("URI Too Long");
1378 break;
1379 case HTTP_STATUS_UPGRADE_REQUIRED :
1380 s = _("Upgrade Required");
1381 break;
1382 case HTTP_STATUS_NOT_IMPLEMENTED :
1383 s = _("Not Implemented");
1384 break;
1385 case HTTP_STATUS_NOT_SUPPORTED :
1386 s = _("Not Supported");
1387 break;
1388 case HTTP_STATUS_EXPECTATION_FAILED :
1389 s = _("Expectation Failed");
1390 break;
1391 case HTTP_STATUS_SERVICE_UNAVAILABLE :
1392 s = _("Service Unavailable");
1393 break;
1394 case HTTP_STATUS_SERVER_ERROR :
1395 s = _("Internal Server Error");
1396 break;
1397 case HTTP_STATUS_CUPS_PKI_ERROR :
1398 s = _("SSL/TLS Negotiation Error");
1399 break;
1400 case HTTP_STATUS_CUPS_WEBIF_DISABLED :
1401 s = _("Web Interface is Disabled");
1402 break;
1403
1404 default :
1405 s = _("Unknown");
1406 break;
1407 }
1408
1409 return (_cupsLangString(lang, s));
1410 }
1411
1412
1413 /*
1414 * 'httpStatus()' - Return a short string describing a HTTP status code.
1415 *
1416 * The returned string is localized to the current POSIX locale and is based
1417 * on the status strings defined in RFC 7231.
1418 */
1419
1420 const char * /* O - Localized status string */
1421 httpStatus(http_status_t status) /* I - HTTP status code */
1422 {
1423 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
1424
1425
1426 if (!cg->lang_default)
1427 cg->lang_default = cupsLangDefault();
1428
1429 return (_httpStatus(cg->lang_default, status));
1430 }
1431
1432 /*
1433 * 'httpURIStatusString()' - Return a string describing a URI status code.
1434 *
1435 * @since CUPS 2.0/OS 10.10@
1436 */
1437
1438 const char * /* O - Localized status string */
1439 httpURIStatusString(
1440 http_uri_status_t status) /* I - URI status code */
1441 {
1442 const char *s; /* Status string */
1443 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
1444
1445
1446 if (!cg->lang_default)
1447 cg->lang_default = cupsLangDefault();
1448
1449 switch (status)
1450 {
1451 case HTTP_URI_STATUS_OVERFLOW :
1452 s = _("URI too large");
1453 break;
1454 case HTTP_URI_STATUS_BAD_ARGUMENTS :
1455 s = _("Bad arguments to function");
1456 break;
1457 case HTTP_URI_STATUS_BAD_RESOURCE :
1458 s = _("Bad resource in URI");
1459 break;
1460 case HTTP_URI_STATUS_BAD_PORT :
1461 s = _("Bad port number in URI");
1462 break;
1463 case HTTP_URI_STATUS_BAD_HOSTNAME :
1464 s = _("Bad hostname/address in URI");
1465 break;
1466 case HTTP_URI_STATUS_BAD_USERNAME :
1467 s = _("Bad username in URI");
1468 break;
1469 case HTTP_URI_STATUS_BAD_SCHEME :
1470 s = _("Bad scheme in URI");
1471 break;
1472 case HTTP_URI_STATUS_BAD_URI :
1473 s = _("Bad/empty URI");
1474 break;
1475 case HTTP_URI_STATUS_OK :
1476 s = _("OK");
1477 break;
1478 case HTTP_URI_STATUS_MISSING_SCHEME :
1479 s = _("Missing scheme in URI");
1480 break;
1481 case HTTP_URI_STATUS_UNKNOWN_SCHEME :
1482 s = _("Unknown scheme in URI");
1483 break;
1484 case HTTP_URI_STATUS_MISSING_RESOURCE :
1485 s = _("Missing resource in URI");
1486 break;
1487
1488 default:
1489 s = _("Unknown");
1490 break;
1491 }
1492
1493 return (_cupsLangString(cg->lang_default, s));
1494 }
1495
1496
1497 #ifndef HAVE_HSTRERROR
1498 /*
1499 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others.
1500 */
1501
1502 const char * /* O - Error string */
1503 _cups_hstrerror(int error) /* I - Error number */
1504 {
1505 static const char * const errors[] = /* Error strings */
1506 {
1507 "OK",
1508 "Host not found.",
1509 "Try again.",
1510 "Unrecoverable lookup error.",
1511 "No data associated with name."
1512 };
1513
1514
1515 if (error < 0 || error > 4)
1516 return ("Unknown hostname lookup error.");
1517 else
1518 return (errors[error]);
1519 }
1520 #endif /* !HAVE_HSTRERROR */
1521
1522
1523 /*
1524 * '_httpDecodeURI()' - Percent-decode a HTTP request URI.
1525 */
1526
1527 char * /* O - Decoded URI or NULL on error */
1528 _httpDecodeURI(char *dst, /* I - Destination buffer */
1529 const char *src, /* I - Source URI */
1530 size_t dstsize) /* I - Size of destination buffer */
1531 {
1532 if (http_copy_decode(dst, src, (int)dstsize, NULL, 1))
1533 return (dst);
1534 else
1535 return (NULL);
1536 }
1537
1538
1539 /*
1540 * '_httpEncodeURI()' - Percent-encode a HTTP request URI.
1541 */
1542
1543 char * /* O - Encoded URI */
1544 _httpEncodeURI(char *dst, /* I - Destination buffer */
1545 const char *src, /* I - Source URI */
1546 size_t dstsize) /* I - Size of destination buffer */
1547 {
1548 http_copy_encode(dst, src, dst + dstsize - 1, NULL, NULL, 1);
1549 return (dst);
1550 }
1551
1552
1553 /*
1554 * '_httpResolveURI()' - Resolve a DNS-SD URI.
1555 */
1556
1557 const char * /* O - Resolved URI */
1558 _httpResolveURI(
1559 const char *uri, /* I - DNS-SD URI */
1560 char *resolved_uri, /* I - Buffer for resolved URI */
1561 size_t resolved_size, /* I - Size of URI buffer */
1562 int options, /* I - Resolve options */
1563 int (*cb)(void *context), /* I - Continue callback function */
1564 void *context) /* I - Context pointer for callback */
1565 {
1566 char scheme[32], /* URI components... */
1567 userpass[256],
1568 hostname[1024],
1569 resource[1024];
1570 int port;
1571 #ifdef DEBUG
1572 http_uri_status_t status; /* URI decode status */
1573 #endif /* DEBUG */
1574
1575
1576 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));
1577
1578 /*
1579 * Get the device URI...
1580 */
1581
1582 #ifdef DEBUG
1583 if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1584 sizeof(scheme), userpass, sizeof(userpass),
1585 hostname, sizeof(hostname), &port, resource,
1586 sizeof(resource))) < HTTP_URI_STATUS_OK)
1587 #else
1588 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1589 sizeof(scheme), userpass, sizeof(userpass),
1590 hostname, sizeof(hostname), &port, resource,
1591 sizeof(resource)) < HTTP_URI_STATUS_OK)
1592 #endif /* DEBUG */
1593 {
1594 if (options & _HTTP_RESOLVE_STDERR)
1595 _cupsLangPrintFilter(stderr, "ERROR", _("Bad device-uri \"%s\"."), uri);
1596
1597 DEBUG_printf(("2_httpResolveURI: httpSeparateURI returned %d!", status));
1598 DEBUG_puts("2_httpResolveURI: Returning NULL");
1599 return (NULL);
1600 }
1601
1602 /*
1603 * Resolve it as needed...
1604 */
1605
1606 if (strstr(hostname, "._tcp"))
1607 {
1608 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1609 char *regtype, /* Pointer to type in hostname */
1610 *domain, /* Pointer to domain in hostname */
1611 *uuid, /* Pointer to UUID in URI */
1612 *uuidend; /* Pointer to end of UUID in URI */
1613 _http_uribuf_t uribuf; /* URI buffer */
1614 int offline = 0; /* offline-report state set? */
1615 # ifdef HAVE_DNSSD
1616 # ifdef WIN32
1617 # pragma comment(lib, "dnssd.lib")
1618 # endif /* WIN32 */
1619 DNSServiceRef ref, /* DNS-SD master service reference */
1620 domainref = NULL,/* DNS-SD service reference for domain */
1621 ippref = NULL, /* DNS-SD service reference for network IPP */
1622 ippsref = NULL, /* DNS-SD service reference for network IPPS */
1623 localref; /* DNS-SD service reference for .local */
1624 int extrasent = 0; /* Send the domain/IPP/IPPS resolves? */
1625 # ifdef HAVE_POLL
1626 struct pollfd polldata; /* Polling data */
1627 # else /* select() */
1628 fd_set input_set; /* Input set for select() */
1629 struct timeval stimeout; /* Timeout value for select() */
1630 # endif /* HAVE_POLL */
1631 # elif defined(HAVE_AVAHI)
1632 AvahiClient *client; /* Client information */
1633 int error; /* Status */
1634 # endif /* HAVE_DNSSD */
1635
1636 if (options & _HTTP_RESOLVE_STDERR)
1637 fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname);
1638
1639 /*
1640 * Separate the hostname into service name, registration type, and domain...
1641 */
1642
1643 for (regtype = strstr(hostname, "._tcp") - 2;
1644 regtype > hostname;
1645 regtype --)
1646 if (regtype[0] == '.' && regtype[1] == '_')
1647 {
1648 /*
1649 * Found ._servicetype in front of ._tcp...
1650 */
1651
1652 *regtype++ = '\0';
1653 break;
1654 }
1655
1656 if (regtype <= hostname)
1657 {
1658 DEBUG_puts("2_httpResolveURI: Bad hostname, returning NULL");
1659 return (NULL);
1660 }
1661
1662 for (domain = strchr(regtype, '.');
1663 domain;
1664 domain = strchr(domain + 1, '.'))
1665 if (domain[1] != '_')
1666 break;
1667
1668 if (domain)
1669 *domain++ = '\0';
1670
1671 if ((uuid = strstr(resource, "?uuid=")) != NULL)
1672 {
1673 *uuid = '\0';
1674 uuid += 6;
1675 if ((uuidend = strchr(uuid, '&')) != NULL)
1676 *uuidend = '\0';
1677 }
1678
1679 resolved_uri[0] = '\0';
1680
1681 uribuf.buffer = resolved_uri;
1682 uribuf.bufsize = resolved_size;
1683 uribuf.options = options;
1684 uribuf.resource = resource;
1685 uribuf.uuid = uuid;
1686
1687 DEBUG_printf(("2_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", "
1688 "domain=\"%s\"\n", hostname, regtype, domain));
1689 if (options & _HTTP_RESOLVE_STDERR)
1690 {
1691 fputs("STATE: +connecting-to-device\n", stderr);
1692 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1693 "domain=\"local.\"...\n", hostname, regtype);
1694 }
1695
1696 uri = NULL;
1697
1698 # ifdef HAVE_DNSSD
1699 if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError)
1700 {
1701 uint32_t myinterface = kDNSServiceInterfaceIndexAny;
1702 /* Lookup on any interface */
1703
1704 if (!strcmp(scheme, "ippusb"))
1705 myinterface = kDNSServiceInterfaceIndexLocalOnly;
1706
1707 localref = ref;
1708 if (DNSServiceResolve(&localref,
1709 kDNSServiceFlagsShareConnection, myinterface,
1710 hostname, regtype, "local.", http_resolve_cb,
1711 &uribuf) == kDNSServiceErr_NoError)
1712 {
1713 int fds; /* Number of ready descriptors */
1714 time_t timeout, /* Poll timeout */
1715 start_time = time(NULL),/* Start time */
1716 end_time = start_time + 90;
1717 /* End time */
1718
1719 while (time(NULL) < end_time)
1720 {
1721 if (options & _HTTP_RESOLVE_STDERR)
1722 _cupsLangPrintFilter(stderr, "INFO", _("Looking for printer."));
1723
1724 if (cb && !(*cb)(context))
1725 {
1726 DEBUG_puts("2_httpResolveURI: callback returned 0 (stop)");
1727 break;
1728 }
1729
1730 /*
1731 * Wakeup every 2 seconds to emit a "looking for printer" message...
1732 */
1733
1734 if ((timeout = end_time - time(NULL)) > 2)
1735 timeout = 2;
1736
1737 # ifdef HAVE_POLL
1738 polldata.fd = DNSServiceRefSockFD(ref);
1739 polldata.events = POLLIN;
1740
1741 fds = poll(&polldata, 1, (int)(1000 * timeout));
1742
1743 # else /* select() */
1744 FD_ZERO(&input_set);
1745 FD_SET(DNSServiceRefSockFD(ref), &input_set);
1746
1747 # ifdef WIN32
1748 stimeout.tv_sec = (long)timeout;
1749 # else
1750 stimeout.tv_sec = timeout;
1751 # endif /* WIN32 */
1752 stimeout.tv_usec = 0;
1753
1754 fds = select(DNSServiceRefSockFD(ref)+1, &input_set, NULL, NULL,
1755 &stimeout);
1756 # endif /* HAVE_POLL */
1757
1758 if (fds < 0)
1759 {
1760 if (errno != EINTR && errno != EAGAIN)
1761 {
1762 DEBUG_printf(("2_httpResolveURI: poll error: %s", strerror(errno)));
1763 break;
1764 }
1765 }
1766 else if (fds == 0)
1767 {
1768 /*
1769 * Wait 2 seconds for a response to the local resolve; if nothing
1770 * comes in, do an additional domain resolution...
1771 */
1772
1773 if (extrasent == 0 && domain && _cups_strcasecmp(domain, "local."))
1774 {
1775 if (options & _HTTP_RESOLVE_STDERR)
1776 fprintf(stderr,
1777 "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1778 "domain=\"%s\"...\n", hostname, regtype,
1779 domain ? domain : "");
1780
1781 domainref = ref;
1782 if (DNSServiceResolve(&domainref,
1783 kDNSServiceFlagsShareConnection,
1784 myinterface, hostname, regtype, domain,
1785 http_resolve_cb,
1786 &uribuf) == kDNSServiceErr_NoError)
1787 extrasent = 1;
1788 }
1789 else if (extrasent == 0 && !strcmp(scheme, "ippusb"))
1790 {
1791 if (options & _HTTP_RESOLVE_STDERR)
1792 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"_ipps._tcp\", domain=\"local.\"...\n", hostname);
1793
1794 ippsref = ref;
1795 if (DNSServiceResolve(&ippsref,
1796 kDNSServiceFlagsShareConnection,
1797 kDNSServiceInterfaceIndexAny, hostname,
1798 "_ipps._tcp", domain, http_resolve_cb,
1799 &uribuf) == kDNSServiceErr_NoError)
1800 extrasent = 1;
1801 }
1802 else if (extrasent == 1 && !strcmp(scheme, "ippusb"))
1803 {
1804 if (options & _HTTP_RESOLVE_STDERR)
1805 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"_ipp._tcp\", domain=\"local.\"...\n", hostname);
1806
1807 ippref = ref;
1808 if (DNSServiceResolve(&ippref,
1809 kDNSServiceFlagsShareConnection,
1810 kDNSServiceInterfaceIndexAny, hostname,
1811 "_ipp._tcp", domain, http_resolve_cb,
1812 &uribuf) == kDNSServiceErr_NoError)
1813 extrasent = 2;
1814 }
1815
1816 /*
1817 * If it hasn't resolved within 5 seconds set the offline-report
1818 * printer-state-reason...
1819 */
1820
1821 if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 &&
1822 time(NULL) > (start_time + 5))
1823 {
1824 fputs("STATE: +offline-report\n", stderr);
1825 offline = 1;
1826 }
1827 }
1828 else
1829 {
1830 if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError &&
1831 resolved_uri[0])
1832 {
1833 uri = resolved_uri;
1834 break;
1835 }
1836 }
1837 }
1838
1839 if (extrasent)
1840 {
1841 if (domainref)
1842 DNSServiceRefDeallocate(domainref);
1843 if (ippref)
1844 DNSServiceRefDeallocate(ippref);
1845 if (ippsref)
1846 DNSServiceRefDeallocate(ippsref);
1847 }
1848
1849 DNSServiceRefDeallocate(localref);
1850 }
1851
1852 DNSServiceRefDeallocate(ref);
1853 }
1854 # else /* HAVE_AVAHI */
1855 if ((uribuf.poll = avahi_simple_poll_new()) != NULL)
1856 {
1857 avahi_simple_poll_set_func(uribuf.poll, http_poll_cb, NULL);
1858
1859 if ((client = avahi_client_new(avahi_simple_poll_get(uribuf.poll),
1860 0, http_client_cb,
1861 &uribuf, &error)) != NULL)
1862 {
1863 if (avahi_service_resolver_new(client, AVAHI_IF_UNSPEC,
1864 AVAHI_PROTO_UNSPEC, hostname,
1865 regtype, "local.", AVAHI_PROTO_UNSPEC, 0,
1866 http_resolve_cb, &uribuf) != NULL)
1867 {
1868 time_t start_time = time(NULL),
1869 /* Start time */
1870 end_time = start_time + 90;
1871 /* End time */
1872 int pstatus; /* Poll status */
1873
1874 pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000);
1875
1876 if (pstatus == 0 && !resolved_uri[0] && domain &&
1877 _cups_strcasecmp(domain, "local."))
1878 {
1879 /*
1880 * Resolve for .local hasn't returned anything, try the listed
1881 * domain...
1882 */
1883
1884 avahi_service_resolver_new(client, AVAHI_IF_UNSPEC,
1885 AVAHI_PROTO_UNSPEC, hostname,
1886 regtype, domain, AVAHI_PROTO_UNSPEC, 0,
1887 http_resolve_cb, &uribuf);
1888 }
1889
1890 while (!pstatus && !resolved_uri[0] && time(NULL) < end_time)
1891 {
1892 if ((pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000)) != 0)
1893 break;
1894
1895 /*
1896 * If it hasn't resolved within 5 seconds set the offline-report
1897 * printer-state-reason...
1898 */
1899
1900 if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 &&
1901 time(NULL) > (start_time + 5))
1902 {
1903 fputs("STATE: +offline-report\n", stderr);
1904 offline = 1;
1905 }
1906 }
1907
1908 /*
1909 * Collect the result (if we got one).
1910 */
1911
1912 if (resolved_uri[0])
1913 uri = resolved_uri;
1914 }
1915
1916 avahi_client_free(client);
1917 }
1918
1919 avahi_simple_poll_free(uribuf.poll);
1920 }
1921 # endif /* HAVE_DNSSD */
1922
1923 if (options & _HTTP_RESOLVE_STDERR)
1924 {
1925 if (uri)
1926 {
1927 fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri);
1928 fputs("STATE: -connecting-to-device,offline-report\n", stderr);
1929 }
1930 else
1931 {
1932 fputs("DEBUG: Unable to resolve URI\n", stderr);
1933 fputs("STATE: -connecting-to-device\n", stderr);
1934 }
1935 }
1936
1937 #else /* HAVE_DNSSD || HAVE_AVAHI */
1938 /*
1939 * No DNS-SD support...
1940 */
1941
1942 uri = NULL;
1943 #endif /* HAVE_DNSSD || HAVE_AVAHI */
1944
1945 if ((options & _HTTP_RESOLVE_STDERR) && !uri)
1946 _cupsLangPrintFilter(stderr, "INFO", _("Unable to find printer."));
1947 }
1948 else
1949 {
1950 /*
1951 * Nothing more to do...
1952 */
1953
1954 strlcpy(resolved_uri, uri, resolved_size);
1955 uri = resolved_uri;
1956 }
1957
1958 DEBUG_printf(("2_httpResolveURI: Returning \"%s\"", uri));
1959
1960 return (uri);
1961 }
1962
1963
1964 #ifdef HAVE_AVAHI
1965 /*
1966 * 'http_client_cb()' - Client callback for resolving URI.
1967 */
1968
1969 static void
1970 http_client_cb(
1971 AvahiClient *client, /* I - Client information */
1972 AvahiClientState state, /* I - Current state */
1973 void *context) /* I - Pointer to URI buffer */
1974 {
1975 DEBUG_printf(("7http_client_cb(client=%p, state=%d, context=%p)", client,
1976 state, context));
1977
1978 /*
1979 * If the connection drops, quit.
1980 */
1981
1982 if (state == AVAHI_CLIENT_FAILURE)
1983 {
1984 _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
1985 /* URI buffer */
1986
1987 avahi_simple_poll_quit(uribuf->poll);
1988 }
1989 }
1990 #endif /* HAVE_AVAHI */
1991
1992
1993 /*
1994 * 'http_copy_decode()' - Copy and decode a URI.
1995 */
1996
1997 static const char * /* O - New source pointer or NULL on error */
1998 http_copy_decode(char *dst, /* O - Destination buffer */
1999 const char *src, /* I - Source pointer */
2000 int dstsize, /* I - Destination size */
2001 const char *term, /* I - Terminating characters */
2002 int decode) /* I - Decode %-encoded values */
2003 {
2004 char *ptr, /* Pointer into buffer */
2005 *end; /* End of buffer */
2006 int quoted; /* Quoted character */
2007
2008
2009 /*
2010 * Copy the src to the destination until we hit a terminating character
2011 * or the end of the string.
2012 */
2013
2014 for (ptr = dst, end = dst + dstsize - 1;
2015 *src && (!term || !strchr(term, *src));
2016 src ++)
2017 if (ptr < end)
2018 {
2019 if (*src == '%' && decode)
2020 {
2021 if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255))
2022 {
2023 /*
2024 * Grab a hex-encoded character...
2025 */
2026
2027 src ++;
2028 if (isalpha(*src))
2029 quoted = (tolower(*src) - 'a' + 10) << 4;
2030 else
2031 quoted = (*src - '0') << 4;
2032
2033 src ++;
2034 if (isalpha(*src))
2035 quoted |= tolower(*src) - 'a' + 10;
2036 else
2037 quoted |= *src - '0';
2038
2039 *ptr++ = (char)quoted;
2040 }
2041 else
2042 {
2043 /*
2044 * Bad hex-encoded character...
2045 */
2046
2047 *ptr = '\0';
2048 return (NULL);
2049 }
2050 }
2051 else if ((*src & 255) <= 0x20 || (*src & 255) >= 0x7f)
2052 {
2053 *ptr = '\0';
2054 return (NULL);
2055 }
2056 else
2057 *ptr++ = *src;
2058 }
2059
2060 *ptr = '\0';
2061
2062 return (src);
2063 }
2064
2065
2066 /*
2067 * 'http_copy_encode()' - Copy and encode a URI.
2068 */
2069
2070 static char * /* O - End of current URI */
2071 http_copy_encode(char *dst, /* O - Destination buffer */
2072 const char *src, /* I - Source pointer */
2073 char *dstend, /* I - End of destination buffer */
2074 const char *reserved, /* I - Extra reserved characters */
2075 const char *term, /* I - Terminating characters */
2076 int encode) /* I - %-encode reserved chars? */
2077 {
2078 static const char hex[] = "0123456789ABCDEF";
2079
2080
2081 while (*src && dst < dstend)
2082 {
2083 if (term && *src == *term)
2084 return (dst);
2085
2086 if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
2087 (reserved && strchr(reserved, *src))))
2088 {
2089 /*
2090 * Hex encode reserved characters...
2091 */
2092
2093 if ((dst + 2) >= dstend)
2094 break;
2095
2096 *dst++ = '%';
2097 *dst++ = hex[(*src >> 4) & 15];
2098 *dst++ = hex[*src & 15];
2099
2100 src ++;
2101 }
2102 else
2103 *dst++ = *src++;
2104 }
2105
2106 *dst = '\0';
2107
2108 if (*src)
2109 return (NULL);
2110 else
2111 return (dst);
2112 }
2113
2114
2115 #ifdef HAVE_DNSSD
2116 /*
2117 * 'http_resolve_cb()' - Build a device URI for the given service name.
2118 */
2119
2120 static void DNSSD_API
2121 http_resolve_cb(
2122 DNSServiceRef sdRef, /* I - Service reference */
2123 DNSServiceFlags flags, /* I - Results flags */
2124 uint32_t interfaceIndex, /* I - Interface number */
2125 DNSServiceErrorType errorCode, /* I - Error, if any */
2126 const char *fullName, /* I - Full service name */
2127 const char *hostTarget, /* I - Hostname */
2128 uint16_t port, /* I - Port number */
2129 uint16_t txtLen, /* I - Length of TXT record */
2130 const unsigned char *txtRecord, /* I - TXT record data */
2131 void *context) /* I - Pointer to URI buffer */
2132 {
2133 _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
2134 /* URI buffer */
2135 const char *scheme, /* URI scheme */
2136 *hostptr, /* Pointer into hostTarget */
2137 *reskey, /* "rp" or "rfo" */
2138 *resdefault; /* Default path */
2139 char resource[257], /* Remote path */
2140 fqdn[256]; /* FQDN of the .local name */
2141 const void *value; /* Value from TXT record */
2142 uint8_t valueLen; /* Length of value */
2143
2144
2145 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));
2146
2147 /*
2148 * If we have a UUID, compare it...
2149 */
2150
2151 if (uribuf->uuid &&
2152 (value = TXTRecordGetValuePtr(txtLen, txtRecord, "UUID",
2153 &valueLen)) != NULL)
2154 {
2155 char uuid[256]; /* UUID value */
2156
2157 memcpy(uuid, value, valueLen);
2158 uuid[valueLen] = '\0';
2159
2160 if (_cups_strcasecmp(uuid, uribuf->uuid))
2161 {
2162 if (uribuf->options & _HTTP_RESOLVE_STDERR)
2163 fprintf(stderr, "DEBUG: Found UUID %s, looking for %s.", uuid,
2164 uribuf->uuid);
2165
2166 DEBUG_printf(("5http_resolve_cb: Found UUID %s, looking for %s.", uuid,
2167 uribuf->uuid));
2168 return;
2169 }
2170 }
2171
2172 /*
2173 * Figure out the scheme from the full name...
2174 */
2175
2176 if (strstr(fullName, "._ipps") || strstr(fullName, "._ipp-tls"))
2177 scheme = "ipps";
2178 else if (strstr(fullName, "._ipp") || strstr(fullName, "._fax-ipp"))
2179 scheme = "ipp";
2180 else if (strstr(fullName, "._http."))
2181 scheme = "http";
2182 else if (strstr(fullName, "._https."))
2183 scheme = "https";
2184 else if (strstr(fullName, "._printer."))
2185 scheme = "lpd";
2186 else if (strstr(fullName, "._pdl-datastream."))
2187 scheme = "socket";
2188 else
2189 scheme = "riousbprint";
2190
2191 /*
2192 * Extract the "remote printer" key from the TXT record...
2193 */
2194
2195 if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) &&
2196 (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
2197 !TXTRecordGetValuePtr(txtLen, txtRecord, "printer-type", &valueLen))
2198 {
2199 reskey = "rfo";
2200 resdefault = "/ipp/faxout";
2201 }
2202 else
2203 {
2204 reskey = "rp";
2205 resdefault = "/";
2206 }
2207
2208 if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, reskey,
2209 &valueLen)) != NULL)
2210 {
2211 if (((char *)value)[0] == '/')
2212 {
2213 /*
2214 * Value (incorrectly) has a leading slash already...
2215 */
2216
2217 memcpy(resource, value, valueLen);
2218 resource[valueLen] = '\0';
2219 }
2220 else
2221 {
2222 /*
2223 * Convert to resource by concatenating with a leading "/"...
2224 */
2225
2226 resource[0] = '/';
2227 memcpy(resource + 1, value, valueLen);
2228 resource[valueLen + 1] = '\0';
2229 }
2230 }
2231 else
2232 {
2233 /*
2234 * Use the default value...
2235 */
2236
2237 strlcpy(resource, resdefault, sizeof(resource));
2238 }
2239
2240 /*
2241 * Lookup the FQDN if needed...
2242 */
2243
2244 if ((uribuf->options & _HTTP_RESOLVE_FQDN) &&
2245 (hostptr = hostTarget + strlen(hostTarget) - 7) > hostTarget &&
2246 !_cups_strcasecmp(hostptr, ".local."))
2247 {
2248 /*
2249 * OK, we got a .local name but the caller needs a real domain. Start by
2250 * getting the IP address of the .local name and then do reverse-lookups...
2251 */
2252
2253 http_addrlist_t *addrlist, /* List of addresses */
2254 *addr; /* Current address */
2255
2256 DEBUG_printf(("5http_resolve_cb: Looking up \"%s\".", hostTarget));
2257
2258 snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port));
2259 if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL)
2260 {
2261 for (addr = addrlist; addr; addr = addr->next)
2262 {
2263 int error = getnameinfo(&(addr->addr.addr), (socklen_t)httpAddrLength(&(addr->addr)), fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD);
2264
2265 if (!error)
2266 {
2267 DEBUG_printf(("5http_resolve_cb: Found \"%s\".", fqdn));
2268
2269 if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn ||
2270 _cups_strcasecmp(hostptr, ".local"))
2271 {
2272 hostTarget = fqdn;
2273 break;
2274 }
2275 }
2276 #ifdef DEBUG
2277 else
2278 DEBUG_printf(("5http_resolve_cb: \"%s\" did not resolve: %d",
2279 httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)),
2280 error));
2281 #endif /* DEBUG */
2282 }
2283
2284 httpAddrFreeList(addrlist);
2285 }
2286 }
2287
2288 /*
2289 * Assemble the final device URI...
2290 */
2291
2292 if ((!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
2293 !strcmp(uribuf->resource, "/cups"))
2294 httpAssembleURIf(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme, NULL, hostTarget, ntohs(port), "%s?snmp=false", resource);
2295 else
2296 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme, NULL, hostTarget, ntohs(port), resource);
2297
2298 DEBUG_printf(("5http_resolve_cb: Resolved URI is \"%s\"...", uribuf->buffer));
2299 }
2300
2301 #elif defined(HAVE_AVAHI)
2302 /*
2303 * 'http_poll_cb()' - Wait for input on the specified file descriptors.
2304 *
2305 * Note: This function is needed because avahi_simple_poll_iterate is broken
2306 * and always uses a timeout of 0 (!) milliseconds.
2307 * (Avahi Ticket #364)
2308 *
2309 * @private@
2310 */
2311
2312 static int /* O - Number of file descriptors matching */
2313 http_poll_cb(
2314 struct pollfd *pollfds, /* I - File descriptors */
2315 unsigned int num_pollfds, /* I - Number of file descriptors */
2316 int timeout, /* I - Timeout in milliseconds (used) */
2317 void *context) /* I - User data (unused) */
2318 {
2319 (void)timeout;
2320 (void)context;
2321
2322 return (poll(pollfds, num_pollfds, 2000));
2323 }
2324
2325
2326 /*
2327 * 'http_resolve_cb()' - Build a device URI for the given service name.
2328 */
2329
2330 static void
2331 http_resolve_cb(
2332 AvahiServiceResolver *resolver, /* I - Resolver (unused) */
2333 AvahiIfIndex interface, /* I - Interface index (unused) */
2334 AvahiProtocol protocol, /* I - Network protocol (unused) */
2335 AvahiResolverEvent event, /* I - Event (found, etc.) */
2336 const char *name, /* I - Service name */
2337 const char *type, /* I - Registration type */
2338 const char *domain, /* I - Domain (unused) */
2339 const char *hostTarget, /* I - Hostname */
2340 const AvahiAddress *address, /* I - Address (unused) */
2341 uint16_t port, /* I - Port number */
2342 AvahiStringList *txt, /* I - TXT record */
2343 AvahiLookupResultFlags flags, /* I - Lookup flags (unused) */
2344 void *context) /* I - Pointer to URI buffer */
2345 {
2346 _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
2347 /* URI buffer */
2348 const char *scheme, /* URI scheme */
2349 *hostptr, /* Pointer into hostTarget */
2350 *reskey, /* "rp" or "rfo" */
2351 *resdefault; /* Default path */
2352 char resource[257], /* Remote path */
2353 fqdn[256]; /* FQDN of the .local name */
2354 AvahiStringList *pair; /* Current TXT record key/value pair */
2355 char *value; /* Value for "rp" key */
2356 size_t valueLen = 0; /* Length of "rp" key */
2357
2358
2359 DEBUG_printf(("4http_resolve_cb(resolver=%p, "
2360 "interface=%d, protocol=%d, event=%d, name=\"%s\", "
2361 "type=\"%s\", domain=\"%s\", hostTarget=\"%s\", address=%p, "
2362 "port=%d, txt=%p, flags=%d, context=%p)",
2363 resolver, interface, protocol, event, name, type, domain,
2364 hostTarget, address, port, txt, flags, context));
2365
2366 if (event != AVAHI_RESOLVER_FOUND)
2367 {
2368 avahi_service_resolver_free(resolver);
2369 avahi_simple_poll_quit(uribuf->poll);
2370 return;
2371 }
2372
2373 /*
2374 * If we have a UUID, compare it...
2375 */
2376
2377 if (uribuf->uuid && (pair = avahi_string_list_find(txt, "UUID")) != NULL)
2378 {
2379 char uuid[256]; /* UUID value */
2380
2381 avahi_string_list_get_pair(pair, NULL, &value, &valueLen);
2382
2383 memcpy(uuid, value, valueLen);
2384 uuid[valueLen] = '\0';
2385
2386 if (_cups_strcasecmp(uuid, uribuf->uuid))
2387 {
2388 if (uribuf->options & _HTTP_RESOLVE_STDERR)
2389 fprintf(stderr, "DEBUG: Found UUID %s, looking for %s.", uuid,
2390 uribuf->uuid);
2391
2392 DEBUG_printf(("5http_resolve_cb: Found UUID %s, looking for %s.", uuid,
2393 uribuf->uuid));
2394 return;
2395 }
2396 }
2397
2398 /*
2399 * Figure out the scheme from the full name...
2400 */
2401
2402 if (strstr(type, "_ipp."))
2403 scheme = "ipp";
2404 else if (strstr(type, "_printer."))
2405 scheme = "lpd";
2406 else if (strstr(type, "_pdl-datastream."))
2407 scheme = "socket";
2408 else
2409 scheme = "riousbprint";
2410
2411 if (!strncmp(type, "_ipps.", 6) || !strncmp(type, "_ipp-tls.", 9))
2412 scheme = "ipps";
2413 else if (!strncmp(type, "_ipp.", 5) || !strncmp(type, "_fax-ipp.", 9))
2414 scheme = "ipp";
2415 else if (!strncmp(type, "_http.", 6))
2416 scheme = "http";
2417 else if (!strncmp(type, "_https.", 7))
2418 scheme = "https";
2419 else if (!strncmp(type, "_printer.", 9))
2420 scheme = "lpd";
2421 else if (!strncmp(type, "_pdl-datastream.", 16))
2422 scheme = "socket";
2423 else
2424 {
2425 avahi_service_resolver_free(resolver);
2426 avahi_simple_poll_quit(uribuf->poll);
2427 return;
2428 }
2429
2430 /*
2431 * Extract the remote resource key from the TXT record...
2432 */
2433
2434 if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) &&
2435 (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
2436 !avahi_string_list_find(txt, "printer-type"))
2437 {
2438 reskey = "rfo";
2439 resdefault = "/ipp/faxout";
2440 }
2441 else
2442 {
2443 reskey = "rp";
2444 resdefault = "/";
2445 }
2446
2447 if ((pair = avahi_string_list_find(txt, reskey)) != NULL)
2448 {
2449 avahi_string_list_get_pair(pair, NULL, &value, &valueLen);
2450
2451 if (value[0] == '/')
2452 {
2453 /*
2454 * Value (incorrectly) has a leading slash already...
2455 */
2456
2457 memcpy(resource, value, valueLen);
2458 resource[valueLen] = '\0';
2459 }
2460 else
2461 {
2462 /*
2463 * Convert to resource by concatenating with a leading "/"...
2464 */
2465
2466 resource[0] = '/';
2467 memcpy(resource + 1, value, valueLen);
2468 resource[valueLen + 1] = '\0';
2469 }
2470 }
2471 else
2472 {
2473 /*
2474 * Use the default value...
2475 */
2476
2477 strlcpy(resource, resdefault, sizeof(resource));
2478 }
2479
2480 /*
2481 * Lookup the FQDN if needed...
2482 */
2483
2484 if ((uribuf->options & _HTTP_RESOLVE_FQDN) &&
2485 (hostptr = hostTarget + strlen(hostTarget) - 6) > hostTarget &&
2486 !_cups_strcasecmp(hostptr, ".local"))
2487 {
2488 /*
2489 * OK, we got a .local name but the caller needs a real domain. Start by
2490 * getting the IP address of the .local name and then do reverse-lookups...
2491 */
2492
2493 http_addrlist_t *addrlist, /* List of addresses */
2494 *addr; /* Current address */
2495
2496 DEBUG_printf(("5http_resolve_cb: Looking up \"%s\".", hostTarget));
2497
2498 snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port));
2499 if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL)
2500 {
2501 for (addr = addrlist; addr; addr = addr->next)
2502 {
2503 int error = getnameinfo(&(addr->addr.addr), (socklen_t)httpAddrLength(&(addr->addr)), fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD);
2504
2505 if (!error)
2506 {
2507 DEBUG_printf(("5http_resolve_cb: Found \"%s\".", fqdn));
2508
2509 if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn ||
2510 _cups_strcasecmp(hostptr, ".local"))
2511 {
2512 hostTarget = fqdn;
2513 break;
2514 }
2515 }
2516 #ifdef DEBUG
2517 else
2518 DEBUG_printf(("5http_resolve_cb: \"%s\" did not resolve: %d",
2519 httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)),
2520 error));
2521 #endif /* DEBUG */
2522 }
2523
2524 httpAddrFreeList(addrlist);
2525 }
2526 }
2527
2528 /*
2529 * Assemble the final device URI using the resolved hostname...
2530 */
2531
2532 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme,
2533 NULL, hostTarget, port, resource);
2534 DEBUG_printf(("5http_resolve_cb: Resolved URI is \"%s\".", uribuf->buffer));
2535
2536 avahi_simple_poll_quit(uribuf->poll);
2537 }
2538 #endif /* HAVE_DNSSD */