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