]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/http-support.c
Load cups into easysw/current.
[thirdparty/cups.git] / cups / http-support.c
1 /*
2 * "$Id: http-support.c 6304 2007-02-22 22:06:23Z mike $"
3 *
4 * HTTP support routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-2007 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, (int)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
599 if (outptr < outend)
600 {
601 if (inlen > 1)
602 *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
603 else
604 *outptr ++ = base64[((in[0] & 255) << 4) & 63];
605 }
606
607 in ++;
608 inlen --;
609 if (inlen <= 0)
610 {
611 if (outptr < outend)
612 *outptr ++ = '=';
613 if (outptr < outend)
614 *outptr ++ = '=';
615 break;
616 }
617
618 if (outptr < outend)
619 {
620 if (inlen > 1)
621 *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
622 else
623 *outptr ++ = base64[((in[0] & 255) << 2) & 63];
624 }
625
626 in ++;
627 inlen --;
628 if (inlen <= 0)
629 {
630 if (outptr < outend)
631 *outptr ++ = '=';
632 break;
633 }
634
635 if (outptr < outend)
636 *outptr ++ = base64[in[0] & 63];
637 }
638
639 *outptr = '\0';
640
641 /*
642 * Return the encoded string...
643 */
644
645 return (out);
646 }
647
648
649 /*
650 * 'httpGetDateString()' - Get a formatted date/time string from a time value.
651 *
652 * @deprecated@
653 */
654
655 const char * /* O - Date/time string */
656 httpGetDateString(time_t t) /* I - UNIX time */
657 {
658 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
659
660
661 return (httpGetDateString2(t, cg->http_date, sizeof(cg->http_date)));
662 }
663
664
665 /*
666 * 'httpGetDateString2()' - Get a formatted date/time string from a time value.
667 *
668 * @since CUPS 1.2@
669 */
670
671 const char * /* O - Date/time string */
672 httpGetDateString2(time_t t, /* I - UNIX time */
673 char *s, /* I - String buffer */
674 int slen) /* I - Size of string buffer */
675 {
676 struct tm *tdate; /* UNIX date/time data */
677
678
679 tdate = gmtime(&t);
680 snprintf(s, slen, "%s, %02d %s %d %02d:%02d:%02d GMT",
681 http_days[tdate->tm_wday], tdate->tm_mday,
682 http_months[tdate->tm_mon], tdate->tm_year + 1900,
683 tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
684
685 return (s);
686 }
687
688
689 /*
690 * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
691 */
692
693 time_t /* O - UNIX time */
694 httpGetDateTime(const char *s) /* I - Date/time string */
695 {
696 int i; /* Looping var */
697 char mon[16]; /* Abbreviated month name */
698 int day, year; /* Day of month and year */
699 int hour, min, sec; /* Time */
700 int days; /* Number of days since 1970 */
701 static const int normal_days[] = /* Days to a month, normal years */
702 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
703 static const int leap_days[] = /* Days to a month, leap years */
704 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
705
706
707 DEBUG_printf(("httpGetDateTime(s=\"%s\")\n", s));
708
709 /*
710 * Extract the date and time from the formatted string...
711 */
712
713 if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
714 return (0);
715
716 DEBUG_printf((" day=%d, mon=\"%s\", year=%d, hour=%d, min=%d, sec=%d\n",
717 day, mon, year, hour, min, sec));
718
719 /*
720 * Convert the month name to a number from 0 to 11.
721 */
722
723 for (i = 0; i < 12; i ++)
724 if (!strcasecmp(mon, http_months[i]))
725 break;
726
727 if (i >= 12)
728 return (0);
729
730 DEBUG_printf((" i=%d\n", i));
731
732 /*
733 * Now convert the date and time to a UNIX time value in seconds since
734 * 1970. We can't use mktime() since the timezone may not be UTC but
735 * the date/time string *is* UTC.
736 */
737
738 if ((year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0))
739 days = leap_days[i] + day - 1;
740 else
741 days = normal_days[i] + day - 1;
742
743 DEBUG_printf((" days=%d\n", days));
744
745 days += (year - 1970) * 365 + /* 365 days per year (normally) */
746 ((year - 1) / 4 - 492) - /* + leap days */
747 ((year - 1) / 100 - 19) + /* - 100 year days */
748 ((year - 1) / 400 - 4); /* + 400 year days */
749
750 DEBUG_printf((" days=%d\n", days));
751
752 return (days * 86400 + hour * 3600 + min * 60 + sec);
753 }
754
755
756 /*
757 * 'httpSeparate()' - Separate a Universal Resource Identifier into its
758 * components.
759 *
760 * This function is deprecated; use the httpSeparateURI() function instead.
761 *
762 * @deprecated@
763 */
764
765 void
766 httpSeparate(const char *uri, /* I - Universal Resource Identifier */
767 char *scheme, /* O - Scheme [32] (http, https, etc.) */
768 char *username, /* O - Username [1024] */
769 char *host, /* O - Hostname [1024] */
770 int *port, /* O - Port number to use */
771 char *resource) /* O - Resource/filename [1024] */
772 {
773 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username,
774 HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource,
775 HTTP_MAX_URI);
776 }
777
778
779 /*
780 * 'httpSeparate2()' - Separate a Universal Resource Identifier into its
781 * components.
782 *
783 * This function is deprecated; use the httpSeparateURI() function instead.
784 *
785 * @since CUPS 1.1.21@
786 * @deprecated@
787 */
788
789 void
790 httpSeparate2(const char *uri, /* I - Universal Resource Identifier */
791 char *scheme, /* O - Scheme (http, https, etc.) */
792 int schemelen, /* I - Size of scheme buffer */
793 char *username, /* O - Username */
794 int usernamelen, /* I - Size of username buffer */
795 char *host, /* O - Hostname */
796 int hostlen, /* I - Size of hostname buffer */
797 int *port, /* O - Port number to use */
798 char *resource, /* O - Resource/filename */
799 int resourcelen) /* I - Size of resource buffer */
800 {
801 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username,
802 usernamelen, host, hostlen, port, resource, resourcelen);
803 }
804
805
806 /*
807 * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its
808 * components.
809 *
810 * @since CUPS 1.2@
811 */
812
813 http_uri_status_t /* O - Result of separation */
814 httpSeparateURI(
815 http_uri_coding_t decoding, /* I - Decoding flags */
816 const char *uri, /* I - Universal Resource Identifier */
817 char *scheme, /* O - Scheme (http, https, etc.) */
818 int schemelen, /* I - Size of scheme buffer */
819 char *username, /* O - Username */
820 int usernamelen, /* I - Size of username buffer */
821 char *host, /* O - Hostname */
822 int hostlen, /* I - Size of hostname buffer */
823 int *port, /* O - Port number to use */
824 char *resource, /* O - Resource/filename */
825 int resourcelen) /* I - Size of resource buffer */
826 {
827 char *ptr, /* Pointer into string... */
828 *end; /* End of string */
829 const char *sep; /* Separator character */
830 http_uri_status_t status; /* Result of separation */
831
832
833 /*
834 * Initialize everything to blank...
835 */
836
837 if (scheme && schemelen > 0)
838 *scheme = '\0';
839
840 if (username && usernamelen > 0)
841 *username = '\0';
842
843 if (host && hostlen > 0)
844 *host = '\0';
845
846 if (port)
847 *port = 0;
848
849 if (resource && resourcelen > 0)
850 *resource = '\0';
851
852 /*
853 * Range check input...
854 */
855
856 if (!uri || !port || !scheme || schemelen <= 0 || !username ||
857 usernamelen <= 0 || !host || hostlen <= 0 || !resource ||
858 resourcelen <= 0)
859 return (HTTP_URI_BAD_ARGUMENTS);
860
861 if (!*uri)
862 return (HTTP_URI_BAD_URI);
863
864 /*
865 * Grab the scheme portion of the URI...
866 */
867
868 status = HTTP_URI_OK;
869
870 if (!strncmp(uri, "//", 2))
871 {
872 /*
873 * Workaround for HP IPP client bug...
874 */
875
876 strlcpy(scheme, "ipp", schemelen);
877 status = HTTP_URI_MISSING_SCHEME;
878 }
879 else if (*uri == '/')
880 {
881 /*
882 * Filename...
883 */
884
885 strlcpy(scheme, "file", schemelen);
886 status = HTTP_URI_MISSING_SCHEME;
887 }
888 else
889 {
890 /*
891 * Standard URI with scheme...
892 */
893
894 for (ptr = scheme, end = scheme + schemelen - 1;
895 *uri && *uri != ':' && ptr < end;)
896 if (isalnum(*uri & 255) || *uri == '-' || *uri == '+' || *uri == '.')
897 *ptr++ = *uri++;
898 else
899 break;
900
901 *ptr = '\0';
902
903 if (*uri != ':')
904 {
905 *scheme = '\0';
906 return (HTTP_URI_BAD_SCHEME);
907 }
908
909 uri ++;
910 }
911
912 /*
913 * Set the default port number...
914 */
915
916 if (!strcmp(scheme, "http"))
917 *port = 80;
918 else if (!strcmp(scheme, "https"))
919 *port = 443;
920 else if (!strcmp(scheme, "ipp"))
921 *port = 631;
922 else if (!strcasecmp(scheme, "lpd"))
923 *port = 515;
924 else if (!strcmp(scheme, "socket")) /* Not yet registered with IANA... */
925 *port = 9100;
926 else if (strcmp(scheme, "file") && strcmp(scheme, "mailto"))
927 status = HTTP_URI_UNKNOWN_SCHEME;
928
929 /*
930 * Now see if we have a hostname...
931 */
932
933 if (!strncmp(uri, "//", 2))
934 {
935 /*
936 * Yes, extract it...
937 */
938
939 uri += 2;
940
941 /*
942 * Grab the username, if any...
943 */
944
945 if ((sep = strpbrk(uri, "@/")) != NULL && *sep == '@')
946 {
947 /*
948 * Get a username:password combo...
949 */
950
951 uri = http_copy_decode(username, uri, usernamelen, "@",
952 decoding & HTTP_URI_CODING_USERNAME);
953
954 if (!uri)
955 {
956 *username = '\0';
957 return (HTTP_URI_BAD_USERNAME);
958 }
959
960 uri ++;
961 }
962
963 /*
964 * Then the hostname/IP address...
965 */
966
967 if (*uri == '[')
968 {
969 /*
970 * Grab IPv6 address...
971 */
972
973 uri ++;
974 if (!strncmp(uri, "v1.", 3))
975 uri += 3; /* Skip IPvN leader... */
976
977 uri = http_copy_decode(host, uri, hostlen, "]",
978 decoding & HTTP_URI_CODING_HOSTNAME);
979
980 if (!uri)
981 {
982 *host = '\0';
983 return (HTTP_URI_BAD_HOSTNAME);
984 }
985
986 /*
987 * Validate value...
988 */
989
990 if (*uri != ']')
991 {
992 *host = '\0';
993 return (HTTP_URI_BAD_HOSTNAME);
994 }
995
996 uri ++;
997
998 for (ptr = host; *ptr; ptr ++)
999 if (*ptr == '+')
1000 {
1001 /*
1002 * Convert zone separator to % and stop here...
1003 */
1004
1005 *ptr = '%';
1006 break;
1007 }
1008 else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255))
1009 {
1010 *host = '\0';
1011 return (HTTP_URI_BAD_HOSTNAME);
1012 }
1013 }
1014 else
1015 {
1016 /*
1017 * Validate the hostname or IPv4 address first...
1018 */
1019
1020 for (ptr = (char *)uri; *ptr; ptr ++)
1021 if (strchr(":?/", *ptr))
1022 break;
1023 else if (!strchr("abcdefghijklmnopqrstuvwxyz"
1024 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1025 "0123456789"
1026 "-._~"
1027 "%"
1028 "!$&'()*+,;=", *ptr))
1029 {
1030 *host = '\0';
1031 return (HTTP_URI_BAD_HOSTNAME);
1032 }
1033
1034 /*
1035 * Then copy the hostname or IPv4 address to the buffer...
1036 */
1037
1038 uri = http_copy_decode(host, uri, hostlen, ":?/",
1039 decoding & HTTP_URI_CODING_HOSTNAME);
1040
1041 if (!uri)
1042 {
1043 *host = '\0';
1044 return (HTTP_URI_BAD_HOSTNAME);
1045 }
1046 }
1047
1048 /*
1049 * Validate hostname for file scheme - only empty and localhost are
1050 * acceptable.
1051 */
1052
1053 if (!strcmp(scheme, "file") && strcmp(host, "localhost") && host[0])
1054 {
1055 *host = '\0';
1056 return (HTTP_URI_BAD_HOSTNAME);
1057 }
1058
1059 /*
1060 * See if we have a port number...
1061 */
1062
1063 if (*uri == ':')
1064 {
1065 /*
1066 * Yes, collect the port number...
1067 */
1068
1069 *port = strtol(uri + 1, (char **)&uri, 10);
1070
1071 if (*uri != '/' && *uri)
1072 {
1073 *port = 0;
1074 return (HTTP_URI_BAD_PORT);
1075 }
1076 }
1077 }
1078
1079 /*
1080 * The remaining portion is the resource string...
1081 */
1082
1083 if (*uri == '?' || !*uri)
1084 {
1085 /*
1086 * Hostname but no path...
1087 */
1088
1089 status = HTTP_URI_MISSING_RESOURCE;
1090 *resource = '/';
1091
1092 /*
1093 * Copy any query string...
1094 */
1095
1096 if (*uri == '?')
1097 uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL,
1098 decoding & HTTP_URI_CODING_QUERY);
1099 else
1100 resource[1] = '\0';
1101 }
1102 else
1103 {
1104 uri = http_copy_decode(resource, uri, resourcelen, "?",
1105 decoding & HTTP_URI_CODING_RESOURCE);
1106
1107 if (uri && *uri == '?')
1108 {
1109 /*
1110 * Concatenate any query string...
1111 */
1112
1113 char *resptr = resource + strlen(resource);
1114
1115 uri = http_copy_decode(resptr, uri, resourcelen - (int)(resptr - resource),
1116 NULL, decoding & HTTP_URI_CODING_QUERY);
1117 }
1118 }
1119
1120 if (!uri)
1121 {
1122 *resource = '\0';
1123 return (HTTP_URI_BAD_RESOURCE);
1124 }
1125
1126 /*
1127 * Return the URI separation status...
1128 */
1129
1130 return (status);
1131 }
1132
1133
1134 /*
1135 * 'httpStatus()' - Return a short string describing a HTTP status code.
1136 */
1137
1138 const char * /* O - String or NULL */
1139 httpStatus(http_status_t status) /* I - HTTP status code */
1140 {
1141 switch (status)
1142 {
1143 case HTTP_CONTINUE :
1144 return ("Continue");
1145 case HTTP_SWITCHING_PROTOCOLS :
1146 return ("Switching Protocols");
1147 case HTTP_OK :
1148 return ("OK");
1149 case HTTP_CREATED :
1150 return ("Created");
1151 case HTTP_ACCEPTED :
1152 return ("Accepted");
1153 case HTTP_NO_CONTENT :
1154 return ("No Content");
1155 case HTTP_MOVED_PERMANENTLY :
1156 return ("Moved Permanently");
1157 case HTTP_SEE_OTHER :
1158 return ("See Other");
1159 case HTTP_NOT_MODIFIED :
1160 return ("Not Modified");
1161 case HTTP_BAD_REQUEST :
1162 return ("Bad Request");
1163 case HTTP_UNAUTHORIZED :
1164 return ("Unauthorized");
1165 case HTTP_FORBIDDEN :
1166 return ("Forbidden");
1167 case HTTP_NOT_FOUND :
1168 return ("Not Found");
1169 case HTTP_REQUEST_TOO_LARGE :
1170 return ("Request Entity Too Large");
1171 case HTTP_URI_TOO_LONG :
1172 return ("URI Too Long");
1173 case HTTP_UPGRADE_REQUIRED :
1174 return ("Upgrade Required");
1175 case HTTP_NOT_IMPLEMENTED :
1176 return ("Not Implemented");
1177 case HTTP_NOT_SUPPORTED :
1178 return ("Not Supported");
1179 case HTTP_EXPECTATION_FAILED :
1180 return ("Expectation Failed");
1181
1182 default :
1183 return ("Unknown");
1184 }
1185 }
1186
1187
1188 #ifndef HAVE_HSTRERROR
1189 /*
1190 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others...
1191 */
1192
1193 const char * /* O - Error string */
1194 _cups_hstrerror(int error) /* I - Error number */
1195 {
1196 static const char * const errors[] = /* Error strings */
1197 {
1198 "OK",
1199 "Host not found.",
1200 "Try again.",
1201 "Unrecoverable lookup error.",
1202 "No data associated with name."
1203 };
1204
1205
1206 if (error < 0 || error > 4)
1207 return ("Unknown hostname lookup error.");
1208 else
1209 return (errors[error]);
1210 }
1211 #endif /* !HAVE_HSTRERROR */
1212
1213
1214 /*
1215 * 'http_copy_decode()' - Copy and decode a URI.
1216 */
1217
1218 static const char * /* O - New source pointer or NULL on error */
1219 http_copy_decode(char *dst, /* O - Destination buffer */
1220 const char *src, /* I - Source pointer */
1221 int dstsize, /* I - Destination size */
1222 const char *term, /* I - Terminating characters */
1223 int decode) /* I - Decode %-encoded values */
1224 {
1225 char *ptr, /* Pointer into buffer */
1226 *end; /* End of buffer */
1227 int quoted; /* Quoted character */
1228
1229
1230 /*
1231 * Copy the src to the destination until we hit a terminating character
1232 * or the end of the string.
1233 */
1234
1235 for (ptr = dst, end = dst + dstsize - 1;
1236 *src && (!term || !strchr(term, *src));
1237 src ++)
1238 if (ptr < end)
1239 {
1240 if (*src == '%' && decode)
1241 {
1242 if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255))
1243 {
1244 /*
1245 * Grab a hex-encoded character...
1246 */
1247
1248 src ++;
1249 if (isalpha(*src))
1250 quoted = (tolower(*src) - 'a' + 10) << 4;
1251 else
1252 quoted = (*src - '0') << 4;
1253
1254 src ++;
1255 if (isalpha(*src))
1256 quoted |= tolower(*src) - 'a' + 10;
1257 else
1258 quoted |= *src - '0';
1259
1260 *ptr++ = quoted;
1261 }
1262 else
1263 {
1264 /*
1265 * Bad hex-encoded character...
1266 */
1267
1268 *ptr = '\0';
1269 return (NULL);
1270 }
1271 }
1272 else
1273 *ptr++ = *src;
1274 }
1275
1276 *ptr = '\0';
1277
1278 return (src);
1279 }
1280
1281
1282 /*
1283 * 'http_copy_encode()' - Copy and encode a URI.
1284 */
1285
1286 static char * /* O - End of current URI */
1287 http_copy_encode(char *dst, /* O - Destination buffer */
1288 const char *src, /* I - Source pointer */
1289 char *dstend, /* I - End of destination buffer */
1290 const char *reserved, /* I - Extra reserved characters */
1291 const char *term, /* I - Terminating characters */
1292 int encode) /* I - %-encode reserved chars? */
1293 {
1294 static const char *hex = "0123456789ABCDEF";
1295
1296
1297 while (*src && dst < dstend)
1298 {
1299 if (term && *src == *term)
1300 return (dst);
1301
1302 if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
1303 (reserved && strchr(reserved, *src))))
1304 {
1305 /*
1306 * Hex encode reserved characters...
1307 */
1308
1309 if ((dst + 2) >= dstend)
1310 break;
1311
1312 *dst++ = '%';
1313 *dst++ = hex[(*src >> 4) & 15];
1314 *dst++ = hex[*src & 15];
1315
1316 src ++;
1317 }
1318 else
1319 *dst++ = *src++;
1320 }
1321
1322 if (*src)
1323 return (NULL);
1324 else
1325 return (dst);
1326 }
1327
1328
1329 /*
1330 * End of "$Id: http-support.c 6304 2007-02-22 22:06:23Z mike $".
1331 */