]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/http-support.c
Merge changes from CUPS 1.5svn-r8849.
[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 the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 2007-2009 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 "debug.h"
51 #include "globals.h"
52 #include <stdlib.h>
53 #include <errno.h>
54 #ifdef HAVE_DNSSD
55 # include <dns_sd.h>
56 # include <poll.h>
57 #endif /* HAVE_DNSSD */
58
59
60 /*
61 * Local types...
62 */
63
64 typedef struct _http_uribuf_s /* URI buffer */
65 {
66 char *buffer; /* Pointer to buffer */
67 size_t bufsize; /* Size of buffer */
68 } _http_uribuf_t;
69
70
71 /*
72 * Local globals...
73 */
74
75 static const char * const http_days[7] =
76 {
77 "Sun",
78 "Mon",
79 "Tue",
80 "Wed",
81 "Thu",
82 "Fri",
83 "Sat"
84 };
85 static 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
106 static const char *http_copy_decode(char *dst, const char *src,
107 int dstsize, const char *term,
108 int decode);
109 static char *http_copy_encode(char *dst, const char *src,
110 char *dstend, const char *reserved,
111 const char *term, int encode);
112 #ifdef HAVE_DNSSD
113 static 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 */
123
124
125 /*
126 * 'httpAssembleURI()' - Assemble a uniform resource identifier from its
127 * components.
128 *
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.
133 *
134 * @since CUPS 1.2/Mac OS X 10.5@
135 */
136
137 http_uri_status_t /* O - URI status */
138 httpAssembleURI(
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 */
147 {
148 char *ptr, /* Pointer into URI buffer */
149 *end; /* End of URI buffer */
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;
169 ptr = http_copy_encode(uri, scheme, end, NULL, NULL, 0);
170
171 if (!ptr)
172 goto assemble_overflow;
173
174 if (!strcmp(scheme, "mailto"))
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
213 ptr = http_copy_encode(ptr, username, end, "/?@", NULL,
214 encoding & HTTP_URI_CODING_USERNAME);
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
227 * with, we have several special cases to deal with. If we get
228 * an IPv6 address with brackets around it, assume it is already in
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...
232 */
233
234 if (host[0] != '[' && strchr(host, ':') && !strstr(host, "._tcp"))
235 {
236 /*
237 * We have a raw IPv6 address...
238 */
239
240 if (strchr(host, '%'))
241 {
242 /*
243 * We have a link-local address, add "[v1." prefix...
244 */
245
246 if ((ptr + 4) < end)
247 {
248 *ptr++ = '[';
249 *ptr++ = 'v';
250 *ptr++ = '1';
251 *ptr++ = '.';
252 }
253 else
254 goto assemble_overflow;
255 }
256 else
257 {
258 /*
259 * We have a normal address, add "[" prefix...
260 */
261
262 if (ptr < end)
263 *ptr++ = '[';
264 else
265 goto assemble_overflow;
266 }
267
268 /*
269 * Copy the rest of the IPv6 address, and terminate with "]".
270 */
271
272 while (ptr < end && *host)
273 {
274 if (*host == '%')
275 {
276 *ptr++ = '+'; /* Convert zone separator */
277 host ++;
278 }
279 else
280 *ptr++ = *host++;
281 }
282
283 if (*host)
284 goto assemble_overflow;
285
286 if (ptr < end)
287 *ptr++ = ']';
288 else
289 goto assemble_overflow;
290 }
291 else
292 {
293 /*
294 * Otherwise, just copy the host string...
295 */
296
297 ptr = http_copy_encode(ptr, host, end, ":/?#[]@\\", NULL,
298 encoding & HTTP_URI_CODING_HOSTNAME);
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
322 if (resource)
323 {
324 char *query; /* Pointer to query string */
325
326
327 /*
328 * Copy the resource string up to the query string if present...
329 */
330
331 query = strchr(resource, '?');
332 ptr = http_copy_encode(ptr, resource, end, NULL, "?",
333 encoding & HTTP_URI_CODING_RESOURCE);
334 if (!ptr)
335 goto assemble_overflow;
336
337 if (query)
338 {
339 /*
340 * Copy query string without encoding...
341 */
342
343 ptr = http_copy_encode(ptr, query, end, NULL, NULL,
344 encoding & HTTP_URI_CODING_QUERY);
345 if (!ptr)
346 goto assemble_overflow;
347 }
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
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
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.
383 *
384 * @since CUPS 1.2/Mac OS X 10.5@
385 */
386
387 http_uri_status_t /* O - URI status */
388 httpAssembleURIf(
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
435 /*
436 * 'httpDecode64()' - Base64-decode a string.
437 *
438 * This function is deprecated. Use the httpDecode64_2() function instead
439 * which provides buffer length arguments.
440 *
441 * @deprecated@
442 */
443
444 char * /* O - Decoded string */
445 httpDecode64(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 *
464 * @since CUPS 1.1.21/Mac OS X 10.4@
465 */
466
467 char * /* O - Decoded string */
468 httpDecode64_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
482 if (!out || !outlen || *outlen < 1 || !in)
483 return (NULL);
484
485 if (!*in)
486 {
487 *out = '\0';
488 *outlen = 0;
489
490 return (out);
491 }
492
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.
565 *
566 * This function is deprecated. Use the httpEncode64_2() function instead
567 * which provides buffer length arguments.
568 *
569 * @deprecated@
570 */
571
572 char * /* O - Encoded string */
573 httpEncode64(char *out, /* I - String to write to */
574 const char *in) /* I - String to read from */
575 {
576 return (httpEncode64_2(out, 512, in, (int)strlen(in)));
577 }
578
579
580 /*
581 * 'httpEncode64_2()' - Base64-encode a string.
582 *
583 * @since CUPS 1.1.21/Mac OS X 10.4@
584 */
585
586 char * /* O - Encoded string */
587 httpEncode64_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];
622
623 if (outptr < outend)
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 }
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)
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 }
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
679 const char * /* O - Date/time string */
680 httpGetDateString(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 *
692 * @since CUPS 1.2/Mac OS X 10.5@
693 */
694
695 const char * /* O - Date/time string */
696 httpGetDateString2(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
717 time_t /* O - UNIX time */
718 httpGetDateTime(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
731 DEBUG_printf(("2httpGetDateTime(s=\"%s\")", s));
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
740 DEBUG_printf(("4httpGetDateTime: day=%d, mon=\"%s\", year=%d, hour=%d, "
741 "min=%d, sec=%d", day, mon, year, hour, min, sec));
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
754 DEBUG_printf(("4httpGetDateTime: i=%d", i));
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
767 DEBUG_printf(("4httpGetDateTime: days=%d", days));
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
774 DEBUG_printf(("4httpGetDateTime: days=%d\n", days));
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.
783 *
784 * This function is deprecated; use the httpSeparateURI() function instead.
785 *
786 * @deprecated@
787 */
788
789 void
790 httpSeparate(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 {
797 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username,
798 HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource,
799 HTTP_MAX_URI);
800 }
801
802
803 /*
804 * 'httpSeparate2()' - Separate a Universal Resource Identifier into its
805 * components.
806 *
807 * This function is deprecated; use the httpSeparateURI() function instead.
808 *
809 * @since CUPS 1.1.21/Mac OS X 10.4@
810 * @deprecated@
811 */
812
813 void
814 httpSeparate2(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 {
825 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username,
826 usernamelen, host, hostlen, port, resource, resourcelen);
827 }
828
829
830 /*
831 * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its
832 * components.
833 *
834 * @since CUPS 1.2/Mac OS X 10.5@
835 */
836
837 http_uri_status_t /* O - Result of separation */
838 httpSeparateURI(
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 */
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
975 uri = http_copy_decode(username, uri, usernamelen, "@",
976 decoding & HTTP_URI_CODING_USERNAME);
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
1001 uri = http_copy_decode(host, uri, hostlen, "]",
1002 decoding & HTTP_URI_CODING_HOSTNAME);
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 /*
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 "%"
1052 "!$&'()*+,;=\\", *ptr))
1053 {
1054 *host = '\0';
1055 return (HTTP_URI_BAD_HOSTNAME);
1056 }
1057
1058 /*
1059 * Then copy the hostname or IPv4 address to the buffer...
1060 */
1061
1062 uri = http_copy_decode(host, uri, hostlen, ":?/",
1063 decoding & HTTP_URI_CODING_HOSTNAME);
1064
1065 if (!uri)
1066 {
1067 *host = '\0';
1068 return (HTTP_URI_BAD_HOSTNAME);
1069 }
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
1093 if (!isdigit(uri[1] & 255))
1094 {
1095 *port = 0;
1096 return (HTTP_URI_BAD_PORT);
1097 }
1098
1099 *port = strtol(uri + 1, (char **)&uri, 10);
1100
1101 if (*uri != '/' && *uri)
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 = '/';
1121
1122 /*
1123 * Copy any query string...
1124 */
1125
1126 if (*uri == '?')
1127 uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL,
1128 decoding & HTTP_URI_CODING_QUERY);
1129 else
1130 resource[1] = '\0';
1131 }
1132 else
1133 {
1134 uri = http_copy_decode(resource, uri, resourcelen, "?",
1135 decoding & HTTP_URI_CODING_RESOURCE);
1136
1137 if (uri && *uri == '?')
1138 {
1139 /*
1140 * Concatenate any query string...
1141 */
1142
1143 char *resptr = resource + strlen(resource);
1144
1145 uri = http_copy_decode(resptr, uri, resourcelen - (int)(resptr - resource),
1146 NULL, decoding & HTTP_URI_CODING_QUERY);
1147 }
1148 }
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.
1166 *
1167 * The returned string is localized to the current POSIX locale and is based
1168 * on the status strings defined in RFC 2616.
1169 */
1170
1171 const char * /* O - Localized status string */
1172 httpStatus(http_status_t status) /* I - HTTP status code */
1173 {
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
1181 switch (status)
1182 {
1183 case HTTP_CONTINUE :
1184 s = _("Continue");
1185 break;
1186 case HTTP_SWITCHING_PROTOCOLS :
1187 s = _("Switching Protocols");
1188 break;
1189 case HTTP_OK :
1190 s = _("OK");
1191 break;
1192 case HTTP_CREATED :
1193 s = _("Created");
1194 break;
1195 case HTTP_ACCEPTED :
1196 s = _("Accepted");
1197 break;
1198 case HTTP_NO_CONTENT :
1199 s = _("No Content");
1200 break;
1201 case HTTP_MOVED_PERMANENTLY :
1202 s = _("Moved Permanently");
1203 break;
1204 case HTTP_SEE_OTHER :
1205 s = _("See Other");
1206 break;
1207 case HTTP_NOT_MODIFIED :
1208 s = _("Not Modified");
1209 break;
1210 case HTTP_BAD_REQUEST :
1211 s = _("Bad Request");
1212 break;
1213 case HTTP_UNAUTHORIZED :
1214 case HTTP_AUTHORIZATION_CANCELED :
1215 s = _("Unauthorized");
1216 break;
1217 case HTTP_FORBIDDEN :
1218 s = _("Forbidden");
1219 break;
1220 case HTTP_NOT_FOUND :
1221 s = _("Not Found");
1222 break;
1223 case HTTP_REQUEST_TOO_LARGE :
1224 s = _("Request Entity Too Large");
1225 break;
1226 case HTTP_URI_TOO_LONG :
1227 s = _("URI Too Long");
1228 break;
1229 case HTTP_UPGRADE_REQUIRED :
1230 s = _("Upgrade Required");
1231 break;
1232 case HTTP_NOT_IMPLEMENTED :
1233 s = _("Not Implemented");
1234 break;
1235 case HTTP_NOT_SUPPORTED :
1236 s = _("Not Supported");
1237 break;
1238 case HTTP_EXPECTATION_FAILED :
1239 s = _("Expectation Failed");
1240 break;
1241 case HTTP_SERVICE_UNAVAILABLE :
1242 s = _("Service Unavailable");
1243 break;
1244 case HTTP_SERVER_ERROR :
1245 s = _("Internal Server Error");
1246 break;
1247
1248 default :
1249 s = _("Unknown");
1250 break;
1251 }
1252
1253 return (_cupsLangString(cg->lang_default, s));
1254 }
1255
1256
1257 #ifndef HAVE_HSTRERROR
1258 /*
1259 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others...
1260 */
1261
1262 const 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
1283 /*
1284 * '_httpEncodeURI()' - Percent-encode a HTTP request URI.
1285 */
1286
1287 char * /* 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
1297 /*
1298 * '_httpResolveURI()' - Resolve a DNS-SD URI.
1299 */
1300
1301 const char * /* O - Resolved URI */
1302 _httpResolveURI(
1303 const char *uri, /* I - DNS-SD URI */
1304 char *resolved_uri, /* I - Buffer for resolved URI */
1305 size_t resolved_size, /* I - Size of URI buffer */
1306 int logit) /* I - Log progress to stderr? */
1307 {
1308 char scheme[32], /* URI components... */
1309 userpass[256],
1310 hostname[1024],
1311 resource[1024];
1312 int port;
1313 #ifdef DEBUG
1314 http_uri_status_t status; /* URI decode status */
1315 #endif /* DEBUG */
1316
1317
1318 DEBUG_printf(("4_httpResolveURI(uri=\"%s\", resolved_uri=%p, "
1319 "resolved_size=" CUPS_LLFMT ")", uri, resolved_uri,
1320 CUPS_LLCAST resolved_size));
1321
1322 /*
1323 * Get the device URI...
1324 */
1325
1326 #ifdef DEBUG
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)
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 */
1337 {
1338 if (logit)
1339 _cupsLangPrintf(stderr, _("Bad device URI \"%s\"\n"), uri);
1340
1341 DEBUG_printf(("6_httpResolveURI: httpSeparateURI returned %d!", status));
1342 DEBUG_puts("5_httpResolveURI: Returning NULL");
1343 return (NULL);
1344 }
1345
1346 /*
1347 * Resolve it as needed...
1348 */
1349
1350 if (strstr(hostname, "._tcp"))
1351 {
1352 #ifdef HAVE_DNSSD
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? */
1357 char *regtype, /* Pointer to type in hostname */
1358 *domain; /* Pointer to domain in hostname */
1359 _http_uribuf_t uribuf; /* URI buffer */
1360 struct pollfd polldata; /* Polling data */
1361
1362
1363 if (logit)
1364 fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname);
1365
1366 /*
1367 * Separate the hostname into service name, registration type, and domain...
1368 */
1369
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 {
1385 DEBUG_puts("5_httpResolveURI: Bad hostname, returning NULL");
1386 return (NULL);
1387 }
1388
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
1403 DEBUG_printf(("6_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", "
1404 "domain=\"%s\"\n", hostname, regtype, domain));
1405 if (logit)
1406 {
1407 fputs("STATE: +connecting-to-device\n", stderr);
1408 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1409 "domain=\"local.\"...\n", hostname, regtype);
1410 }
1411
1412 uri = NULL;
1413
1414 if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError)
1415 {
1416 localref = ref;
1417 if (DNSServiceResolve(&localref, kDNSServiceFlagsShareConnection, 0,
1418 hostname, regtype, "local.", resolve_callback,
1419 &uribuf) == kDNSServiceErr_NoError)
1420 {
1421 int fds; /* Number of ready descriptors */
1422 time_t timeout, /* Poll timeout */
1423 start_time = time(NULL);/* Start time */
1424
1425 for (;;)
1426 {
1427 if (logit)
1428 _cupsLangPuts(stderr, _("INFO: Looking for printer...\n"));
1429
1430 /*
1431 * For the first minute, wakeup every 2 seconds to emit a
1432 * "looking for printer" message...
1433 */
1434
1435 timeout = (time(NULL) < (start_time + 60)) ? 2000 : -1;
1436
1437 polldata.fd = DNSServiceRefSockFD(ref);
1438 polldata.events = POLLIN;
1439
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)
1451 {
1452 /*
1453 * Wait 2 seconds for a response to the local resolve; if nothing
1454 * comes in, do an additional domain resolution...
1455 */
1456
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 }
1470 }
1471 else
1472 {
1473 if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError)
1474 {
1475 uri = resolved_uri;
1476 break;
1477 }
1478 }
1479 }
1480
1481 if (domainsent)
1482 DNSServiceRefDeallocate(domainref);
1483
1484 DNSServiceRefDeallocate(localref);
1485 }
1486
1487 DNSServiceRefDeallocate(ref);
1488 }
1489
1490 if (logit)
1491 {
1492 if (uri)
1493 fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri);
1494 else
1495 fputs("DEBUG: Unable to resolve URI\n", stderr);
1496
1497 fputs("STATE: -connecting-to-device\n", stderr);
1498 }
1499
1500 #else
1501 /*
1502 * No DNS-SD support...
1503 */
1504
1505 uri = NULL;
1506 #endif /* HAVE_DNSSD */
1507
1508 if (logit && !uri)
1509 _cupsLangPuts(stderr, _("Unable to find printer\n"));
1510 }
1511
1512 DEBUG_printf(("5_httpResolveURI: Returning \"%s\"", uri));
1513
1514 return (uri);
1515 }
1516
1517
1518 /*
1519 * 'http_copy_decode()' - Copy and decode a URI.
1520 */
1521
1522 static const char * /* O - New source pointer or NULL on error */
1523 http_copy_decode(char *dst, /* O - Destination buffer */
1524 const char *src, /* I - Source pointer */
1525 int dstsize, /* I - Destination size */
1526 const char *term, /* I - Terminating characters */
1527 int decode) /* I - Decode %-encoded values */
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
1539 for (ptr = dst, end = dst + dstsize - 1;
1540 *src && (!term || !strchr(term, *src));
1541 src ++)
1542 if (ptr < end)
1543 {
1544 if (*src == '%' && decode)
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
1590 static char * /* O - End of current URI */
1591 http_copy_encode(char *dst, /* O - Destination buffer */
1592 const char *src, /* I - Source pointer */
1593 char *dstend, /* I - End of destination buffer */
1594 const char *reserved, /* I - Extra reserved characters */
1595 const char *term, /* I - Terminating characters */
1596 int encode) /* I - %-encode reserved chars? */
1597 {
1598 static const char hex[] = "0123456789ABCDEF";
1599
1600
1601 while (*src && dst < dstend)
1602 {
1603 if (term && *src == *term)
1604 return (dst);
1605
1606 if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
1607 (reserved && strchr(reserved, *src))))
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
1626 *dst = '\0';
1627
1628 if (*src)
1629 return (NULL);
1630 else
1631 return (dst);
1632 }
1633
1634
1635 #ifdef HAVE_DNSSD
1636 /*
1637 * 'resolve_callback()' - Build a device URI for the given service name.
1638 */
1639
1640 static void
1641 resolve_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
1660 DEBUG_printf(("7resolve_callback(sdRef=%p, flags=%x, interfaceIndex=%u, "
1661 "errorCode=%d, fullName=\"%s\", hostTarget=\"%s\", port=%u, "
1662 "txtLen=%u, txtRecord=%p, context=%p)", sdRef, flags,
1663 interfaceIndex, errorCode, fullName, hostTarget, port, txtLen,
1664 txtRecord, context));
1665
1666 /*
1667 * Figure out the scheme from the full name...
1668 */
1669
1670 if (strstr(fullName, "._ipp") || strstr(fullName, "._fax-ipp"))
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
1706 DEBUG_printf(("8resolve_callback: Resolved URI is \"%s\"...",
1707 uribuf->buffer));
1708 }
1709 #endif /* HAVE_DNSSD */
1710
1711
1712 /*
1713 * End of "$Id: http-support.c 7952 2008-09-17 00:56:20Z mike $".
1714 */