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