]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/http-support.c
Merge changes from CUPS 1.5svn-r8849.
[thirdparty/cups.git] / cups / http-support.c
CommitLineData
ef416fc2 1/*
b19ccc9e 2 * "$Id: http-support.c 7952 2008-09-17 00:56:20Z mike $"
ef416fc2 3 *
4 * HTTP support routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
dfd5680b 6 * Copyright 2007-2009 by Apple Inc.
b86bc4cf 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
19 * httpAssembleURI() - Assemble a uniform resource identifier from its
20 * components.
21 * httpAssembleURIf() - Assemble a uniform resource identifier from its
22 * components with a formatted resource.
23 * httpDecode64() - Base64-decode a string.
24 * httpDecode64_2() - Base64-decode a string.
25 * httpEncode64() - Base64-encode a string.
26 * httpEncode64_2() - Base64-encode a string.
27 * httpGetDateString() - Get a formatted date/time string from a time value.
28 * httpGetDateString2() - Get a formatted date/time string from a time value.
29 * httpGetDateTime() - Get a time value from a formatted date/time string.
30 * httpSeparate() - Separate a Universal Resource Identifier into its
31 * components.
32 * httpSeparate2() - Separate a Universal Resource Identifier into its
33 * components.
34 * httpSeparateURI() - Separate a Universal Resource Identifier into its
35 * components.
36 * httpStatus() - Return a short string describing a HTTP status code.
37 * _cups_hstrerror() - hstrerror() emulation function for Solaris and
38 * others...
839a51c8 39 * _httpEncodeURI() - Percent-encode a HTTP request URI.
5eb9da71 40 * _httpResolveURI() - Resolve a DNS-SD URI.
ef416fc2 41 * http_copy_decode() - Copy and decode a URI.
42 * http_copy_encode() - Copy and encode a URI.
5eb9da71 43 * resolve_callback() - Build a device URI for the given service name.
ef416fc2 44 */
45
46/*
47 * Include necessary headers...
48 */
49
50#include "debug.h"
51#include "globals.h"
52#include <stdlib.h>
acb056cb 53#include <errno.h>
5eb9da71
MS
54#ifdef HAVE_DNSSD
55# include <dns_sd.h>
38e73f87 56# include <poll.h>
5eb9da71
MS
57#endif /* HAVE_DNSSD */
58
59
60/*
61 * Local types...
62 */
63
64typedef struct _http_uribuf_s /* URI buffer */
65{
66 char *buffer; /* Pointer to buffer */
67 size_t bufsize; /* Size of buffer */
68} _http_uribuf_t;
ef416fc2 69
70
71/*
72 * Local globals...
73 */
74
75static const char * const http_days[7] =
76 {
0a682745
MS
77 "Sun",
78 "Mon",
ef416fc2 79 "Tue",
80 "Wed",
81 "Thu",
82 "Fri",
83 "Sat"
84 };
85static const char * const http_months[12] =
86 {
87 "Jan",
88 "Feb",
89 "Mar",
90 "Apr",
91 "May",
92 "Jun",
93 "Jul",
94 "Aug",
95 "Sep",
96 "Oct",
97 "Nov",
98 "Dec"
99 };
100
101
102/*
103 * Local functions...
104 */
105
106static const char *http_copy_decode(char *dst, const char *src,
a4d04587 107 int dstsize, const char *term,
108 int decode);
ef416fc2 109static char *http_copy_encode(char *dst, const char *src,
a4d04587 110 char *dstend, const char *reserved,
111 const char *term, int encode);
5eb9da71
MS
112#ifdef HAVE_DNSSD
113static void resolve_callback(DNSServiceRef sdRef,
114 DNSServiceFlags flags,
115 uint32_t interfaceIndex,
116 DNSServiceErrorType errorCode,
117 const char *fullName,
118 const char *hostTarget,
119 uint16_t port, uint16_t txtLen,
120 const unsigned char *txtRecord,
121 void *context);
122#endif /* HAVE_DNSSD */
ef416fc2 123
124
125/*
126 * 'httpAssembleURI()' - Assemble a uniform resource identifier from its
127 * components.
128 *
ecdc0628 129 * This function escapes reserved characters in the URI depending on the
130 * value of the "encoding" argument. You should use this function in
131 * place of traditional string functions whenever you need to create a
132 * URI string.
ef416fc2 133 *
426c6a59 134 * @since CUPS 1.2/Mac OS X 10.5@
ef416fc2 135 */
136
137http_uri_status_t /* O - URI status */
a4d04587 138httpAssembleURI(
139 http_uri_coding_t encoding, /* I - Encoding flags */
140 char *uri, /* I - URI buffer */
141 int urilen, /* I - Size of URI buffer */
142 const char *scheme, /* I - Scheme name */
143 const char *username, /* I - Username */
144 const char *host, /* I - Hostname or address */
145 int port, /* I - Port number */
146 const char *resource) /* I - Resource */
ef416fc2 147{
148 char *ptr, /* Pointer into URI buffer */
149 *end; /* End of URI buffer */
ef416fc2 150
151
152 /*
153 * Range check input...
154 */
155
156 if (!uri || urilen < 1 || !scheme || port < 0)
157 {
158 if (uri)
159 *uri = '\0';
160
161 return (HTTP_URI_BAD_ARGUMENTS);
162 }
163
164 /*
165 * Assemble the URI starting with the scheme...
166 */
167
168 end = uri + urilen - 1;
a4d04587 169 ptr = http_copy_encode(uri, scheme, end, NULL, NULL, 0);
ef416fc2 170
171 if (!ptr)
172 goto assemble_overflow;
173
f7deaa1a 174 if (!strcmp(scheme, "mailto"))
ef416fc2 175 {
176 /*
177 * mailto: only has :, no //...
178 */
179
180 if (ptr < end)
181 *ptr++ = ':';
182 else
183 goto assemble_overflow;
184 }
185 else
186 {
187 /*
188 * Schemes other than mailto: all have //...
189 */
190
191 if ((ptr + 2) < end)
192 {
193 *ptr++ = ':';
194 *ptr++ = '/';
195 *ptr++ = '/';
196 }
197 else
198 goto assemble_overflow;
199 }
200
201 /*
202 * Next the username and hostname, if any...
203 */
204
205 if (host)
206 {
207 if (username && *username)
208 {
209 /*
210 * Add username@ first...
211 */
212
a4d04587 213 ptr = http_copy_encode(ptr, username, end, "/?@", NULL,
214 encoding & HTTP_URI_CODING_USERNAME);
ef416fc2 215
216 if (!ptr)
217 goto assemble_overflow;
218
219 if (ptr < end)
220 *ptr++ = '@';
221 else
222 goto assemble_overflow;
223 }
224
225 /*
226 * Then add the hostname. Since IPv6 is a particular pain to deal
c9fc04c6 227 * with, we have several special cases to deal with. If we get
ef416fc2 228 * an IPv6 address with brackets around it, assume it is already in
c9fc04c6
MS
229 * URI format. Since DNS-SD service names can sometimes look like
230 * raw IPv6 addresses, we specifically look for "._tcp" in the name,
231 * too...
ef416fc2 232 */
233
c9fc04c6 234 if (host[0] != '[' && strchr(host, ':') && !strstr(host, "._tcp"))
ef416fc2 235 {
236 /*
c9fc04c6 237 * We have a raw IPv6 address...
ef416fc2 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
bf3816c7 297 ptr = http_copy_encode(ptr, host, end, ":/?#[]@\\", NULL,
a4d04587 298 encoding & HTTP_URI_CODING_HOSTNAME);
ef416fc2 299
300 if (!ptr)
301 goto assemble_overflow;
302 }
303
304 /*
305 * Finish things off with the port number...
306 */
307
308 if (port > 0)
309 {
310 snprintf(ptr, end - ptr + 1, ":%d", port);
311 ptr += strlen(ptr);
312
313 if (ptr >= end)
314 goto assemble_overflow;
315 }
316 }
317
318 /*
319 * Last but not least, add the resource string...
320 */
321
a4d04587 322 if (resource)
ef416fc2 323 {
fa73b229 324 char *query; /* Pointer to query string */
325
326
fa73b229 327 /*
a4d04587 328 * Copy the resource string up to the query string if present...
fa73b229 329 */
330
a4d04587 331 query = strchr(resource, '?');
332 ptr = http_copy_encode(ptr, resource, end, NULL, "?",
333 encoding & HTTP_URI_CODING_RESOURCE);
ef416fc2 334 if (!ptr)
335 goto assemble_overflow;
fa73b229 336
337 if (query)
338 {
339 /*
340 * Copy query string without encoding...
341 */
342
a4d04587 343 ptr = http_copy_encode(ptr, query, end, NULL, NULL,
344 encoding & HTTP_URI_CODING_QUERY);
345 if (!ptr)
346 goto assemble_overflow;
fa73b229 347 }
ef416fc2 348 }
349 else if (ptr < end)
350 *ptr++ = '/';
351 else
352 goto assemble_overflow;
353
354 /*
355 * Nul-terminate the URI buffer and return with no errors...
356 */
357
358 *ptr = '\0';
359
360 return (HTTP_URI_OK);
361
362 /*
363 * Clear the URI string and return an overflow error; I don't usually
364 * like goto's, but in this case it makes sense...
365 */
366
367 assemble_overflow:
368
369 *uri = '\0';
370 return (HTTP_URI_OVERFLOW);
371}
372
373
a4d04587 374/*
375 * 'httpAssembleURIf()' - Assemble a uniform resource identifier from its
376 * components with a formatted resource.
377 *
378 * This function creates a formatted version of the resource string
ecdc0628 379 * argument "resourcef" and escapes reserved characters in the URI
380 * depending on the value of the "encoding" argument. You should use
381 * this function in place of traditional string functions whenever
382 * you need to create a URI string.
a4d04587 383 *
426c6a59 384 * @since CUPS 1.2/Mac OS X 10.5@
a4d04587 385 */
386
387http_uri_status_t /* O - URI status */
388httpAssembleURIf(
389 http_uri_coding_t encoding, /* I - Encoding flags */
390 char *uri, /* I - URI buffer */
391 int urilen, /* I - Size of URI buffer */
392 const char *scheme, /* I - Scheme name */
393 const char *username, /* I - Username */
394 const char *host, /* I - Hostname or address */
395 int port, /* I - Port number */
396 const char *resourcef, /* I - Printf-style resource */
397 ...) /* I - Additional arguments as needed */
398{
399 va_list ap; /* Pointer to additional arguments */
400 char resource[1024]; /* Formatted resource string */
401 int bytes; /* Bytes in formatted string */
402
403
404 /*
405 * Range check input...
406 */
407
408 if (!uri || urilen < 1 || !scheme || port < 0 || !resourcef)
409 {
410 if (uri)
411 *uri = '\0';
412
413 return (HTTP_URI_BAD_ARGUMENTS);
414 }
415
416 /*
417 * Format the resource string and assemble the URI...
418 */
419
420 va_start(ap, resourcef);
421 bytes = vsnprintf(resource, sizeof(resource), resourcef, ap);
422 va_end(ap);
423
424 if (bytes >= sizeof(resource))
425 {
426 *uri = '\0';
427 return (HTTP_URI_OVERFLOW);
428 }
429 else
430 return (httpAssembleURI(encoding, uri, urilen, scheme, username, host,
431 port, resource));
432}
433
434
ef416fc2 435/*
436 * 'httpDecode64()' - Base64-decode a string.
ecdc0628 437 *
438 * This function is deprecated. Use the httpDecode64_2() function instead
439 * which provides buffer length arguments.
440 *
441 * @deprecated@
ef416fc2 442 */
443
444char * /* O - Decoded string */
445httpDecode64(char *out, /* I - String to write to */
446 const char *in) /* I - String to read from */
447{
448 int outlen; /* Output buffer length */
449
450
451 /*
452 * Use the old maximum buffer size for binary compatibility...
453 */
454
455 outlen = 512;
456
457 return (httpDecode64_2(out, &outlen, in));
458}
459
460
461/*
462 * 'httpDecode64_2()' - Base64-decode a string.
463 *
426c6a59 464 * @since CUPS 1.1.21/Mac OS X 10.4@
ef416fc2 465 */
466
467char * /* O - Decoded string */
468httpDecode64_2(char *out, /* I - String to write to */
469 int *outlen, /* IO - Size of output string */
470 const char *in) /* I - String to read from */
471{
472 int pos, /* Bit position */
473 base64; /* Value of this character */
474 char *outptr, /* Output pointer */
475 *outend; /* End of output buffer */
476
477
478 /*
479 * Range check input...
480 */
481
fa73b229 482 if (!out || !outlen || *outlen < 1 || !in)
ef416fc2 483 return (NULL);
484
fa73b229 485 if (!*in)
486 {
487 *out = '\0';
488 *outlen = 0;
489
490 return (out);
491 }
492
ef416fc2 493 /*
494 * Convert from base-64 to bytes...
495 */
496
497 for (outptr = out, outend = out + *outlen - 1, pos = 0; *in != '\0'; in ++)
498 {
499 /*
500 * Decode this character into a number from 0 to 63...
501 */
502
503 if (*in >= 'A' && *in <= 'Z')
504 base64 = *in - 'A';
505 else if (*in >= 'a' && *in <= 'z')
506 base64 = *in - 'a' + 26;
507 else if (*in >= '0' && *in <= '9')
508 base64 = *in - '0' + 52;
509 else if (*in == '+')
510 base64 = 62;
511 else if (*in == '/')
512 base64 = 63;
513 else if (*in == '=')
514 break;
515 else
516 continue;
517
518 /*
519 * Store the result in the appropriate chars...
520 */
521
522 switch (pos)
523 {
524 case 0 :
525 if (outptr < outend)
526 *outptr = base64 << 2;
527 pos ++;
528 break;
529 case 1 :
530 if (outptr < outend)
531 *outptr++ |= (base64 >> 4) & 3;
532 if (outptr < outend)
533 *outptr = (base64 << 4) & 255;
534 pos ++;
535 break;
536 case 2 :
537 if (outptr < outend)
538 *outptr++ |= (base64 >> 2) & 15;
539 if (outptr < outend)
540 *outptr = (base64 << 6) & 255;
541 pos ++;
542 break;
543 case 3 :
544 if (outptr < outend)
545 *outptr++ |= base64;
546 pos = 0;
547 break;
548 }
549 }
550
551 *outptr = '\0';
552
553 /*
554 * Return the decoded string and size...
555 */
556
557 *outlen = (int)(outptr - out);
558
559 return (out);
560}
561
562
563/*
564 * 'httpEncode64()' - Base64-encode a string.
ecdc0628 565 *
566 * This function is deprecated. Use the httpEncode64_2() function instead
567 * which provides buffer length arguments.
568 *
569 * @deprecated@
ef416fc2 570 */
571
572char * /* O - Encoded string */
573httpEncode64(char *out, /* I - String to write to */
574 const char *in) /* I - String to read from */
575{
b86bc4cf 576 return (httpEncode64_2(out, 512, in, (int)strlen(in)));
ef416fc2 577}
578
579
580/*
581 * 'httpEncode64_2()' - Base64-encode a string.
582 *
426c6a59 583 * @since CUPS 1.1.21/Mac OS X 10.4@
ef416fc2 584 */
585
586char * /* O - Encoded string */
587httpEncode64_2(char *out, /* I - String to write to */
588 int outlen, /* I - Size of output string */
589 const char *in, /* I - String to read from */
590 int inlen) /* I - Size of input string */
591{
592 char *outptr, /* Output pointer */
593 *outend; /* End of output buffer */
594 static const char base64[] = /* Base64 characters... */
595 {
596 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
597 "abcdefghijklmnopqrstuvwxyz"
598 "0123456789"
599 "+/"
600 };
601
602
603 /*
604 * Range check input...
605 */
606
607 if (!out || outlen < 1 || !in)
608 return (NULL);
609
610 /*
611 * Convert bytes to base-64...
612 */
613
614 for (outptr = out, outend = out + outlen - 1; inlen > 0; in ++, inlen --)
615 {
616 /*
617 * Encode the up to 3 characters as 4 Base64 numbers...
618 */
619
620 if (outptr < outend)
621 *outptr ++ = base64[(in[0] & 255) >> 2];
26d47ec6 622
ef416fc2 623 if (outptr < outend)
26d47ec6 624 {
625 if (inlen > 1)
626 *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
627 else
628 *outptr ++ = base64[((in[0] & 255) << 4) & 63];
629 }
ef416fc2 630
631 in ++;
632 inlen --;
633 if (inlen <= 0)
634 {
635 if (outptr < outend)
636 *outptr ++ = '=';
637 if (outptr < outend)
638 *outptr ++ = '=';
639 break;
640 }
641
642 if (outptr < outend)
26d47ec6 643 {
644 if (inlen > 1)
645 *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
646 else
647 *outptr ++ = base64[((in[0] & 255) << 2) & 63];
648 }
ef416fc2 649
650 in ++;
651 inlen --;
652 if (inlen <= 0)
653 {
654 if (outptr < outend)
655 *outptr ++ = '=';
656 break;
657 }
658
659 if (outptr < outend)
660 *outptr ++ = base64[in[0] & 63];
661 }
662
663 *outptr = '\0';
664
665 /*
666 * Return the encoded string...
667 */
668
669 return (out);
670}
671
672
673/*
674 * 'httpGetDateString()' - Get a formatted date/time string from a time value.
675 *
676 * @deprecated@
677 */
678
679const char * /* O - Date/time string */
680httpGetDateString(time_t t) /* I - UNIX time */
681{
682 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
683
684
685 return (httpGetDateString2(t, cg->http_date, sizeof(cg->http_date)));
686}
687
688
689/*
690 * 'httpGetDateString2()' - Get a formatted date/time string from a time value.
691 *
426c6a59 692 * @since CUPS 1.2/Mac OS X 10.5@
ef416fc2 693 */
694
695const char * /* O - Date/time string */
696httpGetDateString2(time_t t, /* I - UNIX time */
697 char *s, /* I - String buffer */
698 int slen) /* I - Size of string buffer */
699{
700 struct tm *tdate; /* UNIX date/time data */
701
702
703 tdate = gmtime(&t);
704 snprintf(s, slen, "%s, %02d %s %d %02d:%02d:%02d GMT",
705 http_days[tdate->tm_wday], tdate->tm_mday,
706 http_months[tdate->tm_mon], tdate->tm_year + 1900,
707 tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
708
709 return (s);
710}
711
712
713/*
714 * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
715 */
716
717time_t /* O - UNIX time */
718httpGetDateTime(const char *s) /* I - Date/time string */
719{
720 int i; /* Looping var */
721 char mon[16]; /* Abbreviated month name */
722 int day, year; /* Day of month and year */
723 int hour, min, sec; /* Time */
724 int days; /* Number of days since 1970 */
725 static const int normal_days[] = /* Days to a month, normal years */
726 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
727 static const int leap_days[] = /* Days to a month, leap years */
728 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
729
730
e07d4801 731 DEBUG_printf(("2httpGetDateTime(s=\"%s\")", s));
ef416fc2 732
733 /*
734 * Extract the date and time from the formatted string...
735 */
736
737 if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
738 return (0);
739
e07d4801
MS
740 DEBUG_printf(("4httpGetDateTime: day=%d, mon=\"%s\", year=%d, hour=%d, "
741 "min=%d, sec=%d", day, mon, year, hour, min, sec));
ef416fc2 742
743 /*
744 * Convert the month name to a number from 0 to 11.
745 */
746
747 for (i = 0; i < 12; i ++)
748 if (!strcasecmp(mon, http_months[i]))
749 break;
750
751 if (i >= 12)
752 return (0);
753
e07d4801 754 DEBUG_printf(("4httpGetDateTime: i=%d", i));
ef416fc2 755
756 /*
757 * Now convert the date and time to a UNIX time value in seconds since
758 * 1970. We can't use mktime() since the timezone may not be UTC but
759 * the date/time string *is* UTC.
760 */
761
762 if ((year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0))
763 days = leap_days[i] + day - 1;
764 else
765 days = normal_days[i] + day - 1;
766
e07d4801 767 DEBUG_printf(("4httpGetDateTime: days=%d", days));
ef416fc2 768
769 days += (year - 1970) * 365 + /* 365 days per year (normally) */
770 ((year - 1) / 4 - 492) - /* + leap days */
771 ((year - 1) / 100 - 19) + /* - 100 year days */
772 ((year - 1) / 400 - 4); /* + 400 year days */
773
e07d4801 774 DEBUG_printf(("4httpGetDateTime: days=%d\n", days));
ef416fc2 775
776 return (days * 86400 + hour * 3600 + min * 60 + sec);
777}
778
779
780/*
781 * 'httpSeparate()' - Separate a Universal Resource Identifier into its
782 * components.
ecdc0628 783 *
784 * This function is deprecated; use the httpSeparateURI() function instead.
785 *
786 * @deprecated@
ef416fc2 787 */
788
789void
790httpSeparate(const char *uri, /* I - Universal Resource Identifier */
791 char *scheme, /* O - Scheme [32] (http, https, etc.) */
792 char *username, /* O - Username [1024] */
793 char *host, /* O - Hostname [1024] */
794 int *port, /* O - Port number to use */
795 char *resource) /* O - Resource/filename [1024] */
796{
a4d04587 797 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username,
798 HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource,
799 HTTP_MAX_URI);
ef416fc2 800}
801
802
803/*
804 * 'httpSeparate2()' - Separate a Universal Resource Identifier into its
805 * components.
806 *
ecdc0628 807 * This function is deprecated; use the httpSeparateURI() function instead.
808 *
426c6a59 809 * @since CUPS 1.1.21/Mac OS X 10.4@
ecdc0628 810 * @deprecated@
ef416fc2 811 */
812
813void
814httpSeparate2(const char *uri, /* I - Universal Resource Identifier */
815 char *scheme, /* O - Scheme (http, https, etc.) */
816 int schemelen, /* I - Size of scheme buffer */
817 char *username, /* O - Username */
818 int usernamelen, /* I - Size of username buffer */
819 char *host, /* O - Hostname */
820 int hostlen, /* I - Size of hostname buffer */
821 int *port, /* O - Port number to use */
822 char *resource, /* O - Resource/filename */
823 int resourcelen) /* I - Size of resource buffer */
824{
a4d04587 825 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username,
826 usernamelen, host, hostlen, port, resource, resourcelen);
ef416fc2 827}
828
829
830/*
831 * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its
832 * components.
833 *
426c6a59 834 * @since CUPS 1.2/Mac OS X 10.5@
ef416fc2 835 */
836
837http_uri_status_t /* O - Result of separation */
a4d04587 838httpSeparateURI(
839 http_uri_coding_t decoding, /* I - Decoding flags */
840 const char *uri, /* I - Universal Resource Identifier */
841 char *scheme, /* O - Scheme (http, https, etc.) */
842 int schemelen, /* I - Size of scheme buffer */
843 char *username, /* O - Username */
844 int usernamelen, /* I - Size of username buffer */
845 char *host, /* O - Hostname */
846 int hostlen, /* I - Size of hostname buffer */
847 int *port, /* O - Port number to use */
848 char *resource, /* O - Resource/filename */
849 int resourcelen) /* I - Size of resource buffer */
ef416fc2 850{
851 char *ptr, /* Pointer into string... */
852 *end; /* End of string */
853 const char *sep; /* Separator character */
854 http_uri_status_t status; /* Result of separation */
855
856
857 /*
858 * Initialize everything to blank...
859 */
860
861 if (scheme && schemelen > 0)
862 *scheme = '\0';
863
864 if (username && usernamelen > 0)
865 *username = '\0';
866
867 if (host && hostlen > 0)
868 *host = '\0';
869
870 if (port)
871 *port = 0;
872
873 if (resource && resourcelen > 0)
874 *resource = '\0';
875
876 /*
877 * Range check input...
878 */
879
880 if (!uri || !port || !scheme || schemelen <= 0 || !username ||
881 usernamelen <= 0 || !host || hostlen <= 0 || !resource ||
882 resourcelen <= 0)
883 return (HTTP_URI_BAD_ARGUMENTS);
884
885 if (!*uri)
886 return (HTTP_URI_BAD_URI);
887
888 /*
889 * Grab the scheme portion of the URI...
890 */
891
892 status = HTTP_URI_OK;
893
894 if (!strncmp(uri, "//", 2))
895 {
896 /*
897 * Workaround for HP IPP client bug...
898 */
899
900 strlcpy(scheme, "ipp", schemelen);
901 status = HTTP_URI_MISSING_SCHEME;
902 }
903 else if (*uri == '/')
904 {
905 /*
906 * Filename...
907 */
908
909 strlcpy(scheme, "file", schemelen);
910 status = HTTP_URI_MISSING_SCHEME;
911 }
912 else
913 {
914 /*
915 * Standard URI with scheme...
916 */
917
918 for (ptr = scheme, end = scheme + schemelen - 1;
919 *uri && *uri != ':' && ptr < end;)
920 if (isalnum(*uri & 255) || *uri == '-' || *uri == '+' || *uri == '.')
921 *ptr++ = *uri++;
922 else
923 break;
924
925 *ptr = '\0';
926
927 if (*uri != ':')
928 {
929 *scheme = '\0';
930 return (HTTP_URI_BAD_SCHEME);
931 }
932
933 uri ++;
934 }
935
936 /*
937 * Set the default port number...
938 */
939
940 if (!strcmp(scheme, "http"))
941 *port = 80;
942 else if (!strcmp(scheme, "https"))
943 *port = 443;
944 else if (!strcmp(scheme, "ipp"))
945 *port = 631;
946 else if (!strcasecmp(scheme, "lpd"))
947 *port = 515;
948 else if (!strcmp(scheme, "socket")) /* Not yet registered with IANA... */
949 *port = 9100;
950 else if (strcmp(scheme, "file") && strcmp(scheme, "mailto"))
951 status = HTTP_URI_UNKNOWN_SCHEME;
952
953 /*
954 * Now see if we have a hostname...
955 */
956
957 if (!strncmp(uri, "//", 2))
958 {
959 /*
960 * Yes, extract it...
961 */
962
963 uri += 2;
964
965 /*
966 * Grab the username, if any...
967 */
968
969 if ((sep = strpbrk(uri, "@/")) != NULL && *sep == '@')
970 {
971 /*
972 * Get a username:password combo...
973 */
974
a4d04587 975 uri = http_copy_decode(username, uri, usernamelen, "@",
976 decoding & HTTP_URI_CODING_USERNAME);
ef416fc2 977
978 if (!uri)
979 {
980 *username = '\0';
981 return (HTTP_URI_BAD_USERNAME);
982 }
983
984 uri ++;
985 }
986
987 /*
988 * Then the hostname/IP address...
989 */
990
991 if (*uri == '[')
992 {
993 /*
994 * Grab IPv6 address...
995 */
996
997 uri ++;
998 if (!strncmp(uri, "v1.", 3))
999 uri += 3; /* Skip IPvN leader... */
1000
a4d04587 1001 uri = http_copy_decode(host, uri, hostlen, "]",
1002 decoding & HTTP_URI_CODING_HOSTNAME);
ef416fc2 1003
1004 if (!uri)
1005 {
1006 *host = '\0';
1007 return (HTTP_URI_BAD_HOSTNAME);
1008 }
1009
1010 /*
1011 * Validate value...
1012 */
1013
1014 if (*uri != ']')
1015 {
1016 *host = '\0';
1017 return (HTTP_URI_BAD_HOSTNAME);
1018 }
1019
1020 uri ++;
1021
1022 for (ptr = host; *ptr; ptr ++)
1023 if (*ptr == '+')
1024 {
1025 /*
1026 * Convert zone separator to % and stop here...
1027 */
1028
1029 *ptr = '%';
1030 break;
1031 }
1032 else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255))
1033 {
1034 *host = '\0';
1035 return (HTTP_URI_BAD_HOSTNAME);
1036 }
1037 }
1038 else
1039 {
1040 /*
b423cd4c 1041 * Validate the hostname or IPv4 address first...
1042 */
1043
1044 for (ptr = (char *)uri; *ptr; ptr ++)
1045 if (strchr(":?/", *ptr))
1046 break;
1047 else if (!strchr("abcdefghijklmnopqrstuvwxyz"
1048 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1049 "0123456789"
1050 "-._~"
1051 "%"
2fb76298 1052 "!$&'()*+,;=\\", *ptr))
b423cd4c 1053 {
1054 *host = '\0';
1055 return (HTTP_URI_BAD_HOSTNAME);
1056 }
1057
1058 /*
1059 * Then copy the hostname or IPv4 address to the buffer...
ef416fc2 1060 */
1061
a4d04587 1062 uri = http_copy_decode(host, uri, hostlen, ":?/",
1063 decoding & HTTP_URI_CODING_HOSTNAME);
ef416fc2 1064
1065 if (!uri)
1066 {
1067 *host = '\0';
1068 return (HTTP_URI_BAD_HOSTNAME);
1069 }
ef416fc2 1070 }
1071
1072 /*
1073 * Validate hostname for file scheme - only empty and localhost are
1074 * acceptable.
1075 */
1076
1077 if (!strcmp(scheme, "file") && strcmp(host, "localhost") && host[0])
1078 {
1079 *host = '\0';
1080 return (HTTP_URI_BAD_HOSTNAME);
1081 }
1082
1083 /*
1084 * See if we have a port number...
1085 */
1086
1087 if (*uri == ':')
1088 {
1089 /*
1090 * Yes, collect the port number...
1091 */
1092
dfd5680b
MS
1093 if (!isdigit(uri[1] & 255))
1094 {
1095 *port = 0;
1096 return (HTTP_URI_BAD_PORT);
1097 }
1098
ef416fc2 1099 *port = strtol(uri + 1, (char **)&uri, 10);
1100
d6ae789d 1101 if (*uri != '/' && *uri)
ef416fc2 1102 {
1103 *port = 0;
1104 return (HTTP_URI_BAD_PORT);
1105 }
1106 }
1107 }
1108
1109 /*
1110 * The remaining portion is the resource string...
1111 */
1112
1113 if (*uri == '?' || !*uri)
1114 {
1115 /*
1116 * Hostname but no path...
1117 */
1118
1119 status = HTTP_URI_MISSING_RESOURCE;
1120 *resource = '/';
fa73b229 1121
1122 /*
a4d04587 1123 * Copy any query string...
fa73b229 1124 */
1125
1126 if (*uri == '?')
a4d04587 1127 uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL,
1128 decoding & HTTP_URI_CODING_QUERY);
fa73b229 1129 else
1130 resource[1] = '\0';
ef416fc2 1131 }
1132 else
fa73b229 1133 {
a4d04587 1134 uri = http_copy_decode(resource, uri, resourcelen, "?",
1135 decoding & HTTP_URI_CODING_RESOURCE);
fa73b229 1136
1137 if (uri && *uri == '?')
1138 {
1139 /*
a4d04587 1140 * Concatenate any query string...
fa73b229 1141 */
1142
a4d04587 1143 char *resptr = resource + strlen(resource);
1144
b86bc4cf 1145 uri = http_copy_decode(resptr, uri, resourcelen - (int)(resptr - resource),
a4d04587 1146 NULL, decoding & HTTP_URI_CODING_QUERY);
fa73b229 1147 }
1148 }
ef416fc2 1149
1150 if (!uri)
1151 {
1152 *resource = '\0';
1153 return (HTTP_URI_BAD_RESOURCE);
1154 }
1155
1156 /*
1157 * Return the URI separation status...
1158 */
1159
1160 return (status);
1161}
1162
1163
1164/*
1165 * 'httpStatus()' - Return a short string describing a HTTP status code.
9f5eb9be
MS
1166 *
1167 * The returned string is localized to the current POSIX locale and is based
1168 * on the status strings defined in RFC 2616.
ef416fc2 1169 */
1170
9f5eb9be 1171const char * /* O - Localized status string */
ef416fc2 1172httpStatus(http_status_t status) /* I - HTTP status code */
1173{
9f5eb9be
MS
1174 const char *s; /* Status string */
1175 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
1176
1177
1178 if (!cg->lang_default)
1179 cg->lang_default = cupsLangDefault();
1180
ef416fc2 1181 switch (status)
1182 {
1183 case HTTP_CONTINUE :
9f5eb9be
MS
1184 s = _("Continue");
1185 break;
ef416fc2 1186 case HTTP_SWITCHING_PROTOCOLS :
9f5eb9be
MS
1187 s = _("Switching Protocols");
1188 break;
ef416fc2 1189 case HTTP_OK :
9f5eb9be
MS
1190 s = _("OK");
1191 break;
ef416fc2 1192 case HTTP_CREATED :
9f5eb9be
MS
1193 s = _("Created");
1194 break;
ef416fc2 1195 case HTTP_ACCEPTED :
9f5eb9be
MS
1196 s = _("Accepted");
1197 break;
ef416fc2 1198 case HTTP_NO_CONTENT :
9f5eb9be
MS
1199 s = _("No Content");
1200 break;
d6ae789d 1201 case HTTP_MOVED_PERMANENTLY :
9f5eb9be
MS
1202 s = _("Moved Permanently");
1203 break;
d6ae789d 1204 case HTTP_SEE_OTHER :
9f5eb9be
MS
1205 s = _("See Other");
1206 break;
ef416fc2 1207 case HTTP_NOT_MODIFIED :
9f5eb9be
MS
1208 s = _("Not Modified");
1209 break;
ef416fc2 1210 case HTTP_BAD_REQUEST :
9f5eb9be
MS
1211 s = _("Bad Request");
1212 break;
ef416fc2 1213 case HTTP_UNAUTHORIZED :
f11a948a 1214 case HTTP_AUTHORIZATION_CANCELED :
9f5eb9be
MS
1215 s = _("Unauthorized");
1216 break;
ef416fc2 1217 case HTTP_FORBIDDEN :
9f5eb9be
MS
1218 s = _("Forbidden");
1219 break;
ef416fc2 1220 case HTTP_NOT_FOUND :
9f5eb9be
MS
1221 s = _("Not Found");
1222 break;
ef416fc2 1223 case HTTP_REQUEST_TOO_LARGE :
9f5eb9be
MS
1224 s = _("Request Entity Too Large");
1225 break;
ef416fc2 1226 case HTTP_URI_TOO_LONG :
9f5eb9be
MS
1227 s = _("URI Too Long");
1228 break;
ef416fc2 1229 case HTTP_UPGRADE_REQUIRED :
9f5eb9be
MS
1230 s = _("Upgrade Required");
1231 break;
ef416fc2 1232 case HTTP_NOT_IMPLEMENTED :
9f5eb9be
MS
1233 s = _("Not Implemented");
1234 break;
ef416fc2 1235 case HTTP_NOT_SUPPORTED :
9f5eb9be
MS
1236 s = _("Not Supported");
1237 break;
b423cd4c 1238 case HTTP_EXPECTATION_FAILED :
9f5eb9be
MS
1239 s = _("Expectation Failed");
1240 break;
1241 case HTTP_SERVICE_UNAVAILABLE :
1242 s = _("Service Unavailable");
1243 break;
94da7e34
MS
1244 case HTTP_SERVER_ERROR :
1245 s = _("Internal Server Error");
1246 break;
b423cd4c 1247
ef416fc2 1248 default :
9f5eb9be
MS
1249 s = _("Unknown");
1250 break;
ef416fc2 1251 }
9f5eb9be
MS
1252
1253 return (_cupsLangString(cg->lang_default, s));
ef416fc2 1254}
1255
1256
1257#ifndef HAVE_HSTRERROR
1258/*
1259 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others...
1260 */
1261
1262const char * /* O - Error string */
1263_cups_hstrerror(int error) /* I - Error number */
1264{
1265 static const char * const errors[] = /* Error strings */
1266 {
1267 "OK",
1268 "Host not found.",
1269 "Try again.",
1270 "Unrecoverable lookup error.",
1271 "No data associated with name."
1272 };
1273
1274
1275 if (error < 0 || error > 4)
1276 return ("Unknown hostname lookup error.");
1277 else
1278 return (errors[error]);
1279}
1280#endif /* !HAVE_HSTRERROR */
1281
1282
839a51c8
MS
1283/*
1284 * '_httpEncodeURI()' - Percent-encode a HTTP request URI.
1285 */
1286
1287char * /* O - Encoded URI */
1288_httpEncodeURI(char *dst, /* I - Destination buffer */
1289 const char *src, /* I - Source URI */
1290 size_t dstsize) /* I - Size of destination buffer */
1291{
1292 http_copy_encode(dst, src, dst + dstsize - 1, NULL, NULL, 1);
1293 return (dst);
1294}
1295
1296
5eb9da71
MS
1297/*
1298 * '_httpResolveURI()' - Resolve a DNS-SD URI.
1299 */
1300
1301const char * /* O - Resolved URI */
1302_httpResolveURI(
1303 const char *uri, /* I - DNS-SD URI */
1304 char *resolved_uri, /* I - Buffer for resolved URI */
1f0275e3 1305 size_t resolved_size, /* I - Size of URI buffer */
dfd5680b 1306 int logit) /* I - Log progress to stderr? */
5eb9da71
MS
1307{
1308 char scheme[32], /* URI components... */
1309 userpass[256],
1310 hostname[1024],
1311 resource[1024];
1312 int port;
b19ccc9e 1313#ifdef DEBUG
1f0275e3 1314 http_uri_status_t status; /* URI decode status */
b19ccc9e 1315#endif /* DEBUG */
5eb9da71
MS
1316
1317
e07d4801
MS
1318 DEBUG_printf(("4_httpResolveURI(uri=\"%s\", resolved_uri=%p, "
1319 "resolved_size=" CUPS_LLFMT ")", uri, resolved_uri,
1f0275e3
MS
1320 CUPS_LLCAST resolved_size));
1321
5eb9da71
MS
1322 /*
1323 * Get the device URI...
1324 */
1325
b19ccc9e 1326#ifdef DEBUG
1f0275e3
MS
1327 if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1328 sizeof(scheme), userpass, sizeof(userpass),
1329 hostname, sizeof(hostname), &port, resource,
1330 sizeof(resource))) < HTTP_URI_OK)
b19ccc9e
MS
1331#else
1332 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1333 sizeof(scheme), userpass, sizeof(userpass),
1334 hostname, sizeof(hostname), &port, resource,
1335 sizeof(resource)) < HTTP_URI_OK)
1336#endif /* DEBUG */
1f0275e3 1337 {
dfd5680b 1338 if (logit)
4d301e69 1339 _cupsLangPrintf(stderr, _("Bad device URI \"%s\"\n"), uri);
1f0275e3 1340
e07d4801
MS
1341 DEBUG_printf(("6_httpResolveURI: httpSeparateURI returned %d!", status));
1342 DEBUG_puts("5_httpResolveURI: Returning NULL");
5eb9da71 1343 return (NULL);
1f0275e3 1344 }
5eb9da71
MS
1345
1346 /*
1347 * Resolve it as needed...
1348 */
1349
1350 if (strstr(hostname, "._tcp"))
1351 {
1352#ifdef HAVE_DNSSD
38e73f87
MS
1353 DNSServiceRef ref, /* DNS-SD master service reference */
1354 domainref, /* DNS-SD service reference for domain */
1355 localref; /* DNS-SD service reference for .local */
1356 int domainsent = 0; /* Send the domain resolve? */
5eb9da71
MS
1357 char *regtype, /* Pointer to type in hostname */
1358 *domain; /* Pointer to domain in hostname */
1359 _http_uribuf_t uribuf; /* URI buffer */
38e73f87
MS
1360 struct pollfd polldata; /* Polling data */
1361
1362
1363 if (logit)
1364 fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname);
5eb9da71
MS
1365
1366 /*
1367 * Separate the hostname into service name, registration type, and domain...
1368 */
1369
1f0275e3
MS
1370 for (regtype = strstr(hostname, "._tcp") - 2;
1371 regtype > hostname;
1372 regtype --)
1373 if (regtype[0] == '.' && regtype[1] == '_')
1374 {
1375 /*
1376 * Found ._servicetype in front of ._tcp...
1377 */
1378
1379 *regtype++ = '\0';
1380 break;
1381 }
1382
1383 if (regtype <= hostname)
1384 {
e07d4801 1385 DEBUG_puts("5_httpResolveURI: Bad hostname, returning NULL");
1f0275e3
MS
1386 return (NULL);
1387 }
5eb9da71 1388
5eb9da71
MS
1389 for (domain = strchr(regtype, '.');
1390 domain;
1391 domain = strchr(domain + 1, '.'))
1392 if (domain[1] != '_')
1393 break;
1394
1395 if (domain)
1396 *domain++ = '\0';
1397
1398 uribuf.buffer = resolved_uri;
1399 uribuf.bufsize = resolved_size;
1400
1401 resolved_uri[0] = '\0';
1402
e07d4801 1403 DEBUG_printf(("6_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", "
1f0275e3 1404 "domain=\"%s\"\n", hostname, regtype, domain));
dfd5680b 1405 if (logit)
1f0275e3
MS
1406 {
1407 fputs("STATE: +connecting-to-device\n", stderr);
38e73f87
MS
1408 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1409 "domain=\"local.\"...\n", hostname, regtype);
1f0275e3
MS
1410 }
1411
38e73f87
MS
1412 uri = NULL;
1413
1414 if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError)
5eb9da71 1415 {
38e73f87
MS
1416 localref = ref;
1417 if (DNSServiceResolve(&localref, kDNSServiceFlagsShareConnection, 0,
1418 hostname, regtype, "local.", resolve_callback,
1419 &uribuf) == kDNSServiceErr_NoError)
1420 {
acb056cb
MS
1421 int fds; /* Number of ready descriptors */
1422 time_t timeout, /* Poll timeout */
1423 start_time = time(NULL);/* Start time */
1424
1425 for (;;)
38e73f87 1426 {
acb056cb
MS
1427 if (logit)
1428 _cupsLangPuts(stderr, _("INFO: Looking for printer...\n"));
1429
38e73f87 1430 /*
acb056cb
MS
1431 * For the first minute, wakeup every 2 seconds to emit a
1432 * "looking for printer" message...
38e73f87
MS
1433 */
1434
acb056cb
MS
1435 timeout = (time(NULL) < (start_time + 60)) ? 2000 : -1;
1436
38e73f87
MS
1437 polldata.fd = DNSServiceRefSockFD(ref);
1438 polldata.events = POLLIN;
1439
acb056cb
MS
1440 fds = poll(&polldata, 1, timeout);
1441
1442 if (fds < 0)
1443 {
1444 if (errno != EINTR && errno != EAGAIN)
1445 {
1446 DEBUG_printf(("5_httpResolveURI: poll error: %s", strerror(errno)));
1447 break;
1448 }
1449 }
1450 else if (fds == 0)
38e73f87
MS
1451 {
1452 /*
acb056cb
MS
1453 * Wait 2 seconds for a response to the local resolve; if nothing
1454 * comes in, do an additional domain resolution...
38e73f87
MS
1455 */
1456
acb056cb
MS
1457 if (domainsent == 0 && strcasecmp(domain, "local."))
1458 {
1459 if (logit)
1460 fprintf(stderr,
1461 "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1462 "domain=\"%s\"...\n", hostname, regtype, domain);
1463
1464 domainref = ref;
1465 if (DNSServiceResolve(&domainref, kDNSServiceFlagsShareConnection, 0,
1466 hostname, regtype, domain, resolve_callback,
1467 &uribuf) == kDNSServiceErr_NoError)
1468 domainsent = 1;
1469 }
38e73f87 1470 }
acb056cb
MS
1471 else
1472 {
1473 if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError)
1474 {
1475 uri = resolved_uri;
1476 break;
1477 }
1478 }
1479 }
38e73f87
MS
1480
1481 if (domainsent)
1482 DNSServiceRefDeallocate(domainref);
1483
1484 DNSServiceRefDeallocate(localref);
1485 }
5eb9da71
MS
1486
1487 DNSServiceRefDeallocate(ref);
1488 }
1f0275e3 1489
dfd5680b 1490 if (logit)
38e73f87
MS
1491 {
1492 if (uri)
38e73f87 1493 fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri);
f11a948a 1494 else
4d301e69 1495 fputs("DEBUG: Unable to resolve URI\n", stderr);
38e73f87 1496
1f0275e3 1497 fputs("STATE: -connecting-to-device\n", stderr);
38e73f87 1498 }
1f0275e3
MS
1499
1500#else
1501 /*
1502 * No DNS-SD support...
1503 */
5eb9da71
MS
1504
1505 uri = NULL;
1f0275e3
MS
1506#endif /* HAVE_DNSSD */
1507
dfd5680b 1508 if (logit && !uri)
4d301e69 1509 _cupsLangPuts(stderr, _("Unable to find printer\n"));
5eb9da71
MS
1510 }
1511
e07d4801 1512 DEBUG_printf(("5_httpResolveURI: Returning \"%s\"", uri));
1f0275e3 1513
5eb9da71
MS
1514 return (uri);
1515}
1516
1517
ef416fc2 1518/*
1519 * 'http_copy_decode()' - Copy and decode a URI.
1520 */
1521
1522static const char * /* O - New source pointer or NULL on error */
1523http_copy_decode(char *dst, /* O - Destination buffer */
1524 const char *src, /* I - Source pointer */
1525 int dstsize, /* I - Destination size */
a4d04587 1526 const char *term, /* I - Terminating characters */
1527 int decode) /* I - Decode %-encoded values */
ef416fc2 1528{
1529 char *ptr, /* Pointer into buffer */
1530 *end; /* End of buffer */
1531 int quoted; /* Quoted character */
1532
1533
1534 /*
1535 * Copy the src to the destination until we hit a terminating character
1536 * or the end of the string.
1537 */
1538
e00b005a 1539 for (ptr = dst, end = dst + dstsize - 1;
1540 *src && (!term || !strchr(term, *src));
1541 src ++)
ef416fc2 1542 if (ptr < end)
1543 {
a4d04587 1544 if (*src == '%' && decode)
ef416fc2 1545 {
1546 if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255))
1547 {
1548 /*
1549 * Grab a hex-encoded character...
1550 */
1551
1552 src ++;
1553 if (isalpha(*src))
1554 quoted = (tolower(*src) - 'a' + 10) << 4;
1555 else
1556 quoted = (*src - '0') << 4;
1557
1558 src ++;
1559 if (isalpha(*src))
1560 quoted |= tolower(*src) - 'a' + 10;
1561 else
1562 quoted |= *src - '0';
1563
1564 *ptr++ = quoted;
1565 }
1566 else
1567 {
1568 /*
1569 * Bad hex-encoded character...
1570 */
1571
1572 *ptr = '\0';
1573 return (NULL);
1574 }
1575 }
1576 else
1577 *ptr++ = *src;
1578 }
1579
1580 *ptr = '\0';
1581
1582 return (src);
1583}
1584
1585
1586/*
1587 * 'http_copy_encode()' - Copy and encode a URI.
1588 */
1589
1590static char * /* O - End of current URI */
1591http_copy_encode(char *dst, /* O - Destination buffer */
1592 const char *src, /* I - Source pointer */
1593 char *dstend, /* I - End of destination buffer */
a4d04587 1594 const char *reserved, /* I - Extra reserved characters */
1595 const char *term, /* I - Terminating characters */
1596 int encode) /* I - %-encode reserved chars? */
ef416fc2 1597{
ac884b6a 1598 static const char hex[] = "0123456789ABCDEF";
ef416fc2 1599
1600
1601 while (*src && dst < dstend)
1602 {
a4d04587 1603 if (term && *src == *term)
1604 return (dst);
1605
1606 if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
1607 (reserved && strchr(reserved, *src))))
ef416fc2 1608 {
1609 /*
1610 * Hex encode reserved characters...
1611 */
1612
1613 if ((dst + 2) >= dstend)
1614 break;
1615
1616 *dst++ = '%';
1617 *dst++ = hex[(*src >> 4) & 15];
1618 *dst++ = hex[*src & 15];
1619
1620 src ++;
1621 }
1622 else
1623 *dst++ = *src++;
1624 }
1625
839a51c8
MS
1626 *dst = '\0';
1627
ef416fc2 1628 if (*src)
1629 return (NULL);
1630 else
1631 return (dst);
1632}
1633
1634
5eb9da71
MS
1635#ifdef HAVE_DNSSD
1636/*
1637 * 'resolve_callback()' - Build a device URI for the given service name.
1638 */
1639
1640static void
1641resolve_callback(
1642 DNSServiceRef sdRef, /* I - Service reference */
1643 DNSServiceFlags flags, /* I - Results flags */
1644 uint32_t interfaceIndex, /* I - Interface number */
1645 DNSServiceErrorType errorCode, /* I - Error, if any */
1646 const char *fullName, /* I - Full service name */
1647 const char *hostTarget, /* I - Hostname */
1648 uint16_t port, /* I - Port number */
1649 uint16_t txtLen, /* I - Length of TXT record */
1650 const unsigned char *txtRecord, /* I - TXT record data */
1651 void *context) /* I - Pointer to URI buffer */
1652{
1653 const char *scheme; /* URI scheme */
1654 char rp[257]; /* Remote printer */
1655 const void *value; /* Value from TXT record */
1656 uint8_t valueLen; /* Length of value */
1657 _http_uribuf_t *uribuf; /* URI buffer */
1658
1659
e07d4801 1660 DEBUG_printf(("7resolve_callback(sdRef=%p, flags=%x, interfaceIndex=%u, "
5eb9da71 1661 "errorCode=%d, fullName=\"%s\", hostTarget=\"%s\", port=%u, "
e07d4801 1662 "txtLen=%u, txtRecord=%p, context=%p)", sdRef, flags,
5eb9da71
MS
1663 interfaceIndex, errorCode, fullName, hostTarget, port, txtLen,
1664 txtRecord, context));
1665
1666 /*
1667 * Figure out the scheme from the full name...
1668 */
1669
f11a948a 1670 if (strstr(fullName, "._ipp") || strstr(fullName, "._fax-ipp"))
5eb9da71
MS
1671 scheme = "ipp";
1672 else if (strstr(fullName, "._printer."))
1673 scheme = "lpd";
1674 else if (strstr(fullName, "._pdl-datastream."))
1675 scheme = "socket";
1676 else
1677 scheme = "riousbprint";
1678
1679 /*
1680 * Extract the "remote printer" key from the TXT record...
1681 */
1682
1683 if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, "rp",
1684 &valueLen)) != NULL)
1685 {
1686 /*
1687 * Convert to resource by concatenating with a leading "/"...
1688 */
1689
1690 rp[0] = '/';
1691 memcpy(rp + 1, value, valueLen);
1692 rp[valueLen + 1] = '\0';
1693 }
1694 else
1695 rp[0] = '\0';
1696
1697 /*
1698 * Assemble the final device URI...
1699 */
1700
1701 uribuf = (_http_uribuf_t *)context;
1702
1703 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, uribuf->bufsize, scheme,
1704 NULL, hostTarget, ntohs(port), rp);
1705
e07d4801 1706 DEBUG_printf(("8resolve_callback: Resolved URI is \"%s\"...",
5eb9da71
MS
1707 uribuf->buffer));
1708}
1709#endif /* HAVE_DNSSD */
1710
1711
ef416fc2 1712/*
b19ccc9e 1713 * End of "$Id: http-support.c 7952 2008-09-17 00:56:20Z mike $".
ef416fc2 1714 */