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