]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/http-support.c
Merge changes from CUPS 1.4svn-r8606.
[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 the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 2007-2009 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 * httpDecode64() - Base64-decode a string.
24 * httpDecode64_2() - Base64-decode a string.
25 * httpEncode64() - Base64-encode a string.
26 * httpEncode64_2() - Base64-encode a string.
27 * httpGetDateString() - Get a formatted date/time string from a time value.
28 * httpGetDateString2() - Get a formatted date/time string from a time value.
29 * httpGetDateTime() - Get a time value from a formatted date/time string.
30 * httpSeparate() - Separate a Universal Resource Identifier into its
31 * components.
32 * httpSeparate2() - Separate a Universal Resource Identifier into its
33 * components.
34 * httpSeparateURI() - Separate a Universal Resource Identifier into its
35 * components.
36 * httpStatus() - Return a short string describing a HTTP status code.
37 * _cups_hstrerror() - hstrerror() emulation function for Solaris and
38 * others...
39 * _httpEncodeURI() - Percent-encode a HTTP request URI.
40 * _httpResolveURI() - Resolve a DNS-SD URI.
41 * http_copy_decode() - Copy and decode a URI.
42 * http_copy_encode() - Copy and encode a URI.
43 * resolve_callback() - Build a device URI for the given service name.
44 */
45
46 /*
47 * Include necessary headers...
48 */
49
50 #include "debug.h"
51 #include "globals.h"
52 #include <stdlib.h>
53 #ifdef HAVE_DNSSD
54 # include <dns_sd.h>
55 # include <poll.h>
56 #endif /* HAVE_DNSSD */
57
58
59 /*
60 * Local types...
61 */
62
63 typedef struct _http_uribuf_s /* URI buffer */
64 {
65 char *buffer; /* Pointer to buffer */
66 size_t bufsize; /* Size of buffer */
67 } _http_uribuf_t;
68
69
70 /*
71 * Local globals...
72 */
73
74 static const char * const http_days[7] =
75 {
76 "Sun",
77 "Mon",
78 "Tue",
79 "Wed",
80 "Thu",
81 "Fri",
82 "Sat"
83 };
84 static const char * const http_months[12] =
85 {
86 "Jan",
87 "Feb",
88 "Mar",
89 "Apr",
90 "May",
91 "Jun",
92 "Jul",
93 "Aug",
94 "Sep",
95 "Oct",
96 "Nov",
97 "Dec"
98 };
99
100
101 /*
102 * Local functions...
103 */
104
105 static const char *http_copy_decode(char *dst, const char *src,
106 int dstsize, const char *term,
107 int decode);
108 static char *http_copy_encode(char *dst, const char *src,
109 char *dstend, const char *reserved,
110 const char *term, int encode);
111 #ifdef HAVE_DNSSD
112 static void resolve_callback(DNSServiceRef sdRef,
113 DNSServiceFlags flags,
114 uint32_t interfaceIndex,
115 DNSServiceErrorType errorCode,
116 const char *fullName,
117 const char *hostTarget,
118 uint16_t port, uint16_t txtLen,
119 const unsigned char *txtRecord,
120 void *context);
121 #endif /* HAVE_DNSSD */
122
123
124 /*
125 * 'httpAssembleURI()' - Assemble a uniform resource identifier from its
126 * components.
127 *
128 * This function escapes reserved characters in the URI depending on the
129 * value of the "encoding" argument. You should use this function in
130 * place of traditional string functions whenever you need to create a
131 * URI string.
132 *
133 * @since CUPS 1.2/Mac OS X 10.5@
134 */
135
136 http_uri_status_t /* O - URI status */
137 httpAssembleURI(
138 http_uri_coding_t encoding, /* I - Encoding flags */
139 char *uri, /* I - URI buffer */
140 int urilen, /* I - Size of URI buffer */
141 const char *scheme, /* I - Scheme name */
142 const char *username, /* I - Username */
143 const char *host, /* I - Hostname or address */
144 int port, /* I - Port number */
145 const char *resource) /* I - Resource */
146 {
147 char *ptr, /* Pointer into URI buffer */
148 *end; /* End of URI buffer */
149
150
151 /*
152 * Range check input...
153 */
154
155 if (!uri || urilen < 1 || !scheme || port < 0)
156 {
157 if (uri)
158 *uri = '\0';
159
160 return (HTTP_URI_BAD_ARGUMENTS);
161 }
162
163 /*
164 * Assemble the URI starting with the scheme...
165 */
166
167 end = uri + urilen - 1;
168 ptr = http_copy_encode(uri, scheme, end, NULL, NULL, 0);
169
170 if (!ptr)
171 goto assemble_overflow;
172
173 if (!strcmp(scheme, "mailto"))
174 {
175 /*
176 * mailto: only has :, no //...
177 */
178
179 if (ptr < end)
180 *ptr++ = ':';
181 else
182 goto assemble_overflow;
183 }
184 else
185 {
186 /*
187 * Schemes other than mailto: all have //...
188 */
189
190 if ((ptr + 2) < end)
191 {
192 *ptr++ = ':';
193 *ptr++ = '/';
194 *ptr++ = '/';
195 }
196 else
197 goto assemble_overflow;
198 }
199
200 /*
201 * Next the username and hostname, if any...
202 */
203
204 if (host)
205 {
206 if (username && *username)
207 {
208 /*
209 * Add username@ first...
210 */
211
212 ptr = http_copy_encode(ptr, username, end, "/?@", NULL,
213 encoding & HTTP_URI_CODING_USERNAME);
214
215 if (!ptr)
216 goto assemble_overflow;
217
218 if (ptr < end)
219 *ptr++ = '@';
220 else
221 goto assemble_overflow;
222 }
223
224 /*
225 * Then add the hostname. Since IPv6 is a particular pain to deal
226 * with, we have several special cases to deal with. If we get
227 * an IPv6 address with brackets around it, assume it is already in
228 * URI format. Since DNS-SD service names can sometimes look like
229 * raw IPv6 addresses, we specifically look for "._tcp" in the name,
230 * too...
231 */
232
233 if (host[0] != '[' && strchr(host, ':') && !strstr(host, "._tcp"))
234 {
235 /*
236 * We have a raw IPv6 address...
237 */
238
239 if (strchr(host, '%'))
240 {
241 /*
242 * We have a link-local address, add "[v1." prefix...
243 */
244
245 if ((ptr + 4) < end)
246 {
247 *ptr++ = '[';
248 *ptr++ = 'v';
249 *ptr++ = '1';
250 *ptr++ = '.';
251 }
252 else
253 goto assemble_overflow;
254 }
255 else
256 {
257 /*
258 * We have a normal address, add "[" prefix...
259 */
260
261 if (ptr < end)
262 *ptr++ = '[';
263 else
264 goto assemble_overflow;
265 }
266
267 /*
268 * Copy the rest of the IPv6 address, and terminate with "]".
269 */
270
271 while (ptr < end && *host)
272 {
273 if (*host == '%')
274 {
275 *ptr++ = '+'; /* Convert zone separator */
276 host ++;
277 }
278 else
279 *ptr++ = *host++;
280 }
281
282 if (*host)
283 goto assemble_overflow;
284
285 if (ptr < end)
286 *ptr++ = ']';
287 else
288 goto assemble_overflow;
289 }
290 else
291 {
292 /*
293 * Otherwise, just copy the host string...
294 */
295
296 ptr = http_copy_encode(ptr, host, end, ":/?#[]@\\", NULL,
297 encoding & HTTP_URI_CODING_HOSTNAME);
298
299 if (!ptr)
300 goto assemble_overflow;
301 }
302
303 /*
304 * Finish things off with the port number...
305 */
306
307 if (port > 0)
308 {
309 snprintf(ptr, end - ptr + 1, ":%d", port);
310 ptr += strlen(ptr);
311
312 if (ptr >= end)
313 goto assemble_overflow;
314 }
315 }
316
317 /*
318 * Last but not least, add the resource string...
319 */
320
321 if (resource)
322 {
323 char *query; /* Pointer to query string */
324
325
326 /*
327 * Copy the resource string up to the query string if present...
328 */
329
330 query = strchr(resource, '?');
331 ptr = http_copy_encode(ptr, resource, end, NULL, "?",
332 encoding & HTTP_URI_CODING_RESOURCE);
333 if (!ptr)
334 goto assemble_overflow;
335
336 if (query)
337 {
338 /*
339 * Copy query string without encoding...
340 */
341
342 ptr = http_copy_encode(ptr, query, end, NULL, NULL,
343 encoding & HTTP_URI_CODING_QUERY);
344 if (!ptr)
345 goto assemble_overflow;
346 }
347 }
348 else if (ptr < end)
349 *ptr++ = '/';
350 else
351 goto assemble_overflow;
352
353 /*
354 * Nul-terminate the URI buffer and return with no errors...
355 */
356
357 *ptr = '\0';
358
359 return (HTTP_URI_OK);
360
361 /*
362 * Clear the URI string and return an overflow error; I don't usually
363 * like goto's, but in this case it makes sense...
364 */
365
366 assemble_overflow:
367
368 *uri = '\0';
369 return (HTTP_URI_OVERFLOW);
370 }
371
372
373 /*
374 * 'httpAssembleURIf()' - Assemble a uniform resource identifier from its
375 * components with a formatted resource.
376 *
377 * This function creates a formatted version of the resource string
378 * argument "resourcef" and escapes reserved characters in the URI
379 * depending on the value of the "encoding" argument. You should use
380 * this function in place of traditional string functions whenever
381 * you need to create a URI string.
382 *
383 * @since CUPS 1.2/Mac OS X 10.5@
384 */
385
386 http_uri_status_t /* O - URI status */
387 httpAssembleURIf(
388 http_uri_coding_t encoding, /* I - Encoding flags */
389 char *uri, /* I - URI buffer */
390 int urilen, /* I - Size of URI buffer */
391 const char *scheme, /* I - Scheme name */
392 const char *username, /* I - Username */
393 const char *host, /* I - Hostname or address */
394 int port, /* I - Port number */
395 const char *resourcef, /* I - Printf-style resource */
396 ...) /* I - Additional arguments as needed */
397 {
398 va_list ap; /* Pointer to additional arguments */
399 char resource[1024]; /* Formatted resource string */
400 int bytes; /* Bytes in formatted string */
401
402
403 /*
404 * Range check input...
405 */
406
407 if (!uri || urilen < 1 || !scheme || port < 0 || !resourcef)
408 {
409 if (uri)
410 *uri = '\0';
411
412 return (HTTP_URI_BAD_ARGUMENTS);
413 }
414
415 /*
416 * Format the resource string and assemble the URI...
417 */
418
419 va_start(ap, resourcef);
420 bytes = vsnprintf(resource, sizeof(resource), resourcef, ap);
421 va_end(ap);
422
423 if (bytes >= sizeof(resource))
424 {
425 *uri = '\0';
426 return (HTTP_URI_OVERFLOW);
427 }
428 else
429 return (httpAssembleURI(encoding, uri, urilen, scheme, username, host,
430 port, resource));
431 }
432
433
434 /*
435 * 'httpDecode64()' - Base64-decode a string.
436 *
437 * This function is deprecated. Use the httpDecode64_2() function instead
438 * which provides buffer length arguments.
439 *
440 * @deprecated@
441 */
442
443 char * /* O - Decoded string */
444 httpDecode64(char *out, /* I - String to write to */
445 const char *in) /* I - String to read from */
446 {
447 int outlen; /* Output buffer length */
448
449
450 /*
451 * Use the old maximum buffer size for binary compatibility...
452 */
453
454 outlen = 512;
455
456 return (httpDecode64_2(out, &outlen, in));
457 }
458
459
460 /*
461 * 'httpDecode64_2()' - Base64-decode a string.
462 *
463 * @since CUPS 1.1.21/Mac OS X 10.4@
464 */
465
466 char * /* O - Decoded string */
467 httpDecode64_2(char *out, /* I - String to write to */
468 int *outlen, /* IO - Size of output string */
469 const char *in) /* I - String to read from */
470 {
471 int pos, /* Bit position */
472 base64; /* Value of this character */
473 char *outptr, /* Output pointer */
474 *outend; /* End of output buffer */
475
476
477 /*
478 * Range check input...
479 */
480
481 if (!out || !outlen || *outlen < 1 || !in)
482 return (NULL);
483
484 if (!*in)
485 {
486 *out = '\0';
487 *outlen = 0;
488
489 return (out);
490 }
491
492 /*
493 * Convert from base-64 to bytes...
494 */
495
496 for (outptr = out, outend = out + *outlen - 1, pos = 0; *in != '\0'; in ++)
497 {
498 /*
499 * Decode this character into a number from 0 to 63...
500 */
501
502 if (*in >= 'A' && *in <= 'Z')
503 base64 = *in - 'A';
504 else if (*in >= 'a' && *in <= 'z')
505 base64 = *in - 'a' + 26;
506 else if (*in >= '0' && *in <= '9')
507 base64 = *in - '0' + 52;
508 else if (*in == '+')
509 base64 = 62;
510 else if (*in == '/')
511 base64 = 63;
512 else if (*in == '=')
513 break;
514 else
515 continue;
516
517 /*
518 * Store the result in the appropriate chars...
519 */
520
521 switch (pos)
522 {
523 case 0 :
524 if (outptr < outend)
525 *outptr = base64 << 2;
526 pos ++;
527 break;
528 case 1 :
529 if (outptr < outend)
530 *outptr++ |= (base64 >> 4) & 3;
531 if (outptr < outend)
532 *outptr = (base64 << 4) & 255;
533 pos ++;
534 break;
535 case 2 :
536 if (outptr < outend)
537 *outptr++ |= (base64 >> 2) & 15;
538 if (outptr < outend)
539 *outptr = (base64 << 6) & 255;
540 pos ++;
541 break;
542 case 3 :
543 if (outptr < outend)
544 *outptr++ |= base64;
545 pos = 0;
546 break;
547 }
548 }
549
550 *outptr = '\0';
551
552 /*
553 * Return the decoded string and size...
554 */
555
556 *outlen = (int)(outptr - out);
557
558 return (out);
559 }
560
561
562 /*
563 * 'httpEncode64()' - Base64-encode a string.
564 *
565 * This function is deprecated. Use the httpEncode64_2() function instead
566 * which provides buffer length arguments.
567 *
568 * @deprecated@
569 */
570
571 char * /* O - Encoded string */
572 httpEncode64(char *out, /* I - String to write to */
573 const char *in) /* I - String to read from */
574 {
575 return (httpEncode64_2(out, 512, in, (int)strlen(in)));
576 }
577
578
579 /*
580 * 'httpEncode64_2()' - Base64-encode a string.
581 *
582 * @since CUPS 1.1.21/Mac OS X 10.4@
583 */
584
585 char * /* O - Encoded string */
586 httpEncode64_2(char *out, /* I - String to write to */
587 int outlen, /* I - Size of output string */
588 const char *in, /* I - String to read from */
589 int inlen) /* I - Size of input string */
590 {
591 char *outptr, /* Output pointer */
592 *outend; /* End of output buffer */
593 static const char base64[] = /* Base64 characters... */
594 {
595 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
596 "abcdefghijklmnopqrstuvwxyz"
597 "0123456789"
598 "+/"
599 };
600
601
602 /*
603 * Range check input...
604 */
605
606 if (!out || outlen < 1 || !in)
607 return (NULL);
608
609 /*
610 * Convert bytes to base-64...
611 */
612
613 for (outptr = out, outend = out + outlen - 1; inlen > 0; in ++, inlen --)
614 {
615 /*
616 * Encode the up to 3 characters as 4 Base64 numbers...
617 */
618
619 if (outptr < outend)
620 *outptr ++ = base64[(in[0] & 255) >> 2];
621
622 if (outptr < outend)
623 {
624 if (inlen > 1)
625 *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
626 else
627 *outptr ++ = base64[((in[0] & 255) << 4) & 63];
628 }
629
630 in ++;
631 inlen --;
632 if (inlen <= 0)
633 {
634 if (outptr < outend)
635 *outptr ++ = '=';
636 if (outptr < outend)
637 *outptr ++ = '=';
638 break;
639 }
640
641 if (outptr < outend)
642 {
643 if (inlen > 1)
644 *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
645 else
646 *outptr ++ = base64[((in[0] & 255) << 2) & 63];
647 }
648
649 in ++;
650 inlen --;
651 if (inlen <= 0)
652 {
653 if (outptr < outend)
654 *outptr ++ = '=';
655 break;
656 }
657
658 if (outptr < outend)
659 *outptr ++ = base64[in[0] & 63];
660 }
661
662 *outptr = '\0';
663
664 /*
665 * Return the encoded string...
666 */
667
668 return (out);
669 }
670
671
672 /*
673 * 'httpGetDateString()' - Get a formatted date/time string from a time value.
674 *
675 * @deprecated@
676 */
677
678 const char * /* O - Date/time string */
679 httpGetDateString(time_t t) /* I - UNIX time */
680 {
681 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
682
683
684 return (httpGetDateString2(t, cg->http_date, sizeof(cg->http_date)));
685 }
686
687
688 /*
689 * 'httpGetDateString2()' - Get a formatted date/time string from a time value.
690 *
691 * @since CUPS 1.2/Mac OS X 10.5@
692 */
693
694 const char * /* O - Date/time string */
695 httpGetDateString2(time_t t, /* I - UNIX time */
696 char *s, /* I - String buffer */
697 int slen) /* I - Size of string buffer */
698 {
699 struct tm *tdate; /* UNIX date/time data */
700
701
702 tdate = gmtime(&t);
703 snprintf(s, slen, "%s, %02d %s %d %02d:%02d:%02d GMT",
704 http_days[tdate->tm_wday], tdate->tm_mday,
705 http_months[tdate->tm_mon], tdate->tm_year + 1900,
706 tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
707
708 return (s);
709 }
710
711
712 /*
713 * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
714 */
715
716 time_t /* O - UNIX time */
717 httpGetDateTime(const char *s) /* I - Date/time string */
718 {
719 int i; /* Looping var */
720 char mon[16]; /* Abbreviated month name */
721 int day, year; /* Day of month and year */
722 int hour, min, sec; /* Time */
723 int days; /* Number of days since 1970 */
724 static const int normal_days[] = /* Days to a month, normal years */
725 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
726 static const int leap_days[] = /* Days to a month, leap years */
727 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
728
729
730 DEBUG_printf(("2httpGetDateTime(s=\"%s\")", s));
731
732 /*
733 * Extract the date and time from the formatted string...
734 */
735
736 if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
737 return (0);
738
739 DEBUG_printf(("4httpGetDateTime: day=%d, mon=\"%s\", year=%d, hour=%d, "
740 "min=%d, sec=%d", day, mon, year, hour, min, sec));
741
742 /*
743 * Convert the month name to a number from 0 to 11.
744 */
745
746 for (i = 0; i < 12; i ++)
747 if (!strcasecmp(mon, http_months[i]))
748 break;
749
750 if (i >= 12)
751 return (0);
752
753 DEBUG_printf(("4httpGetDateTime: i=%d", i));
754
755 /*
756 * Now convert the date and time to a UNIX time value in seconds since
757 * 1970. We can't use mktime() since the timezone may not be UTC but
758 * the date/time string *is* UTC.
759 */
760
761 if ((year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0))
762 days = leap_days[i] + day - 1;
763 else
764 days = normal_days[i] + day - 1;
765
766 DEBUG_printf(("4httpGetDateTime: days=%d", days));
767
768 days += (year - 1970) * 365 + /* 365 days per year (normally) */
769 ((year - 1) / 4 - 492) - /* + leap days */
770 ((year - 1) / 100 - 19) + /* - 100 year days */
771 ((year - 1) / 400 - 4); /* + 400 year days */
772
773 DEBUG_printf(("4httpGetDateTime: days=%d\n", days));
774
775 return (days * 86400 + hour * 3600 + min * 60 + sec);
776 }
777
778
779 /*
780 * 'httpSeparate()' - Separate a Universal Resource Identifier into its
781 * components.
782 *
783 * This function is deprecated; use the httpSeparateURI() function instead.
784 *
785 * @deprecated@
786 */
787
788 void
789 httpSeparate(const char *uri, /* I - Universal Resource Identifier */
790 char *scheme, /* O - Scheme [32] (http, https, etc.) */
791 char *username, /* O - Username [1024] */
792 char *host, /* O - Hostname [1024] */
793 int *port, /* O - Port number to use */
794 char *resource) /* O - Resource/filename [1024] */
795 {
796 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username,
797 HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource,
798 HTTP_MAX_URI);
799 }
800
801
802 /*
803 * 'httpSeparate2()' - Separate a Universal Resource Identifier into its
804 * components.
805 *
806 * This function is deprecated; use the httpSeparateURI() function instead.
807 *
808 * @since CUPS 1.1.21/Mac OS X 10.4@
809 * @deprecated@
810 */
811
812 void
813 httpSeparate2(const char *uri, /* I - Universal Resource Identifier */
814 char *scheme, /* O - Scheme (http, https, etc.) */
815 int schemelen, /* I - Size of scheme buffer */
816 char *username, /* O - Username */
817 int usernamelen, /* I - Size of username buffer */
818 char *host, /* O - Hostname */
819 int hostlen, /* I - Size of hostname buffer */
820 int *port, /* O - Port number to use */
821 char *resource, /* O - Resource/filename */
822 int resourcelen) /* I - Size of resource buffer */
823 {
824 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username,
825 usernamelen, host, hostlen, port, resource, resourcelen);
826 }
827
828
829 /*
830 * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its
831 * components.
832 *
833 * @since CUPS 1.2/Mac OS X 10.5@
834 */
835
836 http_uri_status_t /* O - Result of separation */
837 httpSeparateURI(
838 http_uri_coding_t decoding, /* I - Decoding flags */
839 const char *uri, /* I - Universal Resource Identifier */
840 char *scheme, /* O - Scheme (http, https, etc.) */
841 int schemelen, /* I - Size of scheme buffer */
842 char *username, /* O - Username */
843 int usernamelen, /* I - Size of username buffer */
844 char *host, /* O - Hostname */
845 int hostlen, /* I - Size of hostname buffer */
846 int *port, /* O - Port number to use */
847 char *resource, /* O - Resource/filename */
848 int resourcelen) /* I - Size of resource buffer */
849 {
850 char *ptr, /* Pointer into string... */
851 *end; /* End of string */
852 const char *sep; /* Separator character */
853 http_uri_status_t status; /* Result of separation */
854
855
856 /*
857 * Initialize everything to blank...
858 */
859
860 if (scheme && schemelen > 0)
861 *scheme = '\0';
862
863 if (username && usernamelen > 0)
864 *username = '\0';
865
866 if (host && hostlen > 0)
867 *host = '\0';
868
869 if (port)
870 *port = 0;
871
872 if (resource && resourcelen > 0)
873 *resource = '\0';
874
875 /*
876 * Range check input...
877 */
878
879 if (!uri || !port || !scheme || schemelen <= 0 || !username ||
880 usernamelen <= 0 || !host || hostlen <= 0 || !resource ||
881 resourcelen <= 0)
882 return (HTTP_URI_BAD_ARGUMENTS);
883
884 if (!*uri)
885 return (HTTP_URI_BAD_URI);
886
887 /*
888 * Grab the scheme portion of the URI...
889 */
890
891 status = HTTP_URI_OK;
892
893 if (!strncmp(uri, "//", 2))
894 {
895 /*
896 * Workaround for HP IPP client bug...
897 */
898
899 strlcpy(scheme, "ipp", schemelen);
900 status = HTTP_URI_MISSING_SCHEME;
901 }
902 else if (*uri == '/')
903 {
904 /*
905 * Filename...
906 */
907
908 strlcpy(scheme, "file", schemelen);
909 status = HTTP_URI_MISSING_SCHEME;
910 }
911 else
912 {
913 /*
914 * Standard URI with scheme...
915 */
916
917 for (ptr = scheme, end = scheme + schemelen - 1;
918 *uri && *uri != ':' && ptr < end;)
919 if (isalnum(*uri & 255) || *uri == '-' || *uri == '+' || *uri == '.')
920 *ptr++ = *uri++;
921 else
922 break;
923
924 *ptr = '\0';
925
926 if (*uri != ':')
927 {
928 *scheme = '\0';
929 return (HTTP_URI_BAD_SCHEME);
930 }
931
932 uri ++;
933 }
934
935 /*
936 * Set the default port number...
937 */
938
939 if (!strcmp(scheme, "http"))
940 *port = 80;
941 else if (!strcmp(scheme, "https"))
942 *port = 443;
943 else if (!strcmp(scheme, "ipp"))
944 *port = 631;
945 else if (!strcasecmp(scheme, "lpd"))
946 *port = 515;
947 else if (!strcmp(scheme, "socket")) /* Not yet registered with IANA... */
948 *port = 9100;
949 else if (strcmp(scheme, "file") && strcmp(scheme, "mailto"))
950 status = HTTP_URI_UNKNOWN_SCHEME;
951
952 /*
953 * Now see if we have a hostname...
954 */
955
956 if (!strncmp(uri, "//", 2))
957 {
958 /*
959 * Yes, extract it...
960 */
961
962 uri += 2;
963
964 /*
965 * Grab the username, if any...
966 */
967
968 if ((sep = strpbrk(uri, "@/")) != NULL && *sep == '@')
969 {
970 /*
971 * Get a username:password combo...
972 */
973
974 uri = http_copy_decode(username, uri, usernamelen, "@",
975 decoding & HTTP_URI_CODING_USERNAME);
976
977 if (!uri)
978 {
979 *username = '\0';
980 return (HTTP_URI_BAD_USERNAME);
981 }
982
983 uri ++;
984 }
985
986 /*
987 * Then the hostname/IP address...
988 */
989
990 if (*uri == '[')
991 {
992 /*
993 * Grab IPv6 address...
994 */
995
996 uri ++;
997 if (!strncmp(uri, "v1.", 3))
998 uri += 3; /* Skip IPvN leader... */
999
1000 uri = http_copy_decode(host, uri, hostlen, "]",
1001 decoding & HTTP_URI_CODING_HOSTNAME);
1002
1003 if (!uri)
1004 {
1005 *host = '\0';
1006 return (HTTP_URI_BAD_HOSTNAME);
1007 }
1008
1009 /*
1010 * Validate value...
1011 */
1012
1013 if (*uri != ']')
1014 {
1015 *host = '\0';
1016 return (HTTP_URI_BAD_HOSTNAME);
1017 }
1018
1019 uri ++;
1020
1021 for (ptr = host; *ptr; ptr ++)
1022 if (*ptr == '+')
1023 {
1024 /*
1025 * Convert zone separator to % and stop here...
1026 */
1027
1028 *ptr = '%';
1029 break;
1030 }
1031 else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255))
1032 {
1033 *host = '\0';
1034 return (HTTP_URI_BAD_HOSTNAME);
1035 }
1036 }
1037 else
1038 {
1039 /*
1040 * Validate the hostname or IPv4 address first...
1041 */
1042
1043 for (ptr = (char *)uri; *ptr; ptr ++)
1044 if (strchr(":?/", *ptr))
1045 break;
1046 else if (!strchr("abcdefghijklmnopqrstuvwxyz"
1047 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1048 "0123456789"
1049 "-._~"
1050 "%"
1051 "!$&'()*+,;=\\", *ptr))
1052 {
1053 *host = '\0';
1054 return (HTTP_URI_BAD_HOSTNAME);
1055 }
1056
1057 /*
1058 * Then copy the hostname or IPv4 address to the buffer...
1059 */
1060
1061 uri = http_copy_decode(host, uri, hostlen, ":?/",
1062 decoding & HTTP_URI_CODING_HOSTNAME);
1063
1064 if (!uri)
1065 {
1066 *host = '\0';
1067 return (HTTP_URI_BAD_HOSTNAME);
1068 }
1069 }
1070
1071 /*
1072 * Validate hostname for file scheme - only empty and localhost are
1073 * acceptable.
1074 */
1075
1076 if (!strcmp(scheme, "file") && strcmp(host, "localhost") && host[0])
1077 {
1078 *host = '\0';
1079 return (HTTP_URI_BAD_HOSTNAME);
1080 }
1081
1082 /*
1083 * See if we have a port number...
1084 */
1085
1086 if (*uri == ':')
1087 {
1088 /*
1089 * Yes, collect the port number...
1090 */
1091
1092 if (!isdigit(uri[1] & 255))
1093 {
1094 *port = 0;
1095 return (HTTP_URI_BAD_PORT);
1096 }
1097
1098 *port = strtol(uri + 1, (char **)&uri, 10);
1099
1100 if (*uri != '/' && *uri)
1101 {
1102 *port = 0;
1103 return (HTTP_URI_BAD_PORT);
1104 }
1105 }
1106 }
1107
1108 /*
1109 * The remaining portion is the resource string...
1110 */
1111
1112 if (*uri == '?' || !*uri)
1113 {
1114 /*
1115 * Hostname but no path...
1116 */
1117
1118 status = HTTP_URI_MISSING_RESOURCE;
1119 *resource = '/';
1120
1121 /*
1122 * Copy any query string...
1123 */
1124
1125 if (*uri == '?')
1126 uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL,
1127 decoding & HTTP_URI_CODING_QUERY);
1128 else
1129 resource[1] = '\0';
1130 }
1131 else
1132 {
1133 uri = http_copy_decode(resource, uri, resourcelen, "?",
1134 decoding & HTTP_URI_CODING_RESOURCE);
1135
1136 if (uri && *uri == '?')
1137 {
1138 /*
1139 * Concatenate any query string...
1140 */
1141
1142 char *resptr = resource + strlen(resource);
1143
1144 uri = http_copy_decode(resptr, uri, resourcelen - (int)(resptr - resource),
1145 NULL, decoding & HTTP_URI_CODING_QUERY);
1146 }
1147 }
1148
1149 if (!uri)
1150 {
1151 *resource = '\0';
1152 return (HTTP_URI_BAD_RESOURCE);
1153 }
1154
1155 /*
1156 * Return the URI separation status...
1157 */
1158
1159 return (status);
1160 }
1161
1162
1163 /*
1164 * 'httpStatus()' - Return a short string describing a HTTP status code.
1165 *
1166 * The returned string is localized to the current POSIX locale and is based
1167 * on the status strings defined in RFC 2616.
1168 */
1169
1170 const char * /* O - Localized status string */
1171 httpStatus(http_status_t status) /* I - HTTP status code */
1172 {
1173 const char *s; /* Status string */
1174 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
1175
1176
1177 if (!cg->lang_default)
1178 cg->lang_default = cupsLangDefault();
1179
1180 switch (status)
1181 {
1182 case HTTP_CONTINUE :
1183 s = _("Continue");
1184 break;
1185 case HTTP_SWITCHING_PROTOCOLS :
1186 s = _("Switching Protocols");
1187 break;
1188 case HTTP_OK :
1189 s = _("OK");
1190 break;
1191 case HTTP_CREATED :
1192 s = _("Created");
1193 break;
1194 case HTTP_ACCEPTED :
1195 s = _("Accepted");
1196 break;
1197 case HTTP_NO_CONTENT :
1198 s = _("No Content");
1199 break;
1200 case HTTP_MOVED_PERMANENTLY :
1201 s = _("Moved Permanently");
1202 break;
1203 case HTTP_SEE_OTHER :
1204 s = _("See Other");
1205 break;
1206 case HTTP_NOT_MODIFIED :
1207 s = _("Not Modified");
1208 break;
1209 case HTTP_BAD_REQUEST :
1210 s = _("Bad Request");
1211 break;
1212 case HTTP_UNAUTHORIZED :
1213 case HTTP_AUTHORIZATION_CANCELED :
1214 s = _("Unauthorized");
1215 break;
1216 case HTTP_FORBIDDEN :
1217 s = _("Forbidden");
1218 break;
1219 case HTTP_NOT_FOUND :
1220 s = _("Not Found");
1221 break;
1222 case HTTP_REQUEST_TOO_LARGE :
1223 s = _("Request Entity Too Large");
1224 break;
1225 case HTTP_URI_TOO_LONG :
1226 s = _("URI Too Long");
1227 break;
1228 case HTTP_UPGRADE_REQUIRED :
1229 s = _("Upgrade Required");
1230 break;
1231 case HTTP_NOT_IMPLEMENTED :
1232 s = _("Not Implemented");
1233 break;
1234 case HTTP_NOT_SUPPORTED :
1235 s = _("Not Supported");
1236 break;
1237 case HTTP_EXPECTATION_FAILED :
1238 s = _("Expectation Failed");
1239 break;
1240 case HTTP_SERVICE_UNAVAILABLE :
1241 s = _("Service Unavailable");
1242 break;
1243 case HTTP_SERVER_ERROR :
1244 s = _("Internal Server Error");
1245 break;
1246
1247 default :
1248 s = _("Unknown");
1249 break;
1250 }
1251
1252 return (_cupsLangString(cg->lang_default, s));
1253 }
1254
1255
1256 #ifndef HAVE_HSTRERROR
1257 /*
1258 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others...
1259 */
1260
1261 const char * /* O - Error string */
1262 _cups_hstrerror(int error) /* I - Error number */
1263 {
1264 static const char * const errors[] = /* Error strings */
1265 {
1266 "OK",
1267 "Host not found.",
1268 "Try again.",
1269 "Unrecoverable lookup error.",
1270 "No data associated with name."
1271 };
1272
1273
1274 if (error < 0 || error > 4)
1275 return ("Unknown hostname lookup error.");
1276 else
1277 return (errors[error]);
1278 }
1279 #endif /* !HAVE_HSTRERROR */
1280
1281
1282 /*
1283 * '_httpEncodeURI()' - Percent-encode a HTTP request URI.
1284 */
1285
1286 char * /* O - Encoded URI */
1287 _httpEncodeURI(char *dst, /* I - Destination buffer */
1288 const char *src, /* I - Source URI */
1289 size_t dstsize) /* I - Size of destination buffer */
1290 {
1291 http_copy_encode(dst, src, dst + dstsize - 1, NULL, NULL, 1);
1292 return (dst);
1293 }
1294
1295
1296 /*
1297 * '_httpResolveURI()' - Resolve a DNS-SD URI.
1298 */
1299
1300 const char * /* O - Resolved URI */
1301 _httpResolveURI(
1302 const char *uri, /* I - DNS-SD URI */
1303 char *resolved_uri, /* I - Buffer for resolved URI */
1304 size_t resolved_size, /* I - Size of URI buffer */
1305 int logit) /* I - Log progress to stderr? */
1306 {
1307 char scheme[32], /* URI components... */
1308 userpass[256],
1309 hostname[1024],
1310 resource[1024];
1311 int port;
1312 #ifdef DEBUG
1313 http_uri_status_t status; /* URI decode status */
1314 #endif /* DEBUG */
1315
1316
1317 DEBUG_printf(("4_httpResolveURI(uri=\"%s\", resolved_uri=%p, "
1318 "resolved_size=" CUPS_LLFMT ")", uri, resolved_uri,
1319 CUPS_LLCAST resolved_size));
1320
1321 /*
1322 * Get the device URI...
1323 */
1324
1325 #ifdef DEBUG
1326 if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1327 sizeof(scheme), userpass, sizeof(userpass),
1328 hostname, sizeof(hostname), &port, resource,
1329 sizeof(resource))) < HTTP_URI_OK)
1330 #else
1331 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1332 sizeof(scheme), userpass, sizeof(userpass),
1333 hostname, sizeof(hostname), &port, resource,
1334 sizeof(resource)) < HTTP_URI_OK)
1335 #endif /* DEBUG */
1336 {
1337 if (logit)
1338 _cupsLangPrintf(stderr, _("Bad device URI \"%s\"!\n"), uri);
1339
1340 DEBUG_printf(("6_httpResolveURI: httpSeparateURI returned %d!", status));
1341 DEBUG_puts("5_httpResolveURI: Returning NULL");
1342 return (NULL);
1343 }
1344
1345 /*
1346 * Resolve it as needed...
1347 */
1348
1349 if (strstr(hostname, "._tcp"))
1350 {
1351 #ifdef HAVE_DNSSD
1352 DNSServiceRef ref, /* DNS-SD master service reference */
1353 domainref, /* DNS-SD service reference for domain */
1354 localref; /* DNS-SD service reference for .local */
1355 int domainsent = 0; /* Send the domain resolve? */
1356 char *regtype, /* Pointer to type in hostname */
1357 *domain; /* Pointer to domain in hostname */
1358 _http_uribuf_t uribuf; /* URI buffer */
1359 struct pollfd polldata; /* Polling data */
1360
1361
1362 if (logit)
1363 fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname);
1364
1365 /*
1366 * Separate the hostname into service name, registration type, and domain...
1367 */
1368
1369 for (regtype = strstr(hostname, "._tcp") - 2;
1370 regtype > hostname;
1371 regtype --)
1372 if (regtype[0] == '.' && regtype[1] == '_')
1373 {
1374 /*
1375 * Found ._servicetype in front of ._tcp...
1376 */
1377
1378 *regtype++ = '\0';
1379 break;
1380 }
1381
1382 if (regtype <= hostname)
1383 {
1384 DEBUG_puts("5_httpResolveURI: Bad hostname, returning NULL");
1385 return (NULL);
1386 }
1387
1388 for (domain = strchr(regtype, '.');
1389 domain;
1390 domain = strchr(domain + 1, '.'))
1391 if (domain[1] != '_')
1392 break;
1393
1394 if (domain)
1395 *domain++ = '\0';
1396
1397 uribuf.buffer = resolved_uri;
1398 uribuf.bufsize = resolved_size;
1399
1400 resolved_uri[0] = '\0';
1401
1402 DEBUG_printf(("6_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", "
1403 "domain=\"%s\"\n", hostname, regtype, domain));
1404 if (logit)
1405 {
1406 fputs("STATE: +connecting-to-device\n", stderr);
1407 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1408 "domain=\"local.\"...\n", hostname, regtype);
1409 _cupsLangPuts(stderr, _("INFO: Looking for printer...\n"));
1410 }
1411
1412 uri = NULL;
1413
1414 if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError)
1415 {
1416 localref = ref;
1417 if (DNSServiceResolve(&localref, kDNSServiceFlagsShareConnection, 0,
1418 hostname, regtype, "local.", resolve_callback,
1419 &uribuf) == kDNSServiceErr_NoError)
1420 {
1421 if (strcasecmp(domain, "local."))
1422 {
1423 /*
1424 * Wait 2 seconds for a response to the local resolve; if nothing comes
1425 * in, do an additional domain resolution...
1426 */
1427
1428 polldata.fd = DNSServiceRefSockFD(ref);
1429 polldata.events = POLLIN;
1430
1431 if (poll(&polldata, 1, 2000) != 1)
1432 {
1433 /*
1434 * OK, send the domain name resolve...
1435 */
1436
1437 if (logit)
1438 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1439 "domain=\"%s\"...\n", hostname, regtype, domain);
1440
1441 domainref = ref;
1442 if (DNSServiceResolve(&domainref, kDNSServiceFlagsShareConnection, 0,
1443 hostname, regtype, domain, resolve_callback,
1444 &uribuf) == kDNSServiceErr_NoError)
1445 domainsent = 1;
1446 }
1447 }
1448
1449 if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError)
1450 uri = resolved_uri;
1451
1452 if (domainsent)
1453 DNSServiceRefDeallocate(domainref);
1454
1455 DNSServiceRefDeallocate(localref);
1456 }
1457
1458 DNSServiceRefDeallocate(ref);
1459 }
1460
1461 if (logit)
1462 {
1463 if (uri)
1464 fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri);
1465 else
1466 fputs("DEBUG: Unable to resolve URI!\n", stderr);
1467
1468 fputs("STATE: -connecting-to-device\n", stderr);
1469 }
1470
1471 #else
1472 /*
1473 * No DNS-SD support...
1474 */
1475
1476 uri = NULL;
1477 #endif /* HAVE_DNSSD */
1478
1479 if (logit && !uri)
1480 _cupsLangPuts(stderr, _("Unable to find printer!\n"));
1481 }
1482
1483 DEBUG_printf(("5_httpResolveURI: Returning \"%s\"", uri));
1484
1485 return (uri);
1486 }
1487
1488
1489 /*
1490 * 'http_copy_decode()' - Copy and decode a URI.
1491 */
1492
1493 static const char * /* O - New source pointer or NULL on error */
1494 http_copy_decode(char *dst, /* O - Destination buffer */
1495 const char *src, /* I - Source pointer */
1496 int dstsize, /* I - Destination size */
1497 const char *term, /* I - Terminating characters */
1498 int decode) /* I - Decode %-encoded values */
1499 {
1500 char *ptr, /* Pointer into buffer */
1501 *end; /* End of buffer */
1502 int quoted; /* Quoted character */
1503
1504
1505 /*
1506 * Copy the src to the destination until we hit a terminating character
1507 * or the end of the string.
1508 */
1509
1510 for (ptr = dst, end = dst + dstsize - 1;
1511 *src && (!term || !strchr(term, *src));
1512 src ++)
1513 if (ptr < end)
1514 {
1515 if (*src == '%' && decode)
1516 {
1517 if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255))
1518 {
1519 /*
1520 * Grab a hex-encoded character...
1521 */
1522
1523 src ++;
1524 if (isalpha(*src))
1525 quoted = (tolower(*src) - 'a' + 10) << 4;
1526 else
1527 quoted = (*src - '0') << 4;
1528
1529 src ++;
1530 if (isalpha(*src))
1531 quoted |= tolower(*src) - 'a' + 10;
1532 else
1533 quoted |= *src - '0';
1534
1535 *ptr++ = quoted;
1536 }
1537 else
1538 {
1539 /*
1540 * Bad hex-encoded character...
1541 */
1542
1543 *ptr = '\0';
1544 return (NULL);
1545 }
1546 }
1547 else
1548 *ptr++ = *src;
1549 }
1550
1551 *ptr = '\0';
1552
1553 return (src);
1554 }
1555
1556
1557 /*
1558 * 'http_copy_encode()' - Copy and encode a URI.
1559 */
1560
1561 static char * /* O - End of current URI */
1562 http_copy_encode(char *dst, /* O - Destination buffer */
1563 const char *src, /* I - Source pointer */
1564 char *dstend, /* I - End of destination buffer */
1565 const char *reserved, /* I - Extra reserved characters */
1566 const char *term, /* I - Terminating characters */
1567 int encode) /* I - %-encode reserved chars? */
1568 {
1569 static const char hex[] = "0123456789ABCDEF";
1570
1571
1572 while (*src && dst < dstend)
1573 {
1574 if (term && *src == *term)
1575 return (dst);
1576
1577 if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
1578 (reserved && strchr(reserved, *src))))
1579 {
1580 /*
1581 * Hex encode reserved characters...
1582 */
1583
1584 if ((dst + 2) >= dstend)
1585 break;
1586
1587 *dst++ = '%';
1588 *dst++ = hex[(*src >> 4) & 15];
1589 *dst++ = hex[*src & 15];
1590
1591 src ++;
1592 }
1593 else
1594 *dst++ = *src++;
1595 }
1596
1597 *dst = '\0';
1598
1599 if (*src)
1600 return (NULL);
1601 else
1602 return (dst);
1603 }
1604
1605
1606 #ifdef HAVE_DNSSD
1607 /*
1608 * 'resolve_callback()' - Build a device URI for the given service name.
1609 */
1610
1611 static void
1612 resolve_callback(
1613 DNSServiceRef sdRef, /* I - Service reference */
1614 DNSServiceFlags flags, /* I - Results flags */
1615 uint32_t interfaceIndex, /* I - Interface number */
1616 DNSServiceErrorType errorCode, /* I - Error, if any */
1617 const char *fullName, /* I - Full service name */
1618 const char *hostTarget, /* I - Hostname */
1619 uint16_t port, /* I - Port number */
1620 uint16_t txtLen, /* I - Length of TXT record */
1621 const unsigned char *txtRecord, /* I - TXT record data */
1622 void *context) /* I - Pointer to URI buffer */
1623 {
1624 const char *scheme; /* URI scheme */
1625 char rp[257]; /* Remote printer */
1626 const void *value; /* Value from TXT record */
1627 uint8_t valueLen; /* Length of value */
1628 _http_uribuf_t *uribuf; /* URI buffer */
1629
1630
1631 DEBUG_printf(("7resolve_callback(sdRef=%p, flags=%x, interfaceIndex=%u, "
1632 "errorCode=%d, fullName=\"%s\", hostTarget=\"%s\", port=%u, "
1633 "txtLen=%u, txtRecord=%p, context=%p)", sdRef, flags,
1634 interfaceIndex, errorCode, fullName, hostTarget, port, txtLen,
1635 txtRecord, context));
1636
1637 /*
1638 * Figure out the scheme from the full name...
1639 */
1640
1641 if (strstr(fullName, "._ipp") || strstr(fullName, "._fax-ipp"))
1642 scheme = "ipp";
1643 else if (strstr(fullName, "._printer."))
1644 scheme = "lpd";
1645 else if (strstr(fullName, "._pdl-datastream."))
1646 scheme = "socket";
1647 else
1648 scheme = "riousbprint";
1649
1650 /*
1651 * Extract the "remote printer" key from the TXT record...
1652 */
1653
1654 if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, "rp",
1655 &valueLen)) != NULL)
1656 {
1657 /*
1658 * Convert to resource by concatenating with a leading "/"...
1659 */
1660
1661 rp[0] = '/';
1662 memcpy(rp + 1, value, valueLen);
1663 rp[valueLen + 1] = '\0';
1664 }
1665 else
1666 rp[0] = '\0';
1667
1668 /*
1669 * Assemble the final device URI...
1670 */
1671
1672 uribuf = (_http_uribuf_t *)context;
1673
1674 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, uribuf->bufsize, scheme,
1675 NULL, hostTarget, ntohs(port), rp);
1676
1677 DEBUG_printf(("8resolve_callback: Resolved URI is \"%s\"...",
1678 uribuf->buffer));
1679 }
1680 #endif /* HAVE_DNSSD */
1681
1682
1683 /*
1684 * End of "$Id: http-support.c 7952 2008-09-17 00:56:20Z mike $".
1685 */