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