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