]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/http-support.c
Import CUPS trunk (1.4svn) r7116.
[thirdparty/cups.git] / cups / http-support.c
1 /*
2 * "$Id: http-support.c 6649 2007-07-11 21:46:42Z mike $"
3 *
4 * HTTP support routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 2007 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 * http_copy_decode() - Copy and decode a URI.
40 * http_copy_encode() - Copy and encode a URI.
41 */
42
43 /*
44 * Include necessary headers...
45 */
46
47 #include "debug.h"
48 #include "globals.h"
49 #include <stdlib.h>
50
51
52 /*
53 * Local globals...
54 */
55
56 static const char * const http_days[7] =
57 {
58 "Sun",
59 "Mon",
60 "Tue",
61 "Wed",
62 "Thu",
63 "Fri",
64 "Sat"
65 };
66 static const char * const http_months[12] =
67 {
68 "Jan",
69 "Feb",
70 "Mar",
71 "Apr",
72 "May",
73 "Jun",
74 "Jul",
75 "Aug",
76 "Sep",
77 "Oct",
78 "Nov",
79 "Dec"
80 };
81
82
83 /*
84 * Local functions...
85 */
86
87 static const char *http_copy_decode(char *dst, const char *src,
88 int dstsize, const char *term,
89 int decode);
90 static char *http_copy_encode(char *dst, const char *src,
91 char *dstend, const char *reserved,
92 const char *term, int encode);
93
94
95 /*
96 * 'httpAssembleURI()' - Assemble a uniform resource identifier from its
97 * components.
98 *
99 * This function escapes reserved characters in the URI depending on the
100 * value of the "encoding" argument. You should use this function in
101 * place of traditional string functions whenever you need to create a
102 * URI string.
103 *
104 * @since CUPS 1.2@
105 */
106
107 http_uri_status_t /* O - URI status */
108 httpAssembleURI(
109 http_uri_coding_t encoding, /* I - Encoding flags */
110 char *uri, /* I - URI buffer */
111 int urilen, /* I - Size of URI buffer */
112 const char *scheme, /* I - Scheme name */
113 const char *username, /* I - Username */
114 const char *host, /* I - Hostname or address */
115 int port, /* I - Port number */
116 const char *resource) /* I - Resource */
117 {
118 char *ptr, /* Pointer into URI buffer */
119 *end; /* End of URI buffer */
120
121
122 /*
123 * Range check input...
124 */
125
126 if (!uri || urilen < 1 || !scheme || port < 0)
127 {
128 if (uri)
129 *uri = '\0';
130
131 return (HTTP_URI_BAD_ARGUMENTS);
132 }
133
134 /*
135 * Assemble the URI starting with the scheme...
136 */
137
138 end = uri + urilen - 1;
139 ptr = http_copy_encode(uri, scheme, end, NULL, NULL, 0);
140
141 if (!ptr)
142 goto assemble_overflow;
143
144 if (!strcmp(scheme, "mailto"))
145 {
146 /*
147 * mailto: only has :, no //...
148 */
149
150 if (ptr < end)
151 *ptr++ = ':';
152 else
153 goto assemble_overflow;
154 }
155 else
156 {
157 /*
158 * Schemes other than mailto: all have //...
159 */
160
161 if ((ptr + 2) < end)
162 {
163 *ptr++ = ':';
164 *ptr++ = '/';
165 *ptr++ = '/';
166 }
167 else
168 goto assemble_overflow;
169 }
170
171 /*
172 * Next the username and hostname, if any...
173 */
174
175 if (host)
176 {
177 if (username && *username)
178 {
179 /*
180 * Add username@ first...
181 */
182
183 ptr = http_copy_encode(ptr, username, end, "/?@", NULL,
184 encoding & HTTP_URI_CODING_USERNAME);
185
186 if (!ptr)
187 goto assemble_overflow;
188
189 if (ptr < end)
190 *ptr++ = '@';
191 else
192 goto assemble_overflow;
193 }
194
195 /*
196 * Then add the hostname. Since IPv6 is a particular pain to deal
197 * with, we have several special cases to deal with... If we get
198 * an IPv6 address with brackets around it, assume it is already in
199 * URI format...
200 */
201
202 if (host[0] != '[' && strchr(host, ':'))
203 {
204 /*
205 * We have an IPv6 address...
206 */
207
208 if (strchr(host, '%'))
209 {
210 /*
211 * We have a link-local address, add "[v1." prefix...
212 */
213
214 if ((ptr + 4) < end)
215 {
216 *ptr++ = '[';
217 *ptr++ = 'v';
218 *ptr++ = '1';
219 *ptr++ = '.';
220 }
221 else
222 goto assemble_overflow;
223 }
224 else
225 {
226 /*
227 * We have a normal address, add "[" prefix...
228 */
229
230 if (ptr < end)
231 *ptr++ = '[';
232 else
233 goto assemble_overflow;
234 }
235
236 /*
237 * Copy the rest of the IPv6 address, and terminate with "]".
238 */
239
240 while (ptr < end && *host)
241 {
242 if (*host == '%')
243 {
244 *ptr++ = '+'; /* Convert zone separator */
245 host ++;
246 }
247 else
248 *ptr++ = *host++;
249 }
250
251 if (*host)
252 goto assemble_overflow;
253
254 if (ptr < end)
255 *ptr++ = ']';
256 else
257 goto assemble_overflow;
258 }
259 else
260 {
261 /*
262 * Otherwise, just copy the host string...
263 */
264
265 ptr = http_copy_encode(ptr, host, end, NULL, NULL,
266 encoding & HTTP_URI_CODING_HOSTNAME);
267
268 if (!ptr)
269 goto assemble_overflow;
270 }
271
272 /*
273 * Finish things off with the port number...
274 */
275
276 if (port > 0)
277 {
278 snprintf(ptr, end - ptr + 1, ":%d", port);
279 ptr += strlen(ptr);
280
281 if (ptr >= end)
282 goto assemble_overflow;
283 }
284 }
285
286 /*
287 * Last but not least, add the resource string...
288 */
289
290 if (resource)
291 {
292 char *query; /* Pointer to query string */
293
294
295 /*
296 * Copy the resource string up to the query string if present...
297 */
298
299 query = strchr(resource, '?');
300 ptr = http_copy_encode(ptr, resource, end, NULL, "?",
301 encoding & HTTP_URI_CODING_RESOURCE);
302 if (!ptr)
303 goto assemble_overflow;
304
305 if (query)
306 {
307 /*
308 * Copy query string without encoding...
309 */
310
311 ptr = http_copy_encode(ptr, query, end, NULL, NULL,
312 encoding & HTTP_URI_CODING_QUERY);
313 if (!ptr)
314 goto assemble_overflow;
315 }
316 }
317 else if (ptr < end)
318 *ptr++ = '/';
319 else
320 goto assemble_overflow;
321
322 /*
323 * Nul-terminate the URI buffer and return with no errors...
324 */
325
326 *ptr = '\0';
327
328 return (HTTP_URI_OK);
329
330 /*
331 * Clear the URI string and return an overflow error; I don't usually
332 * like goto's, but in this case it makes sense...
333 */
334
335 assemble_overflow:
336
337 *uri = '\0';
338 return (HTTP_URI_OVERFLOW);
339 }
340
341
342 /*
343 * 'httpAssembleURIf()' - Assemble a uniform resource identifier from its
344 * components with a formatted resource.
345 *
346 * This function creates a formatted version of the resource string
347 * argument "resourcef" and escapes reserved characters in the URI
348 * depending on the value of the "encoding" argument. You should use
349 * this function in place of traditional string functions whenever
350 * you need to create a URI string.
351 *
352 * @since CUPS 1.2@
353 */
354
355 http_uri_status_t /* O - URI status */
356 httpAssembleURIf(
357 http_uri_coding_t encoding, /* I - Encoding flags */
358 char *uri, /* I - URI buffer */
359 int urilen, /* I - Size of URI buffer */
360 const char *scheme, /* I - Scheme name */
361 const char *username, /* I - Username */
362 const char *host, /* I - Hostname or address */
363 int port, /* I - Port number */
364 const char *resourcef, /* I - Printf-style resource */
365 ...) /* I - Additional arguments as needed */
366 {
367 va_list ap; /* Pointer to additional arguments */
368 char resource[1024]; /* Formatted resource string */
369 int bytes; /* Bytes in formatted string */
370
371
372 /*
373 * Range check input...
374 */
375
376 if (!uri || urilen < 1 || !scheme || port < 0 || !resourcef)
377 {
378 if (uri)
379 *uri = '\0';
380
381 return (HTTP_URI_BAD_ARGUMENTS);
382 }
383
384 /*
385 * Format the resource string and assemble the URI...
386 */
387
388 va_start(ap, resourcef);
389 bytes = vsnprintf(resource, sizeof(resource), resourcef, ap);
390 va_end(ap);
391
392 if (bytes >= sizeof(resource))
393 {
394 *uri = '\0';
395 return (HTTP_URI_OVERFLOW);
396 }
397 else
398 return (httpAssembleURI(encoding, uri, urilen, scheme, username, host,
399 port, resource));
400 }
401
402
403 /*
404 * 'httpDecode64()' - Base64-decode a string.
405 *
406 * This function is deprecated. Use the httpDecode64_2() function instead
407 * which provides buffer length arguments.
408 *
409 * @deprecated@
410 */
411
412 char * /* O - Decoded string */
413 httpDecode64(char *out, /* I - String to write to */
414 const char *in) /* I - String to read from */
415 {
416 int outlen; /* Output buffer length */
417
418
419 /*
420 * Use the old maximum buffer size for binary compatibility...
421 */
422
423 outlen = 512;
424
425 return (httpDecode64_2(out, &outlen, in));
426 }
427
428
429 /*
430 * 'httpDecode64_2()' - Base64-decode a string.
431 *
432 * @since CUPS 1.1.21@
433 */
434
435 char * /* O - Decoded string */
436 httpDecode64_2(char *out, /* I - String to write to */
437 int *outlen, /* IO - Size of output string */
438 const char *in) /* I - String to read from */
439 {
440 int pos, /* Bit position */
441 base64; /* Value of this character */
442 char *outptr, /* Output pointer */
443 *outend; /* End of output buffer */
444
445
446 /*
447 * Range check input...
448 */
449
450 if (!out || !outlen || *outlen < 1 || !in)
451 return (NULL);
452
453 if (!*in)
454 {
455 *out = '\0';
456 *outlen = 0;
457
458 return (out);
459 }
460
461 /*
462 * Convert from base-64 to bytes...
463 */
464
465 for (outptr = out, outend = out + *outlen - 1, pos = 0; *in != '\0'; in ++)
466 {
467 /*
468 * Decode this character into a number from 0 to 63...
469 */
470
471 if (*in >= 'A' && *in <= 'Z')
472 base64 = *in - 'A';
473 else if (*in >= 'a' && *in <= 'z')
474 base64 = *in - 'a' + 26;
475 else if (*in >= '0' && *in <= '9')
476 base64 = *in - '0' + 52;
477 else if (*in == '+')
478 base64 = 62;
479 else if (*in == '/')
480 base64 = 63;
481 else if (*in == '=')
482 break;
483 else
484 continue;
485
486 /*
487 * Store the result in the appropriate chars...
488 */
489
490 switch (pos)
491 {
492 case 0 :
493 if (outptr < outend)
494 *outptr = base64 << 2;
495 pos ++;
496 break;
497 case 1 :
498 if (outptr < outend)
499 *outptr++ |= (base64 >> 4) & 3;
500 if (outptr < outend)
501 *outptr = (base64 << 4) & 255;
502 pos ++;
503 break;
504 case 2 :
505 if (outptr < outend)
506 *outptr++ |= (base64 >> 2) & 15;
507 if (outptr < outend)
508 *outptr = (base64 << 6) & 255;
509 pos ++;
510 break;
511 case 3 :
512 if (outptr < outend)
513 *outptr++ |= base64;
514 pos = 0;
515 break;
516 }
517 }
518
519 *outptr = '\0';
520
521 /*
522 * Return the decoded string and size...
523 */
524
525 *outlen = (int)(outptr - out);
526
527 return (out);
528 }
529
530
531 /*
532 * 'httpEncode64()' - Base64-encode a string.
533 *
534 * This function is deprecated. Use the httpEncode64_2() function instead
535 * which provides buffer length arguments.
536 *
537 * @deprecated@
538 */
539
540 char * /* O - Encoded string */
541 httpEncode64(char *out, /* I - String to write to */
542 const char *in) /* I - String to read from */
543 {
544 return (httpEncode64_2(out, 512, in, (int)strlen(in)));
545 }
546
547
548 /*
549 * 'httpEncode64_2()' - Base64-encode a string.
550 *
551 * @since CUPS 1.1.21@
552 */
553
554 char * /* O - Encoded string */
555 httpEncode64_2(char *out, /* I - String to write to */
556 int outlen, /* I - Size of output string */
557 const char *in, /* I - String to read from */
558 int inlen) /* I - Size of input string */
559 {
560 char *outptr, /* Output pointer */
561 *outend; /* End of output buffer */
562 static const char base64[] = /* Base64 characters... */
563 {
564 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
565 "abcdefghijklmnopqrstuvwxyz"
566 "0123456789"
567 "+/"
568 };
569
570
571 /*
572 * Range check input...
573 */
574
575 if (!out || outlen < 1 || !in)
576 return (NULL);
577
578 /*
579 * Convert bytes to base-64...
580 */
581
582 for (outptr = out, outend = out + outlen - 1; inlen > 0; in ++, inlen --)
583 {
584 /*
585 * Encode the up to 3 characters as 4 Base64 numbers...
586 */
587
588 if (outptr < outend)
589 *outptr ++ = base64[(in[0] & 255) >> 2];
590
591 if (outptr < outend)
592 {
593 if (inlen > 1)
594 *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
595 else
596 *outptr ++ = base64[((in[0] & 255) << 4) & 63];
597 }
598
599 in ++;
600 inlen --;
601 if (inlen <= 0)
602 {
603 if (outptr < outend)
604 *outptr ++ = '=';
605 if (outptr < outend)
606 *outptr ++ = '=';
607 break;
608 }
609
610 if (outptr < outend)
611 {
612 if (inlen > 1)
613 *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
614 else
615 *outptr ++ = base64[((in[0] & 255) << 2) & 63];
616 }
617
618 in ++;
619 inlen --;
620 if (inlen <= 0)
621 {
622 if (outptr < outend)
623 *outptr ++ = '=';
624 break;
625 }
626
627 if (outptr < outend)
628 *outptr ++ = base64[in[0] & 63];
629 }
630
631 *outptr = '\0';
632
633 /*
634 * Return the encoded string...
635 */
636
637 return (out);
638 }
639
640
641 /*
642 * 'httpGetDateString()' - Get a formatted date/time string from a time value.
643 *
644 * @deprecated@
645 */
646
647 const char * /* O - Date/time string */
648 httpGetDateString(time_t t) /* I - UNIX time */
649 {
650 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
651
652
653 return (httpGetDateString2(t, cg->http_date, sizeof(cg->http_date)));
654 }
655
656
657 /*
658 * 'httpGetDateString2()' - Get a formatted date/time string from a time value.
659 *
660 * @since CUPS 1.2@
661 */
662
663 const char * /* O - Date/time string */
664 httpGetDateString2(time_t t, /* I - UNIX time */
665 char *s, /* I - String buffer */
666 int slen) /* I - Size of string buffer */
667 {
668 struct tm *tdate; /* UNIX date/time data */
669
670
671 tdate = gmtime(&t);
672 snprintf(s, slen, "%s, %02d %s %d %02d:%02d:%02d GMT",
673 http_days[tdate->tm_wday], tdate->tm_mday,
674 http_months[tdate->tm_mon], tdate->tm_year + 1900,
675 tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
676
677 return (s);
678 }
679
680
681 /*
682 * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
683 */
684
685 time_t /* O - UNIX time */
686 httpGetDateTime(const char *s) /* I - Date/time string */
687 {
688 int i; /* Looping var */
689 char mon[16]; /* Abbreviated month name */
690 int day, year; /* Day of month and year */
691 int hour, min, sec; /* Time */
692 int days; /* Number of days since 1970 */
693 static const int normal_days[] = /* Days to a month, normal years */
694 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
695 static const int leap_days[] = /* Days to a month, leap years */
696 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
697
698
699 DEBUG_printf(("httpGetDateTime(s=\"%s\")\n", s));
700
701 /*
702 * Extract the date and time from the formatted string...
703 */
704
705 if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
706 return (0);
707
708 DEBUG_printf((" day=%d, mon=\"%s\", year=%d, hour=%d, min=%d, sec=%d\n",
709 day, mon, year, hour, min, sec));
710
711 /*
712 * Convert the month name to a number from 0 to 11.
713 */
714
715 for (i = 0; i < 12; i ++)
716 if (!strcasecmp(mon, http_months[i]))
717 break;
718
719 if (i >= 12)
720 return (0);
721
722 DEBUG_printf((" i=%d\n", i));
723
724 /*
725 * Now convert the date and time to a UNIX time value in seconds since
726 * 1970. We can't use mktime() since the timezone may not be UTC but
727 * the date/time string *is* UTC.
728 */
729
730 if ((year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0))
731 days = leap_days[i] + day - 1;
732 else
733 days = normal_days[i] + day - 1;
734
735 DEBUG_printf((" days=%d\n", days));
736
737 days += (year - 1970) * 365 + /* 365 days per year (normally) */
738 ((year - 1) / 4 - 492) - /* + leap days */
739 ((year - 1) / 100 - 19) + /* - 100 year days */
740 ((year - 1) / 400 - 4); /* + 400 year days */
741
742 DEBUG_printf((" days=%d\n", days));
743
744 return (days * 86400 + hour * 3600 + min * 60 + sec);
745 }
746
747
748 /*
749 * 'httpSeparate()' - Separate a Universal Resource Identifier into its
750 * components.
751 *
752 * This function is deprecated; use the httpSeparateURI() function instead.
753 *
754 * @deprecated@
755 */
756
757 void
758 httpSeparate(const char *uri, /* I - Universal Resource Identifier */
759 char *scheme, /* O - Scheme [32] (http, https, etc.) */
760 char *username, /* O - Username [1024] */
761 char *host, /* O - Hostname [1024] */
762 int *port, /* O - Port number to use */
763 char *resource) /* O - Resource/filename [1024] */
764 {
765 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username,
766 HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource,
767 HTTP_MAX_URI);
768 }
769
770
771 /*
772 * 'httpSeparate2()' - Separate a Universal Resource Identifier into its
773 * components.
774 *
775 * This function is deprecated; use the httpSeparateURI() function instead.
776 *
777 * @since CUPS 1.1.21@
778 * @deprecated@
779 */
780
781 void
782 httpSeparate2(const char *uri, /* I - Universal Resource Identifier */
783 char *scheme, /* O - Scheme (http, https, etc.) */
784 int schemelen, /* I - Size of scheme buffer */
785 char *username, /* O - Username */
786 int usernamelen, /* I - Size of username buffer */
787 char *host, /* O - Hostname */
788 int hostlen, /* I - Size of hostname buffer */
789 int *port, /* O - Port number to use */
790 char *resource, /* O - Resource/filename */
791 int resourcelen) /* I - Size of resource buffer */
792 {
793 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username,
794 usernamelen, host, hostlen, port, resource, resourcelen);
795 }
796
797
798 /*
799 * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its
800 * components.
801 *
802 * @since CUPS 1.2@
803 */
804
805 http_uri_status_t /* O - Result of separation */
806 httpSeparateURI(
807 http_uri_coding_t decoding, /* I - Decoding flags */
808 const char *uri, /* I - Universal Resource Identifier */
809 char *scheme, /* O - Scheme (http, https, etc.) */
810 int schemelen, /* I - Size of scheme buffer */
811 char *username, /* O - Username */
812 int usernamelen, /* I - Size of username buffer */
813 char *host, /* O - Hostname */
814 int hostlen, /* I - Size of hostname buffer */
815 int *port, /* O - Port number to use */
816 char *resource, /* O - Resource/filename */
817 int resourcelen) /* I - Size of resource buffer */
818 {
819 char *ptr, /* Pointer into string... */
820 *end; /* End of string */
821 const char *sep; /* Separator character */
822 http_uri_status_t status; /* Result of separation */
823
824
825 /*
826 * Initialize everything to blank...
827 */
828
829 if (scheme && schemelen > 0)
830 *scheme = '\0';
831
832 if (username && usernamelen > 0)
833 *username = '\0';
834
835 if (host && hostlen > 0)
836 *host = '\0';
837
838 if (port)
839 *port = 0;
840
841 if (resource && resourcelen > 0)
842 *resource = '\0';
843
844 /*
845 * Range check input...
846 */
847
848 if (!uri || !port || !scheme || schemelen <= 0 || !username ||
849 usernamelen <= 0 || !host || hostlen <= 0 || !resource ||
850 resourcelen <= 0)
851 return (HTTP_URI_BAD_ARGUMENTS);
852
853 if (!*uri)
854 return (HTTP_URI_BAD_URI);
855
856 /*
857 * Grab the scheme portion of the URI...
858 */
859
860 status = HTTP_URI_OK;
861
862 if (!strncmp(uri, "//", 2))
863 {
864 /*
865 * Workaround for HP IPP client bug...
866 */
867
868 strlcpy(scheme, "ipp", schemelen);
869 status = HTTP_URI_MISSING_SCHEME;
870 }
871 else if (*uri == '/')
872 {
873 /*
874 * Filename...
875 */
876
877 strlcpy(scheme, "file", schemelen);
878 status = HTTP_URI_MISSING_SCHEME;
879 }
880 else
881 {
882 /*
883 * Standard URI with scheme...
884 */
885
886 for (ptr = scheme, end = scheme + schemelen - 1;
887 *uri && *uri != ':' && ptr < end;)
888 if (isalnum(*uri & 255) || *uri == '-' || *uri == '+' || *uri == '.')
889 *ptr++ = *uri++;
890 else
891 break;
892
893 *ptr = '\0';
894
895 if (*uri != ':')
896 {
897 *scheme = '\0';
898 return (HTTP_URI_BAD_SCHEME);
899 }
900
901 uri ++;
902 }
903
904 /*
905 * Set the default port number...
906 */
907
908 if (!strcmp(scheme, "http"))
909 *port = 80;
910 else if (!strcmp(scheme, "https"))
911 *port = 443;
912 else if (!strcmp(scheme, "ipp"))
913 *port = 631;
914 else if (!strcasecmp(scheme, "lpd"))
915 *port = 515;
916 else if (!strcmp(scheme, "socket")) /* Not yet registered with IANA... */
917 *port = 9100;
918 else if (strcmp(scheme, "file") && strcmp(scheme, "mailto"))
919 status = HTTP_URI_UNKNOWN_SCHEME;
920
921 /*
922 * Now see if we have a hostname...
923 */
924
925 if (!strncmp(uri, "//", 2))
926 {
927 /*
928 * Yes, extract it...
929 */
930
931 uri += 2;
932
933 /*
934 * Grab the username, if any...
935 */
936
937 if ((sep = strpbrk(uri, "@/")) != NULL && *sep == '@')
938 {
939 /*
940 * Get a username:password combo...
941 */
942
943 uri = http_copy_decode(username, uri, usernamelen, "@",
944 decoding & HTTP_URI_CODING_USERNAME);
945
946 if (!uri)
947 {
948 *username = '\0';
949 return (HTTP_URI_BAD_USERNAME);
950 }
951
952 uri ++;
953 }
954
955 /*
956 * Then the hostname/IP address...
957 */
958
959 if (*uri == '[')
960 {
961 /*
962 * Grab IPv6 address...
963 */
964
965 uri ++;
966 if (!strncmp(uri, "v1.", 3))
967 uri += 3; /* Skip IPvN leader... */
968
969 uri = http_copy_decode(host, uri, hostlen, "]",
970 decoding & HTTP_URI_CODING_HOSTNAME);
971
972 if (!uri)
973 {
974 *host = '\0';
975 return (HTTP_URI_BAD_HOSTNAME);
976 }
977
978 /*
979 * Validate value...
980 */
981
982 if (*uri != ']')
983 {
984 *host = '\0';
985 return (HTTP_URI_BAD_HOSTNAME);
986 }
987
988 uri ++;
989
990 for (ptr = host; *ptr; ptr ++)
991 if (*ptr == '+')
992 {
993 /*
994 * Convert zone separator to % and stop here...
995 */
996
997 *ptr = '%';
998 break;
999 }
1000 else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255))
1001 {
1002 *host = '\0';
1003 return (HTTP_URI_BAD_HOSTNAME);
1004 }
1005 }
1006 else
1007 {
1008 /*
1009 * Validate the hostname or IPv4 address first...
1010 */
1011
1012 for (ptr = (char *)uri; *ptr; ptr ++)
1013 if (strchr(":?/", *ptr))
1014 break;
1015 else if (!strchr("abcdefghijklmnopqrstuvwxyz"
1016 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1017 "0123456789"
1018 "-._~"
1019 "%"
1020 "!$&'()*+,;=\\", *ptr))
1021 {
1022 *host = '\0';
1023 return (HTTP_URI_BAD_HOSTNAME);
1024 }
1025
1026 /*
1027 * Then copy the hostname or IPv4 address to the buffer...
1028 */
1029
1030 uri = http_copy_decode(host, uri, hostlen, ":?/",
1031 decoding & HTTP_URI_CODING_HOSTNAME);
1032
1033 if (!uri)
1034 {
1035 *host = '\0';
1036 return (HTTP_URI_BAD_HOSTNAME);
1037 }
1038 }
1039
1040 /*
1041 * Validate hostname for file scheme - only empty and localhost are
1042 * acceptable.
1043 */
1044
1045 if (!strcmp(scheme, "file") && strcmp(host, "localhost") && host[0])
1046 {
1047 *host = '\0';
1048 return (HTTP_URI_BAD_HOSTNAME);
1049 }
1050
1051 /*
1052 * See if we have a port number...
1053 */
1054
1055 if (*uri == ':')
1056 {
1057 /*
1058 * Yes, collect the port number...
1059 */
1060
1061 *port = strtol(uri + 1, (char **)&uri, 10);
1062
1063 if (*uri != '/' && *uri)
1064 {
1065 *port = 0;
1066 return (HTTP_URI_BAD_PORT);
1067 }
1068 }
1069 }
1070
1071 /*
1072 * The remaining portion is the resource string...
1073 */
1074
1075 if (*uri == '?' || !*uri)
1076 {
1077 /*
1078 * Hostname but no path...
1079 */
1080
1081 status = HTTP_URI_MISSING_RESOURCE;
1082 *resource = '/';
1083
1084 /*
1085 * Copy any query string...
1086 */
1087
1088 if (*uri == '?')
1089 uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL,
1090 decoding & HTTP_URI_CODING_QUERY);
1091 else
1092 resource[1] = '\0';
1093 }
1094 else
1095 {
1096 uri = http_copy_decode(resource, uri, resourcelen, "?",
1097 decoding & HTTP_URI_CODING_RESOURCE);
1098
1099 if (uri && *uri == '?')
1100 {
1101 /*
1102 * Concatenate any query string...
1103 */
1104
1105 char *resptr = resource + strlen(resource);
1106
1107 uri = http_copy_decode(resptr, uri, resourcelen - (int)(resptr - resource),
1108 NULL, decoding & HTTP_URI_CODING_QUERY);
1109 }
1110 }
1111
1112 if (!uri)
1113 {
1114 *resource = '\0';
1115 return (HTTP_URI_BAD_RESOURCE);
1116 }
1117
1118 /*
1119 * Return the URI separation status...
1120 */
1121
1122 return (status);
1123 }
1124
1125
1126 /*
1127 * 'httpStatus()' - Return a short string describing a HTTP status code.
1128 */
1129
1130 const char * /* O - String or NULL */
1131 httpStatus(http_status_t status) /* I - HTTP status code */
1132 {
1133 switch (status)
1134 {
1135 case HTTP_CONTINUE :
1136 return ("Continue");
1137 case HTTP_SWITCHING_PROTOCOLS :
1138 return ("Switching Protocols");
1139 case HTTP_OK :
1140 return ("OK");
1141 case HTTP_CREATED :
1142 return ("Created");
1143 case HTTP_ACCEPTED :
1144 return ("Accepted");
1145 case HTTP_NO_CONTENT :
1146 return ("No Content");
1147 case HTTP_MOVED_PERMANENTLY :
1148 return ("Moved Permanently");
1149 case HTTP_SEE_OTHER :
1150 return ("See Other");
1151 case HTTP_NOT_MODIFIED :
1152 return ("Not Modified");
1153 case HTTP_BAD_REQUEST :
1154 return ("Bad Request");
1155 case HTTP_UNAUTHORIZED :
1156 return ("Unauthorized");
1157 case HTTP_FORBIDDEN :
1158 return ("Forbidden");
1159 case HTTP_NOT_FOUND :
1160 return ("Not Found");
1161 case HTTP_REQUEST_TOO_LARGE :
1162 return ("Request Entity Too Large");
1163 case HTTP_URI_TOO_LONG :
1164 return ("URI Too Long");
1165 case HTTP_UPGRADE_REQUIRED :
1166 return ("Upgrade Required");
1167 case HTTP_NOT_IMPLEMENTED :
1168 return ("Not Implemented");
1169 case HTTP_NOT_SUPPORTED :
1170 return ("Not Supported");
1171 case HTTP_EXPECTATION_FAILED :
1172 return ("Expectation Failed");
1173
1174 default :
1175 return ("Unknown");
1176 }
1177 }
1178
1179
1180 #ifndef HAVE_HSTRERROR
1181 /*
1182 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others...
1183 */
1184
1185 const char * /* O - Error string */
1186 _cups_hstrerror(int error) /* I - Error number */
1187 {
1188 static const char * const errors[] = /* Error strings */
1189 {
1190 "OK",
1191 "Host not found.",
1192 "Try again.",
1193 "Unrecoverable lookup error.",
1194 "No data associated with name."
1195 };
1196
1197
1198 if (error < 0 || error > 4)
1199 return ("Unknown hostname lookup error.");
1200 else
1201 return (errors[error]);
1202 }
1203 #endif /* !HAVE_HSTRERROR */
1204
1205
1206 /*
1207 * 'http_copy_decode()' - Copy and decode a URI.
1208 */
1209
1210 static const char * /* O - New source pointer or NULL on error */
1211 http_copy_decode(char *dst, /* O - Destination buffer */
1212 const char *src, /* I - Source pointer */
1213 int dstsize, /* I - Destination size */
1214 const char *term, /* I - Terminating characters */
1215 int decode) /* I - Decode %-encoded values */
1216 {
1217 char *ptr, /* Pointer into buffer */
1218 *end; /* End of buffer */
1219 int quoted; /* Quoted character */
1220
1221
1222 /*
1223 * Copy the src to the destination until we hit a terminating character
1224 * or the end of the string.
1225 */
1226
1227 for (ptr = dst, end = dst + dstsize - 1;
1228 *src && (!term || !strchr(term, *src));
1229 src ++)
1230 if (ptr < end)
1231 {
1232 if (*src == '%' && decode)
1233 {
1234 if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255))
1235 {
1236 /*
1237 * Grab a hex-encoded character...
1238 */
1239
1240 src ++;
1241 if (isalpha(*src))
1242 quoted = (tolower(*src) - 'a' + 10) << 4;
1243 else
1244 quoted = (*src - '0') << 4;
1245
1246 src ++;
1247 if (isalpha(*src))
1248 quoted |= tolower(*src) - 'a' + 10;
1249 else
1250 quoted |= *src - '0';
1251
1252 *ptr++ = quoted;
1253 }
1254 else
1255 {
1256 /*
1257 * Bad hex-encoded character...
1258 */
1259
1260 *ptr = '\0';
1261 return (NULL);
1262 }
1263 }
1264 else
1265 *ptr++ = *src;
1266 }
1267
1268 *ptr = '\0';
1269
1270 return (src);
1271 }
1272
1273
1274 /*
1275 * 'http_copy_encode()' - Copy and encode a URI.
1276 */
1277
1278 static char * /* O - End of current URI */
1279 http_copy_encode(char *dst, /* O - Destination buffer */
1280 const char *src, /* I - Source pointer */
1281 char *dstend, /* I - End of destination buffer */
1282 const char *reserved, /* I - Extra reserved characters */
1283 const char *term, /* I - Terminating characters */
1284 int encode) /* I - %-encode reserved chars? */
1285 {
1286 static const char *hex = "0123456789ABCDEF";
1287
1288
1289 while (*src && dst < dstend)
1290 {
1291 if (term && *src == *term)
1292 return (dst);
1293
1294 if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
1295 (reserved && strchr(reserved, *src))))
1296 {
1297 /*
1298 * Hex encode reserved characters...
1299 */
1300
1301 if ((dst + 2) >= dstend)
1302 break;
1303
1304 *dst++ = '%';
1305 *dst++ = hex[(*src >> 4) & 15];
1306 *dst++ = hex[*src & 15];
1307
1308 src ++;
1309 }
1310 else
1311 *dst++ = *src++;
1312 }
1313
1314 if (*src)
1315 return (NULL);
1316 else
1317 return (dst);
1318 }
1319
1320
1321 /*
1322 * End of "$Id: http-support.c 6649 2007-07-11 21:46:42Z mike $".
1323 */