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