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