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