]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/http-support.c
Merge changes from CUPS 1.4svn-r8540.
[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 s = _("Unauthorized");
1214 break;
1215 case HTTP_FORBIDDEN :
1216 s = _("Forbidden");
1217 break;
1218 case HTTP_NOT_FOUND :
1219 s = _("Not Found");
1220 break;
1221 case HTTP_REQUEST_TOO_LARGE :
1222 s = _("Request Entity Too Large");
1223 break;
1224 case HTTP_URI_TOO_LONG :
1225 s = _("URI Too Long");
1226 break;
1227 case HTTP_UPGRADE_REQUIRED :
1228 s = _("Upgrade Required");
1229 break;
1230 case HTTP_NOT_IMPLEMENTED :
1231 s = _("Not Implemented");
1232 break;
1233 case HTTP_NOT_SUPPORTED :
1234 s = _("Not Supported");
1235 break;
1236 case HTTP_EXPECTATION_FAILED :
1237 s = _("Expectation Failed");
1238 break;
1239 case HTTP_SERVICE_UNAVAILABLE :
1240 s = _("Service Unavailable");
1241 break;
1242 case HTTP_SERVER_ERROR :
1243 s = _("Internal Server Error");
1244 break;
1245
1246 default :
1247 s = _("Unknown");
1248 break;
1249 }
1250
1251 return (_cupsLangString(cg->lang_default, s));
1252 }
1253
1254
1255 #ifndef HAVE_HSTRERROR
1256 /*
1257 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others...
1258 */
1259
1260 const char * /* O - Error string */
1261 _cups_hstrerror(int error) /* I - Error number */
1262 {
1263 static const char * const errors[] = /* Error strings */
1264 {
1265 "OK",
1266 "Host not found.",
1267 "Try again.",
1268 "Unrecoverable lookup error.",
1269 "No data associated with name."
1270 };
1271
1272
1273 if (error < 0 || error > 4)
1274 return ("Unknown hostname lookup error.");
1275 else
1276 return (errors[error]);
1277 }
1278 #endif /* !HAVE_HSTRERROR */
1279
1280
1281 /*
1282 * '_httpEncodeURI()' - Percent-encode a HTTP request URI.
1283 */
1284
1285 char * /* O - Encoded URI */
1286 _httpEncodeURI(char *dst, /* I - Destination buffer */
1287 const char *src, /* I - Source URI */
1288 size_t dstsize) /* I - Size of destination buffer */
1289 {
1290 http_copy_encode(dst, src, dst + dstsize - 1, NULL, NULL, 1);
1291 return (dst);
1292 }
1293
1294
1295 /*
1296 * '_httpResolveURI()' - Resolve a DNS-SD URI.
1297 */
1298
1299 const char * /* O - Resolved URI */
1300 _httpResolveURI(
1301 const char *uri, /* I - DNS-SD URI */
1302 char *resolved_uri, /* I - Buffer for resolved URI */
1303 size_t resolved_size, /* I - Size of URI buffer */
1304 int logit) /* I - Log progress to stderr? */
1305 {
1306 char scheme[32], /* URI components... */
1307 userpass[256],
1308 hostname[1024],
1309 resource[1024];
1310 int port;
1311 #ifdef DEBUG
1312 http_uri_status_t status; /* URI decode status */
1313 #endif /* DEBUG */
1314
1315
1316 DEBUG_printf(("4_httpResolveURI(uri=\"%s\", resolved_uri=%p, "
1317 "resolved_size=" CUPS_LLFMT ")", uri, resolved_uri,
1318 CUPS_LLCAST resolved_size));
1319
1320 /*
1321 * Get the device URI...
1322 */
1323
1324 #ifdef DEBUG
1325 if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1326 sizeof(scheme), userpass, sizeof(userpass),
1327 hostname, sizeof(hostname), &port, resource,
1328 sizeof(resource))) < HTTP_URI_OK)
1329 #else
1330 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1331 sizeof(scheme), userpass, sizeof(userpass),
1332 hostname, sizeof(hostname), &port, resource,
1333 sizeof(resource)) < HTTP_URI_OK)
1334 #endif /* DEBUG */
1335 {
1336 if (logit)
1337 _cupsLangPrintf(stderr, _("Bad device URI \"%s\"!\n"), uri);
1338
1339 DEBUG_printf(("6_httpResolveURI: httpSeparateURI returned %d!", status));
1340 DEBUG_puts("5_httpResolveURI: Returning NULL");
1341 return (NULL);
1342 }
1343
1344 /*
1345 * Resolve it as needed...
1346 */
1347
1348 if (strstr(hostname, "._tcp"))
1349 {
1350 #ifdef HAVE_DNSSD
1351 DNSServiceRef ref, /* DNS-SD master service reference */
1352 domainref, /* DNS-SD service reference for domain */
1353 localref; /* DNS-SD service reference for .local */
1354 int domainsent = 0; /* Send the domain resolve? */
1355 char *regtype, /* Pointer to type in hostname */
1356 *domain; /* Pointer to domain in hostname */
1357 _http_uribuf_t uribuf; /* URI buffer */
1358 struct pollfd polldata; /* Polling data */
1359
1360
1361 if (logit)
1362 fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname);
1363
1364 /*
1365 * Separate the hostname into service name, registration type, and domain...
1366 */
1367
1368 for (regtype = strstr(hostname, "._tcp") - 2;
1369 regtype > hostname;
1370 regtype --)
1371 if (regtype[0] == '.' && regtype[1] == '_')
1372 {
1373 /*
1374 * Found ._servicetype in front of ._tcp...
1375 */
1376
1377 *regtype++ = '\0';
1378 break;
1379 }
1380
1381 if (regtype <= hostname)
1382 {
1383 DEBUG_puts("5_httpResolveURI: Bad hostname, returning NULL");
1384 return (NULL);
1385 }
1386
1387 for (domain = strchr(regtype, '.');
1388 domain;
1389 domain = strchr(domain + 1, '.'))
1390 if (domain[1] != '_')
1391 break;
1392
1393 if (domain)
1394 *domain++ = '\0';
1395
1396 uribuf.buffer = resolved_uri;
1397 uribuf.bufsize = resolved_size;
1398
1399 resolved_uri[0] = '\0';
1400
1401 DEBUG_printf(("6_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", "
1402 "domain=\"%s\"\n", hostname, regtype, domain));
1403 if (logit)
1404 {
1405 fputs("STATE: +connecting-to-device\n", stderr);
1406 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1407 "domain=\"local.\"...\n", hostname, regtype);
1408 _cupsLangPuts(stderr, _("INFO: Looking for printer...\n"));
1409 }
1410
1411 uri = NULL;
1412
1413 if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError)
1414 {
1415 localref = ref;
1416 if (DNSServiceResolve(&localref, kDNSServiceFlagsShareConnection, 0,
1417 hostname, regtype, "local.", resolve_callback,
1418 &uribuf) == kDNSServiceErr_NoError)
1419 {
1420 if (strcasecmp(domain, "local."))
1421 {
1422 /*
1423 * Wait 2 seconds for a response to the local resolve; if nothing comes
1424 * in, do an additional domain resolution...
1425 */
1426
1427 polldata.fd = DNSServiceRefSockFD(ref);
1428 polldata.events = POLLIN;
1429
1430 if (poll(&polldata, 1, 2000) != 1)
1431 {
1432 /*
1433 * OK, send the domain name resolve...
1434 */
1435
1436 if (logit)
1437 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1438 "domain=\"%s\"...\n", hostname, regtype, domain);
1439
1440 domainref = ref;
1441 if (DNSServiceResolve(&domainref, kDNSServiceFlagsShareConnection, 0,
1442 hostname, regtype, domain, resolve_callback,
1443 &uribuf) == kDNSServiceErr_NoError)
1444 domainsent = 1;
1445 }
1446 }
1447
1448 if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError)
1449 uri = resolved_uri;
1450
1451 if (domainsent)
1452 DNSServiceRefDeallocate(domainref);
1453
1454 DNSServiceRefDeallocate(localref);
1455 }
1456
1457 DNSServiceRefDeallocate(ref);
1458 }
1459
1460 if (logit)
1461 {
1462 if (uri)
1463 fputs("DEBUG: Unable to resolve URI!\n", stderr);
1464 else
1465 fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri);
1466
1467 fputs("STATE: -connecting-to-device\n", stderr);
1468 }
1469
1470 #else
1471 /*
1472 * No DNS-SD support...
1473 */
1474
1475 uri = NULL;
1476 #endif /* HAVE_DNSSD */
1477
1478 if (logit && !uri)
1479 _cupsLangPuts(stderr, _("Unable to find printer!\n"));
1480 }
1481
1482 DEBUG_printf(("5_httpResolveURI: Returning \"%s\"", uri));
1483
1484 return (uri);
1485 }
1486
1487
1488 /*
1489 * 'http_copy_decode()' - Copy and decode a URI.
1490 */
1491
1492 static const char * /* O - New source pointer or NULL on error */
1493 http_copy_decode(char *dst, /* O - Destination buffer */
1494 const char *src, /* I - Source pointer */
1495 int dstsize, /* I - Destination size */
1496 const char *term, /* I - Terminating characters */
1497 int decode) /* I - Decode %-encoded values */
1498 {
1499 char *ptr, /* Pointer into buffer */
1500 *end; /* End of buffer */
1501 int quoted; /* Quoted character */
1502
1503
1504 /*
1505 * Copy the src to the destination until we hit a terminating character
1506 * or the end of the string.
1507 */
1508
1509 for (ptr = dst, end = dst + dstsize - 1;
1510 *src && (!term || !strchr(term, *src));
1511 src ++)
1512 if (ptr < end)
1513 {
1514 if (*src == '%' && decode)
1515 {
1516 if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255))
1517 {
1518 /*
1519 * Grab a hex-encoded character...
1520 */
1521
1522 src ++;
1523 if (isalpha(*src))
1524 quoted = (tolower(*src) - 'a' + 10) << 4;
1525 else
1526 quoted = (*src - '0') << 4;
1527
1528 src ++;
1529 if (isalpha(*src))
1530 quoted |= tolower(*src) - 'a' + 10;
1531 else
1532 quoted |= *src - '0';
1533
1534 *ptr++ = quoted;
1535 }
1536 else
1537 {
1538 /*
1539 * Bad hex-encoded character...
1540 */
1541
1542 *ptr = '\0';
1543 return (NULL);
1544 }
1545 }
1546 else
1547 *ptr++ = *src;
1548 }
1549
1550 *ptr = '\0';
1551
1552 return (src);
1553 }
1554
1555
1556 /*
1557 * 'http_copy_encode()' - Copy and encode a URI.
1558 */
1559
1560 static char * /* O - End of current URI */
1561 http_copy_encode(char *dst, /* O - Destination buffer */
1562 const char *src, /* I - Source pointer */
1563 char *dstend, /* I - End of destination buffer */
1564 const char *reserved, /* I - Extra reserved characters */
1565 const char *term, /* I - Terminating characters */
1566 int encode) /* I - %-encode reserved chars? */
1567 {
1568 static const char hex[] = "0123456789ABCDEF";
1569
1570
1571 while (*src && dst < dstend)
1572 {
1573 if (term && *src == *term)
1574 return (dst);
1575
1576 if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
1577 (reserved && strchr(reserved, *src))))
1578 {
1579 /*
1580 * Hex encode reserved characters...
1581 */
1582
1583 if ((dst + 2) >= dstend)
1584 break;
1585
1586 *dst++ = '%';
1587 *dst++ = hex[(*src >> 4) & 15];
1588 *dst++ = hex[*src & 15];
1589
1590 src ++;
1591 }
1592 else
1593 *dst++ = *src++;
1594 }
1595
1596 *dst = '\0';
1597
1598 if (*src)
1599 return (NULL);
1600 else
1601 return (dst);
1602 }
1603
1604
1605 #ifdef HAVE_DNSSD
1606 /*
1607 * 'resolve_callback()' - Build a device URI for the given service name.
1608 */
1609
1610 static void
1611 resolve_callback(
1612 DNSServiceRef sdRef, /* I - Service reference */
1613 DNSServiceFlags flags, /* I - Results flags */
1614 uint32_t interfaceIndex, /* I - Interface number */
1615 DNSServiceErrorType errorCode, /* I - Error, if any */
1616 const char *fullName, /* I - Full service name */
1617 const char *hostTarget, /* I - Hostname */
1618 uint16_t port, /* I - Port number */
1619 uint16_t txtLen, /* I - Length of TXT record */
1620 const unsigned char *txtRecord, /* I - TXT record data */
1621 void *context) /* I - Pointer to URI buffer */
1622 {
1623 const char *scheme; /* URI scheme */
1624 char rp[257]; /* Remote printer */
1625 const void *value; /* Value from TXT record */
1626 uint8_t valueLen; /* Length of value */
1627 _http_uribuf_t *uribuf; /* URI buffer */
1628
1629
1630 DEBUG_printf(("7resolve_callback(sdRef=%p, flags=%x, interfaceIndex=%u, "
1631 "errorCode=%d, fullName=\"%s\", hostTarget=\"%s\", port=%u, "
1632 "txtLen=%u, txtRecord=%p, context=%p)", sdRef, flags,
1633 interfaceIndex, errorCode, fullName, hostTarget, port, txtLen,
1634 txtRecord, context));
1635
1636 /*
1637 * Figure out the scheme from the full name...
1638 */
1639
1640 if (strstr(fullName, "._ipp"))
1641 scheme = "ipp";
1642 else if (strstr(fullName, "._printer."))
1643 scheme = "lpd";
1644 else if (strstr(fullName, "._pdl-datastream."))
1645 scheme = "socket";
1646 else
1647 scheme = "riousbprint";
1648
1649 /*
1650 * Extract the "remote printer" key from the TXT record...
1651 */
1652
1653 if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, "rp",
1654 &valueLen)) != NULL)
1655 {
1656 /*
1657 * Convert to resource by concatenating with a leading "/"...
1658 */
1659
1660 rp[0] = '/';
1661 memcpy(rp + 1, value, valueLen);
1662 rp[valueLen + 1] = '\0';
1663 }
1664 else
1665 rp[0] = '\0';
1666
1667 /*
1668 * Assemble the final device URI...
1669 */
1670
1671 uribuf = (_http_uribuf_t *)context;
1672
1673 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, uribuf->bufsize, scheme,
1674 NULL, hostTarget, ntohs(port), rp);
1675
1676 DEBUG_printf(("8resolve_callback: Resolved URI is \"%s\"...",
1677 uribuf->buffer));
1678 }
1679 #endif /* HAVE_DNSSD */
1680
1681
1682 /*
1683 * End of "$Id: http-support.c 7952 2008-09-17 00:56:20Z mike $".
1684 */