]> 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/*
fa73b229 2 * "$Id: http-support.c 4961 2006-01-20 22:19:13Z mike $"
ef416fc2 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 {
fa73b229 323 char *query; /* Pointer to query string */
324
325
ef416fc2 326 va_start(ap, resourcef);
327 bytes = vsnprintf(resource, sizeof(resource), resourcef, ap);
328 va_end(ap);
329
330 if (bytes >= sizeof(resource))
331 goto assemble_overflow;
332
fa73b229 333 /*
334 * Temporarily remove query string if present...
335 */
336
337 if ((query = strchr(resource, '?')) != NULL)
338 *query = '\0';
339
ef416fc2 340 ptr = http_copy_encode(ptr, resource, end, NULL);
341 if (!ptr)
342 goto assemble_overflow;
fa73b229 343
344 if (query)
345 {
346 /*
347 * Copy query string without encoding...
348 */
349
350 *query = '?';
351 strlcpy(ptr, query, end - ptr);
352 ptr += strlen(ptr);
353 }
ef416fc2 354 }
355 else if (ptr < end)
356 *ptr++ = '/';
357 else
358 goto assemble_overflow;
359
360 /*
361 * Nul-terminate the URI buffer and return with no errors...
362 */
363
364 *ptr = '\0';
365
366 return (HTTP_URI_OK);
367
368 /*
369 * Clear the URI string and return an overflow error; I don't usually
370 * like goto's, but in this case it makes sense...
371 */
372
373 assemble_overflow:
374
375 *uri = '\0';
376 return (HTTP_URI_OVERFLOW);
377}
378
379
380/*
381 * 'httpDecode64()' - Base64-decode a string.
382 */
383
384char * /* O - Decoded string */
385httpDecode64(char *out, /* I - String to write to */
386 const char *in) /* I - String to read from */
387{
388 int outlen; /* Output buffer length */
389
390
391 /*
392 * Use the old maximum buffer size for binary compatibility...
393 */
394
395 outlen = 512;
396
397 return (httpDecode64_2(out, &outlen, in));
398}
399
400
401/*
402 * 'httpDecode64_2()' - Base64-decode a string.
403 *
404 * @since CUPS 1.1.21@
405 */
406
407char * /* O - Decoded string */
408httpDecode64_2(char *out, /* I - String to write to */
409 int *outlen, /* IO - Size of output string */
410 const char *in) /* I - String to read from */
411{
412 int pos, /* Bit position */
413 base64; /* Value of this character */
414 char *outptr, /* Output pointer */
415 *outend; /* End of output buffer */
416
417
418 /*
419 * Range check input...
420 */
421
fa73b229 422 if (!out || !outlen || *outlen < 1 || !in)
ef416fc2 423 return (NULL);
424
fa73b229 425 if (!*in)
426 {
427 *out = '\0';
428 *outlen = 0;
429
430 return (out);
431 }
432
ef416fc2 433 /*
434 * Convert from base-64 to bytes...
435 */
436
437 for (outptr = out, outend = out + *outlen - 1, pos = 0; *in != '\0'; in ++)
438 {
439 /*
440 * Decode this character into a number from 0 to 63...
441 */
442
443 if (*in >= 'A' && *in <= 'Z')
444 base64 = *in - 'A';
445 else if (*in >= 'a' && *in <= 'z')
446 base64 = *in - 'a' + 26;
447 else if (*in >= '0' && *in <= '9')
448 base64 = *in - '0' + 52;
449 else if (*in == '+')
450 base64 = 62;
451 else if (*in == '/')
452 base64 = 63;
453 else if (*in == '=')
454 break;
455 else
456 continue;
457
458 /*
459 * Store the result in the appropriate chars...
460 */
461
462 switch (pos)
463 {
464 case 0 :
465 if (outptr < outend)
466 *outptr = base64 << 2;
467 pos ++;
468 break;
469 case 1 :
470 if (outptr < outend)
471 *outptr++ |= (base64 >> 4) & 3;
472 if (outptr < outend)
473 *outptr = (base64 << 4) & 255;
474 pos ++;
475 break;
476 case 2 :
477 if (outptr < outend)
478 *outptr++ |= (base64 >> 2) & 15;
479 if (outptr < outend)
480 *outptr = (base64 << 6) & 255;
481 pos ++;
482 break;
483 case 3 :
484 if (outptr < outend)
485 *outptr++ |= base64;
486 pos = 0;
487 break;
488 }
489 }
490
491 *outptr = '\0';
492
493 /*
494 * Return the decoded string and size...
495 */
496
497 *outlen = (int)(outptr - out);
498
499 return (out);
500}
501
502
503/*
504 * 'httpEncode64()' - Base64-encode a string.
505 */
506
507char * /* O - Encoded string */
508httpEncode64(char *out, /* I - String to write to */
509 const char *in) /* I - String to read from */
510{
511 return (httpEncode64_2(out, 512, in, strlen(in)));
512}
513
514
515/*
516 * 'httpEncode64_2()' - Base64-encode a string.
517 *
518 * @since CUPS 1.1.21@
519 */
520
521char * /* O - Encoded string */
522httpEncode64_2(char *out, /* I - String to write to */
523 int outlen, /* I - Size of output string */
524 const char *in, /* I - String to read from */
525 int inlen) /* I - Size of input string */
526{
527 char *outptr, /* Output pointer */
528 *outend; /* End of output buffer */
529 static const char base64[] = /* Base64 characters... */
530 {
531 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
532 "abcdefghijklmnopqrstuvwxyz"
533 "0123456789"
534 "+/"
535 };
536
537
538 /*
539 * Range check input...
540 */
541
542 if (!out || outlen < 1 || !in)
543 return (NULL);
544
545 /*
546 * Convert bytes to base-64...
547 */
548
549 for (outptr = out, outend = out + outlen - 1; inlen > 0; in ++, inlen --)
550 {
551 /*
552 * Encode the up to 3 characters as 4 Base64 numbers...
553 */
554
555 if (outptr < outend)
556 *outptr ++ = base64[(in[0] & 255) >> 2];
557 if (outptr < outend)
558 *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
559
560 in ++;
561 inlen --;
562 if (inlen <= 0)
563 {
564 if (outptr < outend)
565 *outptr ++ = '=';
566 if (outptr < outend)
567 *outptr ++ = '=';
568 break;
569 }
570
571 if (outptr < outend)
572 *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
573
574 in ++;
575 inlen --;
576 if (inlen <= 0)
577 {
578 if (outptr < outend)
579 *outptr ++ = '=';
580 break;
581 }
582
583 if (outptr < outend)
584 *outptr ++ = base64[in[0] & 63];
585 }
586
587 *outptr = '\0';
588
589 /*
590 * Return the encoded string...
591 */
592
593 return (out);
594}
595
596
597/*
598 * 'httpGetDateString()' - Get a formatted date/time string from a time value.
599 *
600 * @deprecated@
601 */
602
603const char * /* O - Date/time string */
604httpGetDateString(time_t t) /* I - UNIX time */
605{
606 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
607
608
609 return (httpGetDateString2(t, cg->http_date, sizeof(cg->http_date)));
610}
611
612
613/*
614 * 'httpGetDateString2()' - Get a formatted date/time string from a time value.
615 *
616 * @since CUPS 1.2@
617 */
618
619const char * /* O - Date/time string */
620httpGetDateString2(time_t t, /* I - UNIX time */
621 char *s, /* I - String buffer */
622 int slen) /* I - Size of string buffer */
623{
624 struct tm *tdate; /* UNIX date/time data */
625
626
627 tdate = gmtime(&t);
628 snprintf(s, slen, "%s, %02d %s %d %02d:%02d:%02d GMT",
629 http_days[tdate->tm_wday], tdate->tm_mday,
630 http_months[tdate->tm_mon], tdate->tm_year + 1900,
631 tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
632
633 return (s);
634}
635
636
637/*
638 * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
639 */
640
641time_t /* O - UNIX time */
642httpGetDateTime(const char *s) /* I - Date/time string */
643{
644 int i; /* Looping var */
645 char mon[16]; /* Abbreviated month name */
646 int day, year; /* Day of month and year */
647 int hour, min, sec; /* Time */
648 int days; /* Number of days since 1970 */
649 static const int normal_days[] = /* Days to a month, normal years */
650 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
651 static const int leap_days[] = /* Days to a month, leap years */
652 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
653
654
655 DEBUG_printf(("httpGetDateTime(s=\"%s\")\n", s));
656
657 /*
658 * Extract the date and time from the formatted string...
659 */
660
661 if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
662 return (0);
663
664 DEBUG_printf((" day=%d, mon=\"%s\", year=%d, hour=%d, min=%d, sec=%d\n",
665 day, mon, year, hour, min, sec));
666
667 /*
668 * Convert the month name to a number from 0 to 11.
669 */
670
671 for (i = 0; i < 12; i ++)
672 if (!strcasecmp(mon, http_months[i]))
673 break;
674
675 if (i >= 12)
676 return (0);
677
678 DEBUG_printf((" i=%d\n", i));
679
680 /*
681 * Now convert the date and time to a UNIX time value in seconds since
682 * 1970. We can't use mktime() since the timezone may not be UTC but
683 * the date/time string *is* UTC.
684 */
685
686 if ((year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0))
687 days = leap_days[i] + day - 1;
688 else
689 days = normal_days[i] + day - 1;
690
691 DEBUG_printf((" days=%d\n", days));
692
693 days += (year - 1970) * 365 + /* 365 days per year (normally) */
694 ((year - 1) / 4 - 492) - /* + leap days */
695 ((year - 1) / 100 - 19) + /* - 100 year days */
696 ((year - 1) / 400 - 4); /* + 400 year days */
697
698 DEBUG_printf((" days=%d\n", days));
699
700 return (days * 86400 + hour * 3600 + min * 60 + sec);
701}
702
703
704/*
705 * 'httpSeparate()' - Separate a Universal Resource Identifier into its
706 * components.
707 */
708
709void
710httpSeparate(const char *uri, /* I - Universal Resource Identifier */
711 char *scheme, /* O - Scheme [32] (http, https, etc.) */
712 char *username, /* O - Username [1024] */
713 char *host, /* O - Hostname [1024] */
714 int *port, /* O - Port number to use */
715 char *resource) /* O - Resource/filename [1024] */
716{
717 httpSeparateURI(uri, scheme, 32, username, HTTP_MAX_URI, host, HTTP_MAX_URI,
718 port, resource, HTTP_MAX_URI);
719}
720
721
722/*
723 * 'httpSeparate2()' - Separate a Universal Resource Identifier into its
724 * components.
725 *
726 * @since CUPS 1.1.21@
727 */
728
729void
730httpSeparate2(const char *uri, /* I - Universal Resource Identifier */
731 char *scheme, /* O - Scheme (http, https, etc.) */
732 int schemelen, /* I - Size of scheme buffer */
733 char *username, /* O - Username */
734 int usernamelen, /* I - Size of username buffer */
735 char *host, /* O - Hostname */
736 int hostlen, /* I - Size of hostname buffer */
737 int *port, /* O - Port number to use */
738 char *resource, /* O - Resource/filename */
739 int resourcelen) /* I - Size of resource buffer */
740{
741 httpSeparateURI(uri, scheme, schemelen, username, usernamelen, host, hostlen,
742 port, resource, resourcelen);
743}
744
745
746/*
747 * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its
748 * components.
749 *
750 * @since CUPS 1.2@
751 */
752
753http_uri_status_t /* O - Result of separation */
754httpSeparateURI(const char *uri, /* I - Universal Resource Identifier */
755 char *scheme, /* O - Scheme (http, https, etc.) */
756 int schemelen, /* I - Size of scheme buffer */
757 char *username, /* O - Username */
758 int usernamelen, /* I - Size of username buffer */
759 char *host, /* O - Hostname */
760 int hostlen, /* I - Size of hostname buffer */
761 int *port, /* O - Port number to use */
762 char *resource, /* O - Resource/filename */
763 int resourcelen) /* I - Size of resource buffer */
764{
765 char *ptr, /* Pointer into string... */
766 *end; /* End of string */
767 const char *sep; /* Separator character */
768 http_uri_status_t status; /* Result of separation */
769
770
771 /*
772 * Initialize everything to blank...
773 */
774
775 if (scheme && schemelen > 0)
776 *scheme = '\0';
777
778 if (username && usernamelen > 0)
779 *username = '\0';
780
781 if (host && hostlen > 0)
782 *host = '\0';
783
784 if (port)
785 *port = 0;
786
787 if (resource && resourcelen > 0)
788 *resource = '\0';
789
790 /*
791 * Range check input...
792 */
793
794 if (!uri || !port || !scheme || schemelen <= 0 || !username ||
795 usernamelen <= 0 || !host || hostlen <= 0 || !resource ||
796 resourcelen <= 0)
797 return (HTTP_URI_BAD_ARGUMENTS);
798
799 if (!*uri)
800 return (HTTP_URI_BAD_URI);
801
802 /*
803 * Grab the scheme portion of the URI...
804 */
805
806 status = HTTP_URI_OK;
807
808 if (!strncmp(uri, "//", 2))
809 {
810 /*
811 * Workaround for HP IPP client bug...
812 */
813
814 strlcpy(scheme, "ipp", schemelen);
815 status = HTTP_URI_MISSING_SCHEME;
816 }
817 else if (*uri == '/')
818 {
819 /*
820 * Filename...
821 */
822
823 strlcpy(scheme, "file", schemelen);
824 status = HTTP_URI_MISSING_SCHEME;
825 }
826 else
827 {
828 /*
829 * Standard URI with scheme...
830 */
831
832 for (ptr = scheme, end = scheme + schemelen - 1;
833 *uri && *uri != ':' && ptr < end;)
834 if (isalnum(*uri & 255) || *uri == '-' || *uri == '+' || *uri == '.')
835 *ptr++ = *uri++;
836 else
837 break;
838
839 *ptr = '\0';
840
841 if (*uri != ':')
842 {
843 *scheme = '\0';
844 return (HTTP_URI_BAD_SCHEME);
845 }
846
847 uri ++;
848 }
849
850 /*
851 * Set the default port number...
852 */
853
854 if (!strcmp(scheme, "http"))
855 *port = 80;
856 else if (!strcmp(scheme, "https"))
857 *port = 443;
858 else if (!strcmp(scheme, "ipp"))
859 *port = 631;
860 else if (!strcasecmp(scheme, "lpd"))
861 *port = 515;
862 else if (!strcmp(scheme, "socket")) /* Not yet registered with IANA... */
863 *port = 9100;
864 else if (strcmp(scheme, "file") && strcmp(scheme, "mailto"))
865 status = HTTP_URI_UNKNOWN_SCHEME;
866
867 /*
868 * Now see if we have a hostname...
869 */
870
871 if (!strncmp(uri, "//", 2))
872 {
873 /*
874 * Yes, extract it...
875 */
876
877 uri += 2;
878
879 /*
880 * Grab the username, if any...
881 */
882
883 if ((sep = strpbrk(uri, "@/")) != NULL && *sep == '@')
884 {
885 /*
886 * Get a username:password combo...
887 */
888
889 uri = http_copy_decode(username, uri, usernamelen, "@");
890
891 if (!uri)
892 {
893 *username = '\0';
894 return (HTTP_URI_BAD_USERNAME);
895 }
896
897 uri ++;
898 }
899
900 /*
901 * Then the hostname/IP address...
902 */
903
904 if (*uri == '[')
905 {
906 /*
907 * Grab IPv6 address...
908 */
909
910 uri ++;
911 if (!strncmp(uri, "v1.", 3))
912 uri += 3; /* Skip IPvN leader... */
913
914 uri = http_copy_decode(host, uri, hostlen, "]");
915
916 if (!uri)
917 {
918 *host = '\0';
919 return (HTTP_URI_BAD_HOSTNAME);
920 }
921
922 /*
923 * Validate value...
924 */
925
926 if (*uri != ']')
927 {
928 *host = '\0';
929 return (HTTP_URI_BAD_HOSTNAME);
930 }
931
932 uri ++;
933
934 for (ptr = host; *ptr; ptr ++)
935 if (*ptr == '+')
936 {
937 /*
938 * Convert zone separator to % and stop here...
939 */
940
941 *ptr = '%';
942 break;
943 }
944 else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255))
945 {
946 *host = '\0';
947 return (HTTP_URI_BAD_HOSTNAME);
948 }
949 }
950 else
951 {
952 /*
953 * Grab hostname or IPv4 address...
954 */
955
956 uri = http_copy_decode(host, uri, hostlen, ":?/");
957
958 if (!uri)
959 {
960 *host = '\0';
961 return (HTTP_URI_BAD_HOSTNAME);
962 }
963
964 /*
965 * Validate value...
966 */
967
968 for (ptr = host; *ptr; ptr ++)
969 if (!strchr("abcdefghijklmnopqrstuvwxyz"
970 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
971 "0123456789"
972 "-._~"
973 "!$&'()*+,;=", *ptr))
974 {
975 *host = '\0';
976 return (HTTP_URI_BAD_HOSTNAME);
977 }
978 }
979
980 /*
981 * Validate hostname for file scheme - only empty and localhost are
982 * acceptable.
983 */
984
985 if (!strcmp(scheme, "file") && strcmp(host, "localhost") && host[0])
986 {
987 *host = '\0';
988 return (HTTP_URI_BAD_HOSTNAME);
989 }
990
991 /*
992 * See if we have a port number...
993 */
994
995 if (*uri == ':')
996 {
997 /*
998 * Yes, collect the port number...
999 */
1000
1001 *port = strtol(uri + 1, (char **)&uri, 10);
1002
1003 if (*uri != '/')
1004 {
1005 *port = 0;
1006 return (HTTP_URI_BAD_PORT);
1007 }
1008 }
1009 }
1010
1011 /*
1012 * The remaining portion is the resource string...
1013 */
1014
1015 if (*uri == '?' || !*uri)
1016 {
1017 /*
1018 * Hostname but no path...
1019 */
1020
1021 status = HTTP_URI_MISSING_RESOURCE;
1022 *resource = '/';
fa73b229 1023
1024 /*
1025 * Copy any query string without decoding it...
1026 */
1027
1028 if (*uri == '?')
1029 {
1030 strlcpy(resource + 1, uri, resourcelen - 1);
1031 uri += strlen(uri);
1032 }
1033 else
1034 resource[1] = '\0';
ef416fc2 1035 }
1036 else
fa73b229 1037 {
1038 uri = http_copy_decode(resource, uri, resourcelen, "?");
1039
1040 if (uri && *uri == '?')
1041 {
1042 /*
1043 * Concatenate any query string without decoding it...
1044 */
1045
1046 strlcat(resource, uri, resourcelen);
1047 uri += strlen(uri);
1048 }
1049 }
ef416fc2 1050
1051 if (!uri)
1052 {
1053 *resource = '\0';
1054 return (HTTP_URI_BAD_RESOURCE);
1055 }
1056
1057 /*
1058 * Return the URI separation status...
1059 */
1060
1061 return (status);
1062}
1063
1064
1065/*
1066 * 'httpStatus()' - Return a short string describing a HTTP status code.
1067 */
1068
1069const char * /* O - String or NULL */
1070httpStatus(http_status_t status) /* I - HTTP status code */
1071{
1072 switch (status)
1073 {
1074 case HTTP_CONTINUE :
1075 return ("Continue");
1076 case HTTP_SWITCHING_PROTOCOLS :
1077 return ("Switching Protocols");
1078 case HTTP_OK :
1079 return ("OK");
1080 case HTTP_CREATED :
1081 return ("Created");
1082 case HTTP_ACCEPTED :
1083 return ("Accepted");
1084 case HTTP_NO_CONTENT :
1085 return ("No Content");
1086 case HTTP_NOT_MODIFIED :
1087 return ("Not Modified");
1088 case HTTP_BAD_REQUEST :
1089 return ("Bad Request");
1090 case HTTP_UNAUTHORIZED :
1091 return ("Unauthorized");
1092 case HTTP_FORBIDDEN :
1093 return ("Forbidden");
1094 case HTTP_NOT_FOUND :
1095 return ("Not Found");
1096 case HTTP_REQUEST_TOO_LARGE :
1097 return ("Request Entity Too Large");
1098 case HTTP_URI_TOO_LONG :
1099 return ("URI Too Long");
1100 case HTTP_UPGRADE_REQUIRED :
1101 return ("Upgrade Required");
1102 case HTTP_NOT_IMPLEMENTED :
1103 return ("Not Implemented");
1104 case HTTP_NOT_SUPPORTED :
1105 return ("Not Supported");
1106 default :
1107 return ("Unknown");
1108 }
1109}
1110
1111
1112#ifndef HAVE_HSTRERROR
1113/*
1114 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others...
1115 */
1116
1117const char * /* O - Error string */
1118_cups_hstrerror(int error) /* I - Error number */
1119{
1120 static const char * const errors[] = /* Error strings */
1121 {
1122 "OK",
1123 "Host not found.",
1124 "Try again.",
1125 "Unrecoverable lookup error.",
1126 "No data associated with name."
1127 };
1128
1129
1130 if (error < 0 || error > 4)
1131 return ("Unknown hostname lookup error.");
1132 else
1133 return (errors[error]);
1134}
1135#endif /* !HAVE_HSTRERROR */
1136
1137
1138/*
1139 * 'http_copy_decode()' - Copy and decode a URI.
1140 */
1141
1142static const char * /* O - New source pointer or NULL on error */
1143http_copy_decode(char *dst, /* O - Destination buffer */
1144 const char *src, /* I - Source pointer */
1145 int dstsize, /* I - Destination size */
1146 const char *term) /* I - Terminating characters */
1147{
1148 char *ptr, /* Pointer into buffer */
1149 *end; /* End of buffer */
1150 int quoted; /* Quoted character */
1151
1152
1153 /*
1154 * Copy the src to the destination until we hit a terminating character
1155 * or the end of the string.
1156 */
1157
1158 for (ptr = dst, end = dst + dstsize - 1; *src && !strchr(term, *src); src ++)
1159 if (ptr < end)
1160 {
1161 if (*src == '%')
1162 {
1163 if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255))
1164 {
1165 /*
1166 * Grab a hex-encoded character...
1167 */
1168
1169 src ++;
1170 if (isalpha(*src))
1171 quoted = (tolower(*src) - 'a' + 10) << 4;
1172 else
1173 quoted = (*src - '0') << 4;
1174
1175 src ++;
1176 if (isalpha(*src))
1177 quoted |= tolower(*src) - 'a' + 10;
1178 else
1179 quoted |= *src - '0';
1180
1181 *ptr++ = quoted;
1182 }
1183 else
1184 {
1185 /*
1186 * Bad hex-encoded character...
1187 */
1188
1189 *ptr = '\0';
1190 return (NULL);
1191 }
1192 }
1193 else
1194 *ptr++ = *src;
1195 }
1196
1197 *ptr = '\0';
1198
1199 return (src);
1200}
1201
1202
1203/*
1204 * 'http_copy_encode()' - Copy and encode a URI.
1205 */
1206
1207static char * /* O - End of current URI */
1208http_copy_encode(char *dst, /* O - Destination buffer */
1209 const char *src, /* I - Source pointer */
1210 char *dstend, /* I - End of destination buffer */
1211 const char *reserved) /* I - Extra reserved characters */
1212{
1213 static const char *hex = "0123456789ABCDEF";
1214
1215
1216 while (*src && dst < dstend)
1217 {
1218 if (*src == '%' || *src <= ' ' || *src & 128 ||
1219 (reserved && strchr(reserved, *src)))
1220 {
1221 /*
1222 * Hex encode reserved characters...
1223 */
1224
1225 if ((dst + 2) >= dstend)
1226 break;
1227
1228 *dst++ = '%';
1229 *dst++ = hex[(*src >> 4) & 15];
1230 *dst++ = hex[*src & 15];
1231
1232 src ++;
1233 }
1234 else
1235 *dst++ = *src++;
1236 }
1237
1238 if (*src)
1239 return (NULL);
1240 else
1241 return (dst);
1242}
1243
1244
1245/*
fa73b229 1246 * End of "$Id: http-support.c 4961 2006-01-20 22:19:13Z mike $".
ef416fc2 1247 */