]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/http-support.c
Merge changes from CUPS 1.7svn-r10861.
[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 *
0fa6c7fa 6 * Copyright 2007-2013 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 *
f3c17241
MS
19 * httpAssembleURI() - Assemble a uniform resource identifier from its
20 * components.
ef416fc2 21 * httpAssembleURIf() - Assemble a uniform resource identifier from its
f3c17241 22 * components with a formatted resource.
0fa6c7fa
MS
23 * httpAssembleUUID() - Assemble a name-based UUID URN conforming to RFC
24 * 4122.
f3c17241
MS
25 * httpDecode64() - Base64-decode a string.
26 * httpDecode64_2() - Base64-decode a string.
27 * httpEncode64() - Base64-encode a string.
28 * httpEncode64_2() - Base64-encode a string.
ef416fc2 29 * httpGetDateString() - Get a formatted date/time string from a time value.
30 * httpGetDateString2() - Get a formatted date/time string from a time value.
f3c17241
MS
31 * httpGetDateTime() - Get a time value from a formatted date/time string.
32 * httpSeparate() - Separate a Universal Resource Identifier into its
33 * components.
34 * httpSeparate2() - Separate a Universal Resource Identifier into its
35 * components.
36 * httpSeparateURI() - Separate a Universal Resource Identifier into its
37 * components.
38 * httpStatus() - Return a short string describing a HTTP status
39 * code.
40 * _cups_hstrerror() - hstrerror() emulation function for Solaris and
41 * others.
42 * _httpDecodeURI() - Percent-decode a HTTP request URI.
43 * _httpEncodeURI() - Percent-encode a HTTP request URI.
44 * _httpResolveURI() - Resolve a DNS-SD URI.
45 * http_client_cb() - Client callback for resolving URI.
ef416fc2 46 * http_copy_decode() - Copy and decode a URI.
47 * http_copy_encode() - Copy and encode a URI.
f3c17241
MS
48 * http_poll_cb() - Wait for input on the specified file descriptors.
49 * http_resolve_cb() - Build a device URI for the given service name.
50 * http_resolve_cb() - Build a device URI for the given service name.
ef416fc2 51 */
52
53/*
54 * Include necessary headers...
55 */
56
71e16022 57#include "cups-private.h"
5eb9da71
MS
58#ifdef HAVE_DNSSD
59# include <dns_sd.h>
6d2f911b
MS
60# ifdef WIN32
61# include <io.h>
62# elif defined(HAVE_POLL)
63# include <poll.h>
64# else
65# include <sys/select.h>
66# endif /* WIN32 */
f3c17241
MS
67#elif defined(HAVE_AVAHI)
68# include <avahi-client/client.h>
69# include <avahi-client/lookup.h>
70# include <avahi-common/simple-watch.h>
5eb9da71
MS
71#endif /* HAVE_DNSSD */
72
73
74/*
75 * Local types...
76 */
77
78typedef struct _http_uribuf_s /* URI buffer */
79{
f3c17241
MS
80#ifdef HAVE_AVAHI
81 AvahiSimplePoll *poll; /* Poll state */
82#endif /* HAVE_AVAHI */
83 char *buffer; /* Pointer to buffer */
84 size_t bufsize; /* Size of buffer */
85 int options; /* Options passed to _httpResolveURI */
5a9febac 86 const char *resource; /* Resource from URI */
5eb9da71 87} _http_uribuf_t;
ef416fc2 88
89
90/*
91 * Local globals...
92 */
93
94static const char * const http_days[7] =
95 {
0a682745
MS
96 "Sun",
97 "Mon",
ef416fc2 98 "Tue",
99 "Wed",
100 "Thu",
101 "Fri",
102 "Sat"
103 };
104static const char * const http_months[12] =
105 {
106 "Jan",
107 "Feb",
108 "Mar",
109 "Apr",
110 "May",
111 "Jun",
112 "Jul",
113 "Aug",
114 "Sep",
115 "Oct",
116 "Nov",
117 "Dec"
118 };
119
120
121/*
122 * Local functions...
123 */
124
125static const char *http_copy_decode(char *dst, const char *src,
a4d04587 126 int dstsize, const char *term,
127 int decode);
ef416fc2 128static char *http_copy_encode(char *dst, const char *src,
a4d04587 129 char *dstend, const char *reserved,
130 const char *term, int encode);
5eb9da71 131#ifdef HAVE_DNSSD
eac3a0a0
MS
132static void DNSSD_API http_resolve_cb(DNSServiceRef sdRef,
133 DNSServiceFlags flags,
134 uint32_t interfaceIndex,
135 DNSServiceErrorType errorCode,
136 const char *fullName,
137 const char *hostTarget,
138 uint16_t port, uint16_t txtLen,
139 const unsigned char *txtRecord,
140 void *context);
5eb9da71 141#endif /* HAVE_DNSSD */
ef416fc2 142
f3c17241
MS
143#ifdef HAVE_AVAHI
144static void http_client_cb(AvahiClient *client,
145 AvahiClientState state, void *simple_poll);
146static int http_poll_cb(struct pollfd *pollfds, unsigned int num_pollfds,
147 int timeout, void *context);
148static void http_resolve_cb(AvahiServiceResolver *resolver,
149 AvahiIfIndex interface,
150 AvahiProtocol protocol,
151 AvahiResolverEvent event,
152 const char *name, const char *type,
153 const char *domain, const char *host_name,
154 const AvahiAddress *address, uint16_t port,
155 AvahiStringList *txt,
156 AvahiLookupResultFlags flags, void *context);
157#endif /* HAVE_AVAHI */
158
ef416fc2 159
160/*
161 * 'httpAssembleURI()' - Assemble a uniform resource identifier from its
162 * components.
163 *
ecdc0628 164 * This function escapes reserved characters in the URI depending on the
165 * value of the "encoding" argument. You should use this function in
166 * place of traditional string functions whenever you need to create a
167 * URI string.
ef416fc2 168 *
f3c17241 169 * @since CUPS 1.2/OS X 10.5@
ef416fc2 170 */
171
172http_uri_status_t /* O - URI status */
a4d04587 173httpAssembleURI(
174 http_uri_coding_t encoding, /* I - Encoding flags */
175 char *uri, /* I - URI buffer */
176 int urilen, /* I - Size of URI buffer */
177 const char *scheme, /* I - Scheme name */
178 const char *username, /* I - Username */
179 const char *host, /* I - Hostname or address */
180 int port, /* I - Port number */
181 const char *resource) /* I - Resource */
ef416fc2 182{
183 char *ptr, /* Pointer into URI buffer */
184 *end; /* End of URI buffer */
ef416fc2 185
186
187 /*
188 * Range check input...
189 */
190
191 if (!uri || urilen < 1 || !scheme || port < 0)
192 {
193 if (uri)
194 *uri = '\0';
195
196 return (HTTP_URI_BAD_ARGUMENTS);
197 }
198
199 /*
200 * Assemble the URI starting with the scheme...
201 */
202
203 end = uri + urilen - 1;
a4d04587 204 ptr = http_copy_encode(uri, scheme, end, NULL, NULL, 0);
ef416fc2 205
206 if (!ptr)
207 goto assemble_overflow;
208
f7deaa1a 209 if (!strcmp(scheme, "mailto"))
ef416fc2 210 {
211 /*
212 * mailto: only has :, no //...
213 */
214
215 if (ptr < end)
216 *ptr++ = ':';
217 else
218 goto assemble_overflow;
219 }
220 else
221 {
222 /*
223 * Schemes other than mailto: all have //...
224 */
225
226 if ((ptr + 2) < end)
227 {
228 *ptr++ = ':';
229 *ptr++ = '/';
230 *ptr++ = '/';
231 }
232 else
233 goto assemble_overflow;
234 }
235
236 /*
237 * Next the username and hostname, if any...
238 */
239
240 if (host)
241 {
242 if (username && *username)
243 {
244 /*
245 * Add username@ first...
246 */
247
85dda01c 248 ptr = http_copy_encode(ptr, username, end, "/?#[]@", NULL,
a4d04587 249 encoding & HTTP_URI_CODING_USERNAME);
ef416fc2 250
251 if (!ptr)
252 goto assemble_overflow;
253
254 if (ptr < end)
255 *ptr++ = '@';
256 else
257 goto assemble_overflow;
258 }
259
260 /*
261 * Then add the hostname. Since IPv6 is a particular pain to deal
c9fc04c6 262 * with, we have several special cases to deal with. If we get
ef416fc2 263 * an IPv6 address with brackets around it, assume it is already in
c9fc04c6
MS
264 * URI format. Since DNS-SD service names can sometimes look like
265 * raw IPv6 addresses, we specifically look for "._tcp" in the name,
266 * too...
ef416fc2 267 */
268
c9fc04c6 269 if (host[0] != '[' && strchr(host, ':') && !strstr(host, "._tcp"))
ef416fc2 270 {
271 /*
c9fc04c6 272 * We have a raw IPv6 address...
ef416fc2 273 */
274
275 if (strchr(host, '%'))
276 {
277 /*
278 * We have a link-local address, add "[v1." prefix...
279 */
280
281 if ((ptr + 4) < end)
282 {
283 *ptr++ = '[';
284 *ptr++ = 'v';
285 *ptr++ = '1';
286 *ptr++ = '.';
287 }
288 else
289 goto assemble_overflow;
290 }
291 else
292 {
293 /*
294 * We have a normal address, add "[" prefix...
295 */
296
297 if (ptr < end)
298 *ptr++ = '[';
299 else
300 goto assemble_overflow;
301 }
302
303 /*
304 * Copy the rest of the IPv6 address, and terminate with "]".
305 */
306
307 while (ptr < end && *host)
308 {
309 if (*host == '%')
310 {
311 *ptr++ = '+'; /* Convert zone separator */
312 host ++;
313 }
314 else
315 *ptr++ = *host++;
316 }
317
318 if (*host)
319 goto assemble_overflow;
320
321 if (ptr < end)
322 *ptr++ = ']';
323 else
324 goto assemble_overflow;
325 }
326 else
327 {
328 /*
329 * Otherwise, just copy the host string...
330 */
c1420c87 331 ptr = http_copy_encode(ptr, host, end, "<>{}|^:/?#[]@\\\"", NULL,
a4d04587 332 encoding & HTTP_URI_CODING_HOSTNAME);
ef416fc2 333
334 if (!ptr)
335 goto assemble_overflow;
336 }
337
338 /*
339 * Finish things off with the port number...
340 */
341
342 if (port > 0)
343 {
344 snprintf(ptr, end - ptr + 1, ":%d", port);
345 ptr += strlen(ptr);
346
347 if (ptr >= end)
348 goto assemble_overflow;
349 }
350 }
351
352 /*
353 * Last but not least, add the resource string...
354 */
355
a4d04587 356 if (resource)
ef416fc2 357 {
fa73b229 358 char *query; /* Pointer to query string */
359
360
fa73b229 361 /*
a4d04587 362 * Copy the resource string up to the query string if present...
fa73b229 363 */
364
a4d04587 365 query = strchr(resource, '?');
366 ptr = http_copy_encode(ptr, resource, end, NULL, "?",
367 encoding & HTTP_URI_CODING_RESOURCE);
ef416fc2 368 if (!ptr)
369 goto assemble_overflow;
fa73b229 370
371 if (query)
372 {
373 /*
374 * Copy query string without encoding...
375 */
376
a4d04587 377 ptr = http_copy_encode(ptr, query, end, NULL, NULL,
378 encoding & HTTP_URI_CODING_QUERY);
379 if (!ptr)
380 goto assemble_overflow;
fa73b229 381 }
ef416fc2 382 }
383 else if (ptr < end)
384 *ptr++ = '/';
385 else
386 goto assemble_overflow;
387
388 /*
389 * Nul-terminate the URI buffer and return with no errors...
390 */
391
392 *ptr = '\0';
393
394 return (HTTP_URI_OK);
395
396 /*
397 * Clear the URI string and return an overflow error; I don't usually
398 * like goto's, but in this case it makes sense...
399 */
400
401 assemble_overflow:
402
403 *uri = '\0';
404 return (HTTP_URI_OVERFLOW);
405}
406
407
a4d04587 408/*
409 * 'httpAssembleURIf()' - Assemble a uniform resource identifier from its
410 * components with a formatted resource.
411 *
412 * This function creates a formatted version of the resource string
ecdc0628 413 * argument "resourcef" and escapes reserved characters in the URI
414 * depending on the value of the "encoding" argument. You should use
415 * this function in place of traditional string functions whenever
416 * you need to create a URI string.
a4d04587 417 *
f3c17241 418 * @since CUPS 1.2/OS X 10.5@
a4d04587 419 */
420
421http_uri_status_t /* O - URI status */
422httpAssembleURIf(
423 http_uri_coding_t encoding, /* I - Encoding flags */
424 char *uri, /* I - URI buffer */
425 int urilen, /* I - Size of URI buffer */
426 const char *scheme, /* I - Scheme name */
427 const char *username, /* I - Username */
428 const char *host, /* I - Hostname or address */
429 int port, /* I - Port number */
430 const char *resourcef, /* I - Printf-style resource */
431 ...) /* I - Additional arguments as needed */
432{
433 va_list ap; /* Pointer to additional arguments */
434 char resource[1024]; /* Formatted resource string */
435 int bytes; /* Bytes in formatted string */
436
437
438 /*
439 * Range check input...
440 */
441
442 if (!uri || urilen < 1 || !scheme || port < 0 || !resourcef)
443 {
444 if (uri)
445 *uri = '\0';
446
447 return (HTTP_URI_BAD_ARGUMENTS);
448 }
449
450 /*
451 * Format the resource string and assemble the URI...
452 */
453
454 va_start(ap, resourcef);
455 bytes = vsnprintf(resource, sizeof(resource), resourcef, ap);
456 va_end(ap);
457
458 if (bytes >= sizeof(resource))
459 {
460 *uri = '\0';
461 return (HTTP_URI_OVERFLOW);
462 }
463 else
464 return (httpAssembleURI(encoding, uri, urilen, scheme, username, host,
465 port, resource));
466}
467
468
07ed0e9a 469/*
0fa6c7fa
MS
470 * 'httpAssembleUUID()' - Assemble a name-based UUID URN conforming to RFC 4122.
471 *
472 * This function creates a unique 128-bit identifying number using the server
473 * name, port number, random data, and optionally an object name and/or object
474 * number. The result is formatted as a UUID URN as defined in RFC 4122.
07ed0e9a
MS
475 *
476 * The buffer needs to be at least 46 bytes in size.
477 */
478
479char * /* I - UUID string */
0fa6c7fa
MS
480httpAssembleUUID(const char *server, /* I - Server name */
481 int port, /* I - Port number */
482 const char *name, /* I - Object name or NULL */
483 int number, /* I - Object number or 0 */
484 char *buffer, /* I - String buffer */
485 size_t bufsize) /* I - Size of buffer */
07ed0e9a
MS
486{
487 char data[1024]; /* Source string for MD5 */
488 _cups_md5_state_t md5state; /* MD5 state */
489 unsigned char md5sum[16]; /* MD5 digest/sum */
490
491
492 /*
493 * Build a version 3 UUID conforming to RFC 4122.
494 *
495 * Start with the MD5 sum of the server, port, object name and
496 * number, and some random data on the end.
497 */
498
499 snprintf(data, sizeof(data), "%s:%d:%s:%d:%04x:%04x", server,
500 port, name ? name : server, number,
f99f3698 501 (unsigned)CUPS_RAND() & 0xffff, (unsigned)CUPS_RAND() & 0xffff);
07ed0e9a
MS
502
503 _cupsMD5Init(&md5state);
504 _cupsMD5Append(&md5state, (unsigned char *)data, strlen(data));
505 _cupsMD5Finish(&md5state, md5sum);
506
507 /*
508 * Generate the UUID from the MD5...
509 */
510
511 snprintf(buffer, bufsize,
512 "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
513 "%02x%02x%02x%02x%02x%02x",
514 md5sum[0], md5sum[1], md5sum[2], md5sum[3], md5sum[4], md5sum[5],
515 (md5sum[6] & 15) | 0x30, md5sum[7], (md5sum[8] & 0x3f) | 0x40,
516 md5sum[9], md5sum[10], md5sum[11], md5sum[12], md5sum[13],
517 md5sum[14], md5sum[15]);
518
519 return (buffer);
520}
521
0fa6c7fa
MS
522/* For OS X 10.8 and earlier */
523char *_httpAssembleUUID(const char *server, int port, const char *name,
524 int number, char *buffer, size_t bufsize)
525{
526 return (httpAssembleUUID(server, port, name, number, buffer, bufsize));
527}
528
07ed0e9a 529
ef416fc2 530/*
531 * 'httpDecode64()' - Base64-decode a string.
ecdc0628 532 *
533 * This function is deprecated. Use the httpDecode64_2() function instead
534 * which provides buffer length arguments.
535 *
536 * @deprecated@
ef416fc2 537 */
538
539char * /* O - Decoded string */
540httpDecode64(char *out, /* I - String to write to */
541 const char *in) /* I - String to read from */
542{
543 int outlen; /* Output buffer length */
544
545
546 /*
547 * Use the old maximum buffer size for binary compatibility...
548 */
549
550 outlen = 512;
551
552 return (httpDecode64_2(out, &outlen, in));
553}
554
555
556/*
557 * 'httpDecode64_2()' - Base64-decode a string.
558 *
f3c17241 559 * @since CUPS 1.1.21/OS X 10.4@
ef416fc2 560 */
561
562char * /* O - Decoded string */
563httpDecode64_2(char *out, /* I - String to write to */
564 int *outlen, /* IO - Size of output string */
565 const char *in) /* I - String to read from */
566{
567 int pos, /* Bit position */
568 base64; /* Value of this character */
569 char *outptr, /* Output pointer */
570 *outend; /* End of output buffer */
571
572
573 /*
574 * Range check input...
575 */
576
fa73b229 577 if (!out || !outlen || *outlen < 1 || !in)
ef416fc2 578 return (NULL);
579
fa73b229 580 if (!*in)
581 {
582 *out = '\0';
583 *outlen = 0;
584
585 return (out);
586 }
587
ef416fc2 588 /*
589 * Convert from base-64 to bytes...
590 */
591
592 for (outptr = out, outend = out + *outlen - 1, pos = 0; *in != '\0'; in ++)
593 {
594 /*
595 * Decode this character into a number from 0 to 63...
596 */
597
598 if (*in >= 'A' && *in <= 'Z')
599 base64 = *in - 'A';
600 else if (*in >= 'a' && *in <= 'z')
601 base64 = *in - 'a' + 26;
602 else if (*in >= '0' && *in <= '9')
603 base64 = *in - '0' + 52;
604 else if (*in == '+')
605 base64 = 62;
606 else if (*in == '/')
607 base64 = 63;
608 else if (*in == '=')
609 break;
610 else
611 continue;
612
613 /*
614 * Store the result in the appropriate chars...
615 */
616
617 switch (pos)
618 {
619 case 0 :
620 if (outptr < outend)
621 *outptr = base64 << 2;
622 pos ++;
623 break;
624 case 1 :
625 if (outptr < outend)
626 *outptr++ |= (base64 >> 4) & 3;
627 if (outptr < outend)
628 *outptr = (base64 << 4) & 255;
629 pos ++;
630 break;
631 case 2 :
632 if (outptr < outend)
633 *outptr++ |= (base64 >> 2) & 15;
634 if (outptr < outend)
635 *outptr = (base64 << 6) & 255;
636 pos ++;
637 break;
638 case 3 :
639 if (outptr < outend)
640 *outptr++ |= base64;
641 pos = 0;
642 break;
643 }
644 }
645
646 *outptr = '\0';
647
648 /*
649 * Return the decoded string and size...
650 */
651
652 *outlen = (int)(outptr - out);
653
654 return (out);
655}
656
657
658/*
659 * 'httpEncode64()' - Base64-encode a string.
ecdc0628 660 *
661 * This function is deprecated. Use the httpEncode64_2() function instead
662 * which provides buffer length arguments.
663 *
664 * @deprecated@
ef416fc2 665 */
666
667char * /* O - Encoded string */
668httpEncode64(char *out, /* I - String to write to */
669 const char *in) /* I - String to read from */
670{
b86bc4cf 671 return (httpEncode64_2(out, 512, in, (int)strlen(in)));
ef416fc2 672}
673
674
675/*
676 * 'httpEncode64_2()' - Base64-encode a string.
677 *
f3c17241 678 * @since CUPS 1.1.21/OS X 10.4@
ef416fc2 679 */
680
681char * /* O - Encoded string */
682httpEncode64_2(char *out, /* I - String to write to */
683 int outlen, /* I - Size of output string */
684 const char *in, /* I - String to read from */
685 int inlen) /* I - Size of input string */
686{
687 char *outptr, /* Output pointer */
688 *outend; /* End of output buffer */
689 static const char base64[] = /* Base64 characters... */
690 {
691 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
692 "abcdefghijklmnopqrstuvwxyz"
693 "0123456789"
694 "+/"
695 };
696
697
698 /*
699 * Range check input...
700 */
701
702 if (!out || outlen < 1 || !in)
703 return (NULL);
704
705 /*
706 * Convert bytes to base-64...
707 */
708
709 for (outptr = out, outend = out + outlen - 1; inlen > 0; in ++, inlen --)
710 {
711 /*
712 * Encode the up to 3 characters as 4 Base64 numbers...
713 */
714
715 if (outptr < outend)
716 *outptr ++ = base64[(in[0] & 255) >> 2];
26d47ec6 717
ef416fc2 718 if (outptr < outend)
26d47ec6 719 {
720 if (inlen > 1)
721 *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
722 else
723 *outptr ++ = base64[((in[0] & 255) << 4) & 63];
724 }
ef416fc2 725
726 in ++;
727 inlen --;
728 if (inlen <= 0)
729 {
730 if (outptr < outend)
731 *outptr ++ = '=';
732 if (outptr < outend)
733 *outptr ++ = '=';
734 break;
735 }
736
737 if (outptr < outend)
26d47ec6 738 {
739 if (inlen > 1)
740 *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
741 else
742 *outptr ++ = base64[((in[0] & 255) << 2) & 63];
743 }
ef416fc2 744
745 in ++;
746 inlen --;
747 if (inlen <= 0)
748 {
749 if (outptr < outend)
750 *outptr ++ = '=';
751 break;
752 }
753
754 if (outptr < outend)
755 *outptr ++ = base64[in[0] & 63];
756 }
757
758 *outptr = '\0';
759
760 /*
761 * Return the encoded string...
762 */
763
764 return (out);
765}
766
767
768/*
769 * 'httpGetDateString()' - Get a formatted date/time string from a time value.
770 *
771 * @deprecated@
772 */
773
774const char * /* O - Date/time string */
775httpGetDateString(time_t t) /* I - UNIX time */
776{
777 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
778
779
780 return (httpGetDateString2(t, cg->http_date, sizeof(cg->http_date)));
781}
782
783
784/*
785 * 'httpGetDateString2()' - Get a formatted date/time string from a time value.
786 *
f3c17241 787 * @since CUPS 1.2/OS X 10.5@
ef416fc2 788 */
789
790const char * /* O - Date/time string */
791httpGetDateString2(time_t t, /* I - UNIX time */
792 char *s, /* I - String buffer */
793 int slen) /* I - Size of string buffer */
794{
795 struct tm *tdate; /* UNIX date/time data */
796
797
798 tdate = gmtime(&t);
3e7fe0ca
MS
799 if (tdate)
800 snprintf(s, slen, "%s, %02d %s %d %02d:%02d:%02d GMT",
801 http_days[tdate->tm_wday], tdate->tm_mday,
802 http_months[tdate->tm_mon], tdate->tm_year + 1900,
803 tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
804 else
805 s[0] = '\0';
ef416fc2 806
807 return (s);
808}
809
810
811/*
812 * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
813 */
814
815time_t /* O - UNIX time */
816httpGetDateTime(const char *s) /* I - Date/time string */
817{
818 int i; /* Looping var */
819 char mon[16]; /* Abbreviated month name */
820 int day, year; /* Day of month and year */
821 int hour, min, sec; /* Time */
822 int days; /* Number of days since 1970 */
823 static const int normal_days[] = /* Days to a month, normal years */
824 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
825 static const int leap_days[] = /* Days to a month, leap years */
826 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
827
828
e07d4801 829 DEBUG_printf(("2httpGetDateTime(s=\"%s\")", s));
ef416fc2 830
831 /*
832 * Extract the date and time from the formatted string...
833 */
834
835 if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
836 return (0);
837
e07d4801
MS
838 DEBUG_printf(("4httpGetDateTime: day=%d, mon=\"%s\", year=%d, hour=%d, "
839 "min=%d, sec=%d", day, mon, year, hour, min, sec));
ef416fc2 840
841 /*
842 * Convert the month name to a number from 0 to 11.
843 */
844
845 for (i = 0; i < 12; i ++)
88f9aafc 846 if (!_cups_strcasecmp(mon, http_months[i]))
ef416fc2 847 break;
848
849 if (i >= 12)
850 return (0);
851
e07d4801 852 DEBUG_printf(("4httpGetDateTime: i=%d", i));
ef416fc2 853
854 /*
855 * Now convert the date and time to a UNIX time value in seconds since
856 * 1970. We can't use mktime() since the timezone may not be UTC but
857 * the date/time string *is* UTC.
858 */
859
860 if ((year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0))
861 days = leap_days[i] + day - 1;
862 else
863 days = normal_days[i] + day - 1;
864
e07d4801 865 DEBUG_printf(("4httpGetDateTime: days=%d", days));
ef416fc2 866
867 days += (year - 1970) * 365 + /* 365 days per year (normally) */
868 ((year - 1) / 4 - 492) - /* + leap days */
869 ((year - 1) / 100 - 19) + /* - 100 year days */
870 ((year - 1) / 400 - 4); /* + 400 year days */
871
e07d4801 872 DEBUG_printf(("4httpGetDateTime: days=%d\n", days));
ef416fc2 873
874 return (days * 86400 + hour * 3600 + min * 60 + sec);
875}
876
877
878/*
879 * 'httpSeparate()' - Separate a Universal Resource Identifier into its
880 * components.
ecdc0628 881 *
882 * This function is deprecated; use the httpSeparateURI() function instead.
883 *
884 * @deprecated@
ef416fc2 885 */
886
887void
888httpSeparate(const char *uri, /* I - Universal Resource Identifier */
889 char *scheme, /* O - Scheme [32] (http, https, etc.) */
890 char *username, /* O - Username [1024] */
891 char *host, /* O - Hostname [1024] */
892 int *port, /* O - Port number to use */
893 char *resource) /* O - Resource/filename [1024] */
894{
a4d04587 895 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username,
896 HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource,
897 HTTP_MAX_URI);
ef416fc2 898}
899
900
901/*
902 * 'httpSeparate2()' - Separate a Universal Resource Identifier into its
903 * components.
904 *
ecdc0628 905 * This function is deprecated; use the httpSeparateURI() function instead.
906 *
f3c17241 907 * @since CUPS 1.1.21/OS X 10.4@
ecdc0628 908 * @deprecated@
ef416fc2 909 */
910
911void
912httpSeparate2(const char *uri, /* I - Universal Resource Identifier */
913 char *scheme, /* O - Scheme (http, https, etc.) */
914 int schemelen, /* I - Size of scheme buffer */
915 char *username, /* O - Username */
916 int usernamelen, /* I - Size of username buffer */
917 char *host, /* O - Hostname */
918 int hostlen, /* I - Size of hostname buffer */
919 int *port, /* O - Port number to use */
920 char *resource, /* O - Resource/filename */
921 int resourcelen) /* I - Size of resource buffer */
922{
a4d04587 923 httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username,
924 usernamelen, host, hostlen, port, resource, resourcelen);
ef416fc2 925}
926
927
928/*
929 * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its
930 * components.
931 *
f3c17241 932 * @since CUPS 1.2/OS X 10.5@
ef416fc2 933 */
934
935http_uri_status_t /* O - Result of separation */
a4d04587 936httpSeparateURI(
937 http_uri_coding_t decoding, /* I - Decoding flags */
938 const char *uri, /* I - Universal Resource Identifier */
939 char *scheme, /* O - Scheme (http, https, etc.) */
940 int schemelen, /* I - Size of scheme buffer */
941 char *username, /* O - Username */
942 int usernamelen, /* I - Size of username buffer */
943 char *host, /* O - Hostname */
944 int hostlen, /* I - Size of hostname buffer */
945 int *port, /* O - Port number to use */
946 char *resource, /* O - Resource/filename */
947 int resourcelen) /* I - Size of resource buffer */
ef416fc2 948{
949 char *ptr, /* Pointer into string... */
950 *end; /* End of string */
951 const char *sep; /* Separator character */
952 http_uri_status_t status; /* Result of separation */
953
954
955 /*
956 * Initialize everything to blank...
957 */
958
959 if (scheme && schemelen > 0)
960 *scheme = '\0';
961
962 if (username && usernamelen > 0)
963 *username = '\0';
964
965 if (host && hostlen > 0)
966 *host = '\0';
967
968 if (port)
969 *port = 0;
970
971 if (resource && resourcelen > 0)
972 *resource = '\0';
973
974 /*
975 * Range check input...
976 */
977
978 if (!uri || !port || !scheme || schemelen <= 0 || !username ||
979 usernamelen <= 0 || !host || hostlen <= 0 || !resource ||
980 resourcelen <= 0)
981 return (HTTP_URI_BAD_ARGUMENTS);
982
983 if (!*uri)
984 return (HTTP_URI_BAD_URI);
985
986 /*
987 * Grab the scheme portion of the URI...
988 */
989
990 status = HTTP_URI_OK;
991
992 if (!strncmp(uri, "//", 2))
993 {
994 /*
995 * Workaround for HP IPP client bug...
996 */
997
998 strlcpy(scheme, "ipp", schemelen);
999 status = HTTP_URI_MISSING_SCHEME;
1000 }
1001 else if (*uri == '/')
1002 {
1003 /*
1004 * Filename...
1005 */
1006
1007 strlcpy(scheme, "file", schemelen);
1008 status = HTTP_URI_MISSING_SCHEME;
1009 }
1010 else
1011 {
1012 /*
1013 * Standard URI with scheme...
1014 */
1015
1016 for (ptr = scheme, end = scheme + schemelen - 1;
1017 *uri && *uri != ':' && ptr < end;)
7cf5915e
MS
1018 if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1019 "abcdefghijklmnopqrstuvwxyz"
1020 "0123456789-+.", *uri) != NULL)
ef416fc2 1021 *ptr++ = *uri++;
1022 else
1023 break;
1024
1025 *ptr = '\0';
1026
1027 if (*uri != ':')
1028 {
1029 *scheme = '\0';
1030 return (HTTP_URI_BAD_SCHEME);
1031 }
1032
1033 uri ++;
1034 }
1035
1036 /*
1037 * Set the default port number...
1038 */
1039
1040 if (!strcmp(scheme, "http"))
1041 *port = 80;
1042 else if (!strcmp(scheme, "https"))
1043 *port = 443;
7cf5915e 1044 else if (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps"))
ef416fc2 1045 *port = 631;
88f9aafc 1046 else if (!_cups_strcasecmp(scheme, "lpd"))
ef416fc2 1047 *port = 515;
1048 else if (!strcmp(scheme, "socket")) /* Not yet registered with IANA... */
1049 *port = 9100;
1050 else if (strcmp(scheme, "file") && strcmp(scheme, "mailto"))
1051 status = HTTP_URI_UNKNOWN_SCHEME;
1052
1053 /*
1054 * Now see if we have a hostname...
1055 */
1056
1057 if (!strncmp(uri, "//", 2))
1058 {
1059 /*
1060 * Yes, extract it...
1061 */
1062
1063 uri += 2;
1064
1065 /*
1066 * Grab the username, if any...
1067 */
1068
1069 if ((sep = strpbrk(uri, "@/")) != NULL && *sep == '@')
1070 {
1071 /*
1072 * Get a username:password combo...
1073 */
1074
a4d04587 1075 uri = http_copy_decode(username, uri, usernamelen, "@",
1076 decoding & HTTP_URI_CODING_USERNAME);
ef416fc2 1077
1078 if (!uri)
1079 {
1080 *username = '\0';
1081 return (HTTP_URI_BAD_USERNAME);
1082 }
1083
1084 uri ++;
1085 }
1086
1087 /*
1088 * Then the hostname/IP address...
1089 */
1090
1091 if (*uri == '[')
1092 {
1093 /*
1094 * Grab IPv6 address...
1095 */
1096
1097 uri ++;
1098 if (!strncmp(uri, "v1.", 3))
1099 uri += 3; /* Skip IPvN leader... */
1100
a4d04587 1101 uri = http_copy_decode(host, uri, hostlen, "]",
1102 decoding & HTTP_URI_CODING_HOSTNAME);
ef416fc2 1103
1104 if (!uri)
1105 {
1106 *host = '\0';
1107 return (HTTP_URI_BAD_HOSTNAME);
1108 }
1109
1110 /*
1111 * Validate value...
1112 */
1113
1114 if (*uri != ']')
1115 {
1116 *host = '\0';
1117 return (HTTP_URI_BAD_HOSTNAME);
1118 }
1119
1120 uri ++;
1121
1122 for (ptr = host; *ptr; ptr ++)
1123 if (*ptr == '+')
1124 {
1125 /*
1126 * Convert zone separator to % and stop here...
1127 */
1128
1129 *ptr = '%';
1130 break;
1131 }
1132 else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255))
1133 {
1134 *host = '\0';
1135 return (HTTP_URI_BAD_HOSTNAME);
1136 }
1137 }
1138 else
1139 {
1140 /*
b423cd4c 1141 * Validate the hostname or IPv4 address first...
1142 */
1143
1144 for (ptr = (char *)uri; *ptr; ptr ++)
1145 if (strchr(":?/", *ptr))
1146 break;
1147 else if (!strchr("abcdefghijklmnopqrstuvwxyz"
1148 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1149 "0123456789"
1150 "-._~"
1151 "%"
2fb76298 1152 "!$&'()*+,;=\\", *ptr))
b423cd4c 1153 {
1154 *host = '\0';
1155 return (HTTP_URI_BAD_HOSTNAME);
1156 }
1157
1158 /*
1159 * Then copy the hostname or IPv4 address to the buffer...
ef416fc2 1160 */
1161
a4d04587 1162 uri = http_copy_decode(host, uri, hostlen, ":?/",
1163 decoding & HTTP_URI_CODING_HOSTNAME);
ef416fc2 1164
1165 if (!uri)
1166 {
1167 *host = '\0';
1168 return (HTTP_URI_BAD_HOSTNAME);
1169 }
ef416fc2 1170 }
1171
1172 /*
1173 * Validate hostname for file scheme - only empty and localhost are
1174 * acceptable.
1175 */
1176
1177 if (!strcmp(scheme, "file") && strcmp(host, "localhost") && host[0])
1178 {
1179 *host = '\0';
1180 return (HTTP_URI_BAD_HOSTNAME);
1181 }
1182
1183 /*
1184 * See if we have a port number...
1185 */
1186
1187 if (*uri == ':')
1188 {
1189 /*
1190 * Yes, collect the port number...
1191 */
1192
dfd5680b
MS
1193 if (!isdigit(uri[1] & 255))
1194 {
1195 *port = 0;
1196 return (HTTP_URI_BAD_PORT);
1197 }
1198
ef416fc2 1199 *port = strtol(uri + 1, (char **)&uri, 10);
1200
d6ae789d 1201 if (*uri != '/' && *uri)
ef416fc2 1202 {
1203 *port = 0;
1204 return (HTTP_URI_BAD_PORT);
1205 }
1206 }
1207 }
1208
1209 /*
1210 * The remaining portion is the resource string...
1211 */
1212
1213 if (*uri == '?' || !*uri)
1214 {
1215 /*
1216 * Hostname but no path...
1217 */
1218
1219 status = HTTP_URI_MISSING_RESOURCE;
1220 *resource = '/';
fa73b229 1221
1222 /*
a4d04587 1223 * Copy any query string...
fa73b229 1224 */
1225
1226 if (*uri == '?')
a4d04587 1227 uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL,
1228 decoding & HTTP_URI_CODING_QUERY);
fa73b229 1229 else
1230 resource[1] = '\0';
ef416fc2 1231 }
1232 else
fa73b229 1233 {
a4d04587 1234 uri = http_copy_decode(resource, uri, resourcelen, "?",
1235 decoding & HTTP_URI_CODING_RESOURCE);
fa73b229 1236
1237 if (uri && *uri == '?')
1238 {
1239 /*
a4d04587 1240 * Concatenate any query string...
fa73b229 1241 */
1242
a4d04587 1243 char *resptr = resource + strlen(resource);
1244
b86bc4cf 1245 uri = http_copy_decode(resptr, uri, resourcelen - (int)(resptr - resource),
a4d04587 1246 NULL, decoding & HTTP_URI_CODING_QUERY);
fa73b229 1247 }
1248 }
ef416fc2 1249
1250 if (!uri)
1251 {
1252 *resource = '\0';
1253 return (HTTP_URI_BAD_RESOURCE);
1254 }
1255
1256 /*
1257 * Return the URI separation status...
1258 */
1259
1260 return (status);
1261}
1262
1263
1264/*
1265 * 'httpStatus()' - Return a short string describing a HTTP status code.
9f5eb9be
MS
1266 *
1267 * The returned string is localized to the current POSIX locale and is based
1268 * on the status strings defined in RFC 2616.
ef416fc2 1269 */
1270
9f5eb9be 1271const char * /* O - Localized status string */
ef416fc2 1272httpStatus(http_status_t status) /* I - HTTP status code */
1273{
9f5eb9be
MS
1274 const char *s; /* Status string */
1275 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
1276
1277
1278 if (!cg->lang_default)
1279 cg->lang_default = cupsLangDefault();
1280
ef416fc2 1281 switch (status)
1282 {
1283 case HTTP_CONTINUE :
9f5eb9be
MS
1284 s = _("Continue");
1285 break;
ef416fc2 1286 case HTTP_SWITCHING_PROTOCOLS :
9f5eb9be
MS
1287 s = _("Switching Protocols");
1288 break;
ef416fc2 1289 case HTTP_OK :
9f5eb9be
MS
1290 s = _("OK");
1291 break;
ef416fc2 1292 case HTTP_CREATED :
9f5eb9be
MS
1293 s = _("Created");
1294 break;
ef416fc2 1295 case HTTP_ACCEPTED :
9f5eb9be
MS
1296 s = _("Accepted");
1297 break;
ef416fc2 1298 case HTTP_NO_CONTENT :
9f5eb9be
MS
1299 s = _("No Content");
1300 break;
d6ae789d 1301 case HTTP_MOVED_PERMANENTLY :
9f5eb9be
MS
1302 s = _("Moved Permanently");
1303 break;
d6ae789d 1304 case HTTP_SEE_OTHER :
9f5eb9be
MS
1305 s = _("See Other");
1306 break;
ef416fc2 1307 case HTTP_NOT_MODIFIED :
9f5eb9be
MS
1308 s = _("Not Modified");
1309 break;
ef416fc2 1310 case HTTP_BAD_REQUEST :
9f5eb9be
MS
1311 s = _("Bad Request");
1312 break;
ef416fc2 1313 case HTTP_UNAUTHORIZED :
f11a948a 1314 case HTTP_AUTHORIZATION_CANCELED :
9f5eb9be
MS
1315 s = _("Unauthorized");
1316 break;
ef416fc2 1317 case HTTP_FORBIDDEN :
9f5eb9be
MS
1318 s = _("Forbidden");
1319 break;
ef416fc2 1320 case HTTP_NOT_FOUND :
9f5eb9be
MS
1321 s = _("Not Found");
1322 break;
ef416fc2 1323 case HTTP_REQUEST_TOO_LARGE :
9f5eb9be
MS
1324 s = _("Request Entity Too Large");
1325 break;
ef416fc2 1326 case HTTP_URI_TOO_LONG :
9f5eb9be
MS
1327 s = _("URI Too Long");
1328 break;
ef416fc2 1329 case HTTP_UPGRADE_REQUIRED :
9f5eb9be
MS
1330 s = _("Upgrade Required");
1331 break;
ef416fc2 1332 case HTTP_NOT_IMPLEMENTED :
9f5eb9be
MS
1333 s = _("Not Implemented");
1334 break;
ef416fc2 1335 case HTTP_NOT_SUPPORTED :
9f5eb9be
MS
1336 s = _("Not Supported");
1337 break;
b423cd4c 1338 case HTTP_EXPECTATION_FAILED :
9f5eb9be
MS
1339 s = _("Expectation Failed");
1340 break;
1341 case HTTP_SERVICE_UNAVAILABLE :
1342 s = _("Service Unavailable");
1343 break;
94da7e34
MS
1344 case HTTP_SERVER_ERROR :
1345 s = _("Internal Server Error");
1346 break;
7cf5915e
MS
1347 case HTTP_PKI_ERROR :
1348 s = _("SSL/TLS Negotiation Error");
1349 break;
229681c1
MS
1350 case HTTP_WEBIF_DISABLED :
1351 s = _("Web Interface is Disabled");
1352 break;
b423cd4c 1353
ef416fc2 1354 default :
9f5eb9be
MS
1355 s = _("Unknown");
1356 break;
ef416fc2 1357 }
9f5eb9be
MS
1358
1359 return (_cupsLangString(cg->lang_default, s));
ef416fc2 1360}
1361
1362
1363#ifndef HAVE_HSTRERROR
1364/*
eac3a0a0 1365 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others.
ef416fc2 1366 */
1367
1368const char * /* O - Error string */
1369_cups_hstrerror(int error) /* I - Error number */
1370{
1371 static const char * const errors[] = /* Error strings */
1372 {
1373 "OK",
1374 "Host not found.",
1375 "Try again.",
1376 "Unrecoverable lookup error.",
1377 "No data associated with name."
1378 };
1379
1380
1381 if (error < 0 || error > 4)
1382 return ("Unknown hostname lookup error.");
1383 else
1384 return (errors[error]);
1385}
1386#endif /* !HAVE_HSTRERROR */
1387
1388
1106b00e
MS
1389/*
1390 * '_httpDecodeURI()' - Percent-decode a HTTP request URI.
1391 */
1392
1393char * /* O - Decoded URI or NULL on error */
1394_httpDecodeURI(char *dst, /* I - Destination buffer */
1395 const char *src, /* I - Source URI */
1396 size_t dstsize) /* I - Size of destination buffer */
1397{
1398 if (http_copy_decode(dst, src, (int)dstsize, NULL, 1))
1399 return (dst);
1400 else
1401 return (NULL);
1402}
1403
1404
839a51c8
MS
1405/*
1406 * '_httpEncodeURI()' - Percent-encode a HTTP request URI.
1407 */
1408
1409char * /* O - Encoded URI */
1410_httpEncodeURI(char *dst, /* I - Destination buffer */
1411 const char *src, /* I - Source URI */
1412 size_t dstsize) /* I - Size of destination buffer */
1413{
1414 http_copy_encode(dst, src, dst + dstsize - 1, NULL, NULL, 1);
1415 return (dst);
1416}
1417
1418
5eb9da71
MS
1419/*
1420 * '_httpResolveURI()' - Resolve a DNS-SD URI.
1421 */
1422
1423const char * /* O - Resolved URI */
1424_httpResolveURI(
1425 const char *uri, /* I - DNS-SD URI */
1426 char *resolved_uri, /* I - Buffer for resolved URI */
1f0275e3 1427 size_t resolved_size, /* I - Size of URI buffer */
eac3a0a0 1428 int options, /* I - Resolve options */
07ed0e9a
MS
1429 int (*cb)(void *context), /* I - Continue callback function */
1430 void *context) /* I - Context pointer for callback */
5eb9da71
MS
1431{
1432 char scheme[32], /* URI components... */
1433 userpass[256],
1434 hostname[1024],
1435 resource[1024];
1436 int port;
b19ccc9e 1437#ifdef DEBUG
1f0275e3 1438 http_uri_status_t status; /* URI decode status */
b19ccc9e 1439#endif /* DEBUG */
5eb9da71
MS
1440
1441
e07d4801
MS
1442 DEBUG_printf(("4_httpResolveURI(uri=\"%s\", resolved_uri=%p, "
1443 "resolved_size=" CUPS_LLFMT ")", uri, resolved_uri,
1f0275e3
MS
1444 CUPS_LLCAST resolved_size));
1445
5eb9da71
MS
1446 /*
1447 * Get the device URI...
1448 */
1449
b19ccc9e 1450#ifdef DEBUG
1f0275e3
MS
1451 if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1452 sizeof(scheme), userpass, sizeof(userpass),
1453 hostname, sizeof(hostname), &port, resource,
1454 sizeof(resource))) < HTTP_URI_OK)
b19ccc9e
MS
1455#else
1456 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1457 sizeof(scheme), userpass, sizeof(userpass),
1458 hostname, sizeof(hostname), &port, resource,
1459 sizeof(resource)) < HTTP_URI_OK)
1460#endif /* DEBUG */
1f0275e3 1461 {
eac3a0a0 1462 if (options & _HTTP_RESOLVE_STDERR)
84315f46 1463 _cupsLangPrintFilter(stderr, "ERROR", _("Bad device-uri \"%s\"."), uri);
1f0275e3 1464
e07d4801
MS
1465 DEBUG_printf(("6_httpResolveURI: httpSeparateURI returned %d!", status));
1466 DEBUG_puts("5_httpResolveURI: Returning NULL");
5eb9da71 1467 return (NULL);
1f0275e3 1468 }
5eb9da71
MS
1469
1470 /*
1471 * Resolve it as needed...
1472 */
1473
1474 if (strstr(hostname, "._tcp"))
1475 {
f3c17241 1476#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
5eb9da71
MS
1477 char *regtype, /* Pointer to type in hostname */
1478 *domain; /* Pointer to domain in hostname */
1479 _http_uribuf_t uribuf; /* URI buffer */
f3c17241
MS
1480 int offline = 0; /* offline-report state set? */
1481# ifdef HAVE_DNSSD
1482# ifdef WIN32
1483# pragma comment(lib, "dnssd.lib")
1484# endif /* WIN32 */
1485 DNSServiceRef ref, /* DNS-SD master service reference */
1486 domainref, /* DNS-SD service reference for domain */
1487 localref; /* DNS-SD service reference for .local */
1488 int domainsent = 0; /* Send the domain resolve? */
1489# ifdef HAVE_POLL
38e73f87 1490 struct pollfd polldata; /* Polling data */
f3c17241 1491# else /* select() */
6d2f911b
MS
1492 fd_set input_set; /* Input set for select() */
1493 struct timeval stimeout; /* Timeout value for select() */
f3c17241
MS
1494# endif /* HAVE_POLL */
1495# elif defined(HAVE_AVAHI)
1496 AvahiClient *client; /* Client information */
1497 int error; /* Status */
1498# endif /* HAVE_DNSSD */
38e73f87 1499
eac3a0a0 1500 if (options & _HTTP_RESOLVE_STDERR)
38e73f87 1501 fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname);
5eb9da71
MS
1502
1503 /*
1504 * Separate the hostname into service name, registration type, and domain...
1505 */
1506
1f0275e3
MS
1507 for (regtype = strstr(hostname, "._tcp") - 2;
1508 regtype > hostname;
1509 regtype --)
1510 if (regtype[0] == '.' && regtype[1] == '_')
1511 {
1512 /*
1513 * Found ._servicetype in front of ._tcp...
1514 */
1515
1516 *regtype++ = '\0';
1517 break;
1518 }
1519
1520 if (regtype <= hostname)
1521 {
e07d4801 1522 DEBUG_puts("5_httpResolveURI: Bad hostname, returning NULL");
1f0275e3
MS
1523 return (NULL);
1524 }
5eb9da71 1525
5eb9da71
MS
1526 for (domain = strchr(regtype, '.');
1527 domain;
1528 domain = strchr(domain + 1, '.'))
1529 if (domain[1] != '_')
1530 break;
1531
1532 if (domain)
1533 *domain++ = '\0';
1534
eac3a0a0
MS
1535 uribuf.buffer = resolved_uri;
1536 uribuf.bufsize = resolved_size;
1537 uribuf.options = options;
5a9febac 1538 uribuf.resource = resource;
f3c17241 1539
5eb9da71
MS
1540 resolved_uri[0] = '\0';
1541
e07d4801 1542 DEBUG_printf(("6_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", "
1f0275e3 1543 "domain=\"%s\"\n", hostname, regtype, domain));
eac3a0a0 1544 if (options & _HTTP_RESOLVE_STDERR)
1f0275e3
MS
1545 {
1546 fputs("STATE: +connecting-to-device\n", stderr);
38e73f87
MS
1547 fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1548 "domain=\"local.\"...\n", hostname, regtype);
1f0275e3
MS
1549 }
1550
38e73f87
MS
1551 uri = NULL;
1552
f3c17241 1553# ifdef HAVE_DNSSD
38e73f87 1554 if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError)
5eb9da71 1555 {
db8b865d 1556 int myinterface = kDNSServiceInterfaceIndexAny;
a469f8a5
MS
1557 /* Lookup on any interface */
1558
1559 if (!strcmp(scheme, "ippusb"))
db8b865d 1560 myinterface = kDNSServiceInterfaceIndexLocalOnly;
a469f8a5 1561
38e73f87 1562 localref = ref;
3e7fe0ca 1563 if (DNSServiceResolve(&localref,
db8b865d 1564 kDNSServiceFlagsShareConnection, myinterface,
a469f8a5 1565 hostname, regtype, "local.", http_resolve_cb,
38e73f87
MS
1566 &uribuf) == kDNSServiceErr_NoError)
1567 {
acb056cb
MS
1568 int fds; /* Number of ready descriptors */
1569 time_t timeout, /* Poll timeout */
3e7fe0ca
MS
1570 start_time = time(NULL),/* Start time */
1571 end_time = start_time + 90;
1572 /* End time */
acb056cb 1573
3e7fe0ca 1574 while (time(NULL) < end_time)
38e73f87 1575 {
eac3a0a0 1576 if (options & _HTTP_RESOLVE_STDERR)
0837b7e8 1577 _cupsLangPrintFilter(stderr, "INFO", _("Looking for printer."));
acb056cb 1578
07ed0e9a
MS
1579 if (cb && !(*cb)(context))
1580 {
1581 DEBUG_puts("5_httpResolveURI: callback returned 0 (stop)");
1582 break;
1583 }
1584
38e73f87 1585 /*
3e7fe0ca 1586 * Wakeup every 2 seconds to emit a "looking for printer" message...
38e73f87
MS
1587 */
1588
3e7fe0ca
MS
1589 if ((timeout = end_time - time(NULL)) > 2)
1590 timeout = 2;
acb056cb 1591
f3c17241 1592# ifdef HAVE_POLL
38e73f87
MS
1593 polldata.fd = DNSServiceRefSockFD(ref);
1594 polldata.events = POLLIN;
1595
3e7fe0ca 1596 fds = poll(&polldata, 1, 1000 * timeout);
acb056cb 1597
f3c17241 1598# else /* select() */
6d2f911b
MS
1599 FD_ZERO(&input_set);
1600 FD_SET(DNSServiceRefSockFD(ref), &input_set);
1601
f3c17241 1602# ifdef WIN32
82cc1f9a 1603 stimeout.tv_sec = (long)timeout;
f3c17241 1604# else
3e7fe0ca 1605 stimeout.tv_sec = timeout;
f3c17241 1606# endif /* WIN32 */
3e7fe0ca 1607 stimeout.tv_usec = 0;
6d2f911b 1608
88f9aafc 1609 fds = select(DNSServiceRefSockFD(ref)+1, &input_set, NULL, NULL,
3e7fe0ca 1610 &stimeout);
f3c17241 1611# endif /* HAVE_POLL */
6d2f911b 1612
acb056cb
MS
1613 if (fds < 0)
1614 {
1615 if (errno != EINTR && errno != EAGAIN)
1616 {
1617 DEBUG_printf(("5_httpResolveURI: poll error: %s", strerror(errno)));
1618 break;
1619 }
1620 }
1621 else if (fds == 0)
38e73f87
MS
1622 {
1623 /*
acb056cb
MS
1624 * Wait 2 seconds for a response to the local resolve; if nothing
1625 * comes in, do an additional domain resolution...
38e73f87
MS
1626 */
1627
3e7fe0ca 1628 if (domainsent == 0 && domain && _cups_strcasecmp(domain, "local."))
acb056cb 1629 {
eac3a0a0 1630 if (options & _HTTP_RESOLVE_STDERR)
acb056cb
MS
1631 fprintf(stderr,
1632 "DEBUG: Resolving \"%s\", regtype=\"%s\", "
eac3a0a0
MS
1633 "domain=\"%s\"...\n", hostname, regtype,
1634 domain ? domain : "");
88f9aafc 1635
acb056cb 1636 domainref = ref;
3e7fe0ca 1637 if (DNSServiceResolve(&domainref,
82cc1f9a 1638 kDNSServiceFlagsShareConnection,
db8b865d 1639 myinterface, hostname, regtype, domain,
3e7fe0ca
MS
1640 http_resolve_cb,
1641 &uribuf) == kDNSServiceErr_NoError)
acb056cb
MS
1642 domainsent = 1;
1643 }
6c48a6ca
MS
1644
1645 /*
1646 * If it hasn't resolved within 5 seconds set the offline-report
1647 * printer-state-reason...
1648 */
1649
eac3a0a0
MS
1650 if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 &&
1651 time(NULL) > (start_time + 5))
6c48a6ca
MS
1652 {
1653 fputs("STATE: +offline-report\n", stderr);
1654 offline = 1;
1655 }
38e73f87 1656 }
acb056cb
MS
1657 else
1658 {
1659 if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError)
1660 {
1661 uri = resolved_uri;
1662 break;
1663 }
1664 }
1665 }
38e73f87
MS
1666
1667 if (domainsent)
1668 DNSServiceRefDeallocate(domainref);
1669
1670 DNSServiceRefDeallocate(localref);
1671 }
5eb9da71
MS
1672
1673 DNSServiceRefDeallocate(ref);
1674 }
f3c17241
MS
1675# else /* HAVE_AVAHI */
1676 if ((uribuf.poll = avahi_simple_poll_new()) != NULL)
1677 {
1678 avahi_simple_poll_set_func(uribuf.poll, http_poll_cb, NULL);
1679
1680 if ((client = avahi_client_new(avahi_simple_poll_get(uribuf.poll),
1681 0, http_client_cb,
1682 &uribuf, &error)) != NULL)
1683 {
1684 if (avahi_service_resolver_new(client, AVAHI_IF_UNSPEC,
1685 AVAHI_PROTO_UNSPEC, hostname,
1686 regtype, "local.", AVAHI_PROTO_UNSPEC, 0,
1687 http_resolve_cb, &uribuf) != NULL)
1688 {
1689 time_t start_time = time(NULL),
1690 /* Start time */
1691 end_time = start_time + 90;
1692 /* End time */
1693 int pstatus; /* Poll status */
1694
1695 pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000);
1696
1697 if (pstatus == 0 && !resolved_uri[0] && domain &&
1698 _cups_strcasecmp(domain, "local."))
1699 {
1700 /*
1701 * Resolve for .local hasn't returned anything, try the listed
1702 * domain...
1703 */
1704
1705 avahi_service_resolver_new(client, AVAHI_IF_UNSPEC,
1706 AVAHI_PROTO_UNSPEC, hostname,
1707 regtype, domain, AVAHI_PROTO_UNSPEC, 0,
1708 http_resolve_cb, &uribuf);
1709 }
1710
1711 while (!pstatus && !resolved_uri[0] && time(NULL) < end_time)
1712 {
1713 if ((pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000)) != 0)
1714 break;
1715
1716 /*
1717 * If it hasn't resolved within 5 seconds set the offline-report
1718 * printer-state-reason...
1719 */
1720
1721 if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 &&
1722 time(NULL) > (start_time + 5))
1723 {
1724 fputs("STATE: +offline-report\n", stderr);
1725 offline = 1;
1726 }
1727 }
1728
1729 /*
1730 * Collect the result (if we got one).
1731 */
1732
1733 if (resolved_uri[0])
1734 uri = resolved_uri;
1735 }
1736
1737 avahi_client_free(client);
1738 }
1739
1740 avahi_simple_poll_free(uribuf.poll);
1741 }
1742# endif /* HAVE_DNSSD */
1f0275e3 1743
eac3a0a0 1744 if (options & _HTTP_RESOLVE_STDERR)
38e73f87
MS
1745 {
1746 if (uri)
3e7fe0ca 1747 {
38e73f87 1748 fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri);
3e7fe0ca
MS
1749 fputs("STATE: -connecting-to-device,offline-report\n", stderr);
1750 }
f11a948a 1751 else
3e7fe0ca 1752 {
4d301e69 1753 fputs("DEBUG: Unable to resolve URI\n", stderr);
3e7fe0ca
MS
1754 fputs("STATE: -connecting-to-device\n", stderr);
1755 }
38e73f87 1756 }
1f0275e3 1757
f3c17241 1758#else /* HAVE_DNSSD || HAVE_AVAHI */
1f0275e3
MS
1759 /*
1760 * No DNS-SD support...
1761 */
5eb9da71
MS
1762
1763 uri = NULL;
f3c17241 1764#endif /* HAVE_DNSSD || HAVE_AVAHI */
1f0275e3 1765
eac3a0a0 1766 if ((options & _HTTP_RESOLVE_STDERR) && !uri)
a469f8a5 1767 _cupsLangPrintFilter(stderr, "INFO", _("Unable to find printer."));
5eb9da71 1768 }
eac3a0a0
MS
1769 else
1770 {
1771 /*
1772 * Nothing more to do...
1773 */
1774
1775 strlcpy(resolved_uri, uri, resolved_size);
1776 uri = resolved_uri;
1777 }
5eb9da71 1778
e07d4801 1779 DEBUG_printf(("5_httpResolveURI: Returning \"%s\"", uri));
1f0275e3 1780
5eb9da71
MS
1781 return (uri);
1782}
1783
1784
f3c17241
MS
1785#ifdef HAVE_AVAHI
1786/*
1787 * 'http_client_cb()' - Client callback for resolving URI.
1788 */
1789
1790static void
1791http_client_cb(
1792 AvahiClient *client, /* I - Client information */
1793 AvahiClientState state, /* I - Current state */
1794 void *context) /* I - Pointer to URI buffer */
1795{
1796 DEBUG_printf(("7http_client_cb(client=%p, state=%d, context=%p)", client,
1797 state, context));
1798
1799 /*
1800 * If the connection drops, quit.
1801 */
1802
1803 if (state == AVAHI_CLIENT_FAILURE)
1804 {
1805 _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
1806 /* URI buffer */
1807
1808 avahi_simple_poll_quit(uribuf->poll);
1809 }
1810}
1811#endif /* HAVE_AVAHI */
1812
1813
ef416fc2 1814/*
1815 * 'http_copy_decode()' - Copy and decode a URI.
1816 */
1817
1818static const char * /* O - New source pointer or NULL on error */
88f9aafc 1819http_copy_decode(char *dst, /* O - Destination buffer */
ef416fc2 1820 const char *src, /* I - Source pointer */
1821 int dstsize, /* I - Destination size */
a4d04587 1822 const char *term, /* I - Terminating characters */
1823 int decode) /* I - Decode %-encoded values */
ef416fc2 1824{
1825 char *ptr, /* Pointer into buffer */
1826 *end; /* End of buffer */
1827 int quoted; /* Quoted character */
1828
1829
1830 /*
1831 * Copy the src to the destination until we hit a terminating character
1832 * or the end of the string.
1833 */
1834
e00b005a 1835 for (ptr = dst, end = dst + dstsize - 1;
1836 *src && (!term || !strchr(term, *src));
1837 src ++)
ef416fc2 1838 if (ptr < end)
1839 {
a4d04587 1840 if (*src == '%' && decode)
ef416fc2 1841 {
1842 if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255))
1843 {
1844 /*
1845 * Grab a hex-encoded character...
1846 */
1847
1848 src ++;
1849 if (isalpha(*src))
1850 quoted = (tolower(*src) - 'a' + 10) << 4;
1851 else
1852 quoted = (*src - '0') << 4;
1853
1854 src ++;
1855 if (isalpha(*src))
1856 quoted |= tolower(*src) - 'a' + 10;
1857 else
1858 quoted |= *src - '0';
1859
1860 *ptr++ = quoted;
1861 }
1862 else
1863 {
1864 /*
1865 * Bad hex-encoded character...
1866 */
1867
1868 *ptr = '\0';
1869 return (NULL);
1870 }
1871 }
1872 else
1873 *ptr++ = *src;
1874 }
1875
1876 *ptr = '\0';
1877
1878 return (src);
1879}
1880
1881
1882/*
1883 * 'http_copy_encode()' - Copy and encode a URI.
1884 */
1885
1886static char * /* O - End of current URI */
88f9aafc 1887http_copy_encode(char *dst, /* O - Destination buffer */
ef416fc2 1888 const char *src, /* I - Source pointer */
1889 char *dstend, /* I - End of destination buffer */
a4d04587 1890 const char *reserved, /* I - Extra reserved characters */
1891 const char *term, /* I - Terminating characters */
1892 int encode) /* I - %-encode reserved chars? */
ef416fc2 1893{
ac884b6a 1894 static const char hex[] = "0123456789ABCDEF";
ef416fc2 1895
1896
1897 while (*src && dst < dstend)
1898 {
a4d04587 1899 if (term && *src == *term)
1900 return (dst);
1901
1902 if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
1903 (reserved && strchr(reserved, *src))))
ef416fc2 1904 {
1905 /*
1906 * Hex encode reserved characters...
1907 */
1908
1909 if ((dst + 2) >= dstend)
1910 break;
1911
1912 *dst++ = '%';
1913 *dst++ = hex[(*src >> 4) & 15];
1914 *dst++ = hex[*src & 15];
1915
1916 src ++;
1917 }
1918 else
1919 *dst++ = *src++;
1920 }
1921
839a51c8
MS
1922 *dst = '\0';
1923
ef416fc2 1924 if (*src)
1925 return (NULL);
1926 else
1927 return (dst);
1928}
1929
1930
5eb9da71
MS
1931#ifdef HAVE_DNSSD
1932/*
eac3a0a0 1933 * 'http_resolve_cb()' - Build a device URI for the given service name.
5eb9da71
MS
1934 */
1935
6d2f911b 1936static void DNSSD_API
eac3a0a0 1937http_resolve_cb(
5eb9da71
MS
1938 DNSServiceRef sdRef, /* I - Service reference */
1939 DNSServiceFlags flags, /* I - Results flags */
1940 uint32_t interfaceIndex, /* I - Interface number */
1941 DNSServiceErrorType errorCode, /* I - Error, if any */
1942 const char *fullName, /* I - Full service name */
1943 const char *hostTarget, /* I - Hostname */
1944 uint16_t port, /* I - Port number */
1945 uint16_t txtLen, /* I - Length of TXT record */
1946 const unsigned char *txtRecord, /* I - TXT record data */
1947 void *context) /* I - Pointer to URI buffer */
1948{
f3c17241
MS
1949 _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
1950 /* URI buffer */
eac3a0a0 1951 const char *scheme, /* URI scheme */
f3c17241
MS
1952 *hostptr, /* Pointer into hostTarget */
1953 *reskey, /* "rp" or "rfo" */
1954 *resdefault; /* Default path */
1955 char resource[257], /* Remote path */
eac3a0a0 1956 fqdn[256]; /* FQDN of the .local name */
5eb9da71
MS
1957 const void *value; /* Value from TXT record */
1958 uint8_t valueLen; /* Length of value */
5eb9da71
MS
1959
1960
eac3a0a0 1961 DEBUG_printf(("7http_resolve_cb(sdRef=%p, flags=%x, interfaceIndex=%u, "
5eb9da71 1962 "errorCode=%d, fullName=\"%s\", hostTarget=\"%s\", port=%u, "
e07d4801 1963 "txtLen=%u, txtRecord=%p, context=%p)", sdRef, flags,
5eb9da71
MS
1964 interfaceIndex, errorCode, fullName, hostTarget, port, txtLen,
1965 txtRecord, context));
1966
1967 /*
1968 * Figure out the scheme from the full name...
1969 */
1970
eac3a0a0
MS
1971 if (strstr(fullName, "._ipps") || strstr(fullName, "._ipp-tls"))
1972 scheme = "ipps";
1973 else if (strstr(fullName, "._ipp") || strstr(fullName, "._fax-ipp"))
5eb9da71 1974 scheme = "ipp";
eac3a0a0
MS
1975 else if (strstr(fullName, "._http."))
1976 scheme = "http";
1977 else if (strstr(fullName, "._https."))
1978 scheme = "https";
5eb9da71
MS
1979 else if (strstr(fullName, "._printer."))
1980 scheme = "lpd";
1981 else if (strstr(fullName, "._pdl-datastream."))
1982 scheme = "socket";
1983 else
1984 scheme = "riousbprint";
1985
1986 /*
1987 * Extract the "remote printer" key from the TXT record...
1988 */
1989
f3c17241
MS
1990 if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) &&
1991 (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")))
1992 {
1993 reskey = "rfo";
1994 resdefault = "/ipp/faxout";
1995 }
1996 else
1997 {
1998 reskey = "rp";
1999 resdefault = "/";
2000 }
2001
2002 if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, reskey,
5eb9da71 2003 &valueLen)) != NULL)
9b66acc5
MS
2004 {
2005 if (((char *)value)[0] == '/')
2006 {
2007 /*
f3c17241 2008 * Value (incorrectly) has a leading slash already...
9b66acc5
MS
2009 */
2010
f3c17241
MS
2011 memcpy(resource, value, valueLen);
2012 resource[valueLen] = '\0';
9b66acc5
MS
2013 }
2014 else
2015 {
2016 /*
2017 * Convert to resource by concatenating with a leading "/"...
2018 */
2019
f3c17241
MS
2020 resource[0] = '/';
2021 memcpy(resource + 1, value, valueLen);
2022 resource[valueLen + 1] = '\0';
9b66acc5
MS
2023 }
2024 }
2025 else
5eb9da71
MS
2026 {
2027 /*
f3c17241 2028 * Use the default value...
5eb9da71
MS
2029 */
2030
f3c17241 2031 strlcpy(resource, resdefault, sizeof(resource));
5eb9da71 2032 }
5eb9da71
MS
2033
2034 /*
eac3a0a0 2035 * Lookup the FQDN if needed...
5eb9da71
MS
2036 */
2037
eac3a0a0
MS
2038 if ((uribuf->options & _HTTP_RESOLVE_FQDN) &&
2039 (hostptr = hostTarget + strlen(hostTarget) - 7) > hostTarget &&
88f9aafc 2040 !_cups_strcasecmp(hostptr, ".local."))
eac3a0a0
MS
2041 {
2042 /*
2043 * OK, we got a .local name but the caller needs a real domain. Start by
2044 * getting the IP address of the .local name and then do reverse-lookups...
2045 */
2046
2047 http_addrlist_t *addrlist, /* List of addresses */
2048 *addr; /* Current address */
2049
2050 DEBUG_printf(("8http_resolve_cb: Looking up \"%s\".", hostTarget));
2051
2052 snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port));
2053 if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL)
2054 {
2055 for (addr = addrlist; addr; addr = addr->next)
2056 {
2057 int error = getnameinfo(&(addr->addr.addr),
2058 httpAddrLength(&(addr->addr)),
2059 fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD);
2060
2061 if (!error)
2062 {
2063 DEBUG_printf(("8http_resolve_cb: Found \"%s\".", fqdn));
2064
2065 if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn ||
88f9aafc 2066 _cups_strcasecmp(hostptr, ".local"))
eac3a0a0
MS
2067 {
2068 hostTarget = fqdn;
2069 break;
2070 }
2071 }
2072#ifdef DEBUG
2073 else
2074 DEBUG_printf(("8http_resolve_cb: \"%s\" did not resolve: %d",
2075 httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)),
2076 error));
2077#endif /* DEBUG */
2078 }
0fa6c7fa
MS
2079
2080 httpAddrFreeList(addrlist);
eac3a0a0
MS
2081 }
2082 }
2083
2084 /*
2085 * Assemble the final device URI...
2086 */
5eb9da71 2087
5a9febac
MS
2088 if ((!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
2089 !strcmp(uribuf->resource, "/cups"))
2090 httpAssembleURIf(HTTP_URI_CODING_ALL, uribuf->buffer, uribuf->bufsize,
2091 scheme, NULL, hostTarget, ntohs(port), "%s?snmp=false",
2092 resource);
2093 else
2094 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, uribuf->bufsize,
2095 scheme, NULL, hostTarget, ntohs(port), resource);
5eb9da71 2096
eac3a0a0 2097 DEBUG_printf(("8http_resolve_cb: Resolved URI is \"%s\"...", uribuf->buffer));
5eb9da71 2098}
f3c17241
MS
2099
2100#elif defined(HAVE_AVAHI)
2101/*
2102 * 'http_poll_cb()' - Wait for input on the specified file descriptors.
2103 *
2104 * Note: This function is needed because avahi_simple_poll_iterate is broken
2105 * and always uses a timeout of 0 (!) milliseconds.
2106 * (Avahi Ticket #364)
2107 */
2108
2109static int /* O - Number of file descriptors matching */
2110http_poll_cb(
2111 struct pollfd *pollfds, /* I - File descriptors */
2112 unsigned int num_pollfds, /* I - Number of file descriptors */
2113 int timeout, /* I - Timeout in milliseconds (used) */
2114 void *context) /* I - User data (unused) */
2115{
2116 (void)timeout;
2117 (void)context;
2118
2119 return (poll(pollfds, num_pollfds, 2000));
2120}
2121
2122
2123/*
2124 * 'http_resolve_cb()' - Build a device URI for the given service name.
2125 */
2126
2127static void
2128http_resolve_cb(
2129 AvahiServiceResolver *resolver, /* I - Resolver (unused) */
2130 AvahiIfIndex interface, /* I - Interface index (unused) */
2131 AvahiProtocol protocol, /* I - Network protocol (unused) */
2132 AvahiResolverEvent event, /* I - Event (found, etc.) */
2133 const char *name, /* I - Service name */
2134 const char *type, /* I - Registration type */
2135 const char *domain, /* I - Domain (unused) */
2136 const char *hostTarget, /* I - Hostname */
2137 const AvahiAddress *address, /* I - Address (unused) */
2138 uint16_t port, /* I - Port number */
2139 AvahiStringList *txt, /* I - TXT record */
2140 AvahiLookupResultFlags flags, /* I - Lookup flags (unused) */
2141 void *context) /* I - Pointer to URI buffer */
2142{
2143 _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
2144 /* URI buffer */
2145 const char *scheme, /* URI scheme */
2146 *hostptr, /* Pointer into hostTarget */
2147 *reskey, /* "rp" or "rfo" */
2148 *resdefault; /* Default path */
2149 char resource[257], /* Remote path */
2150 fqdn[256]; /* FQDN of the .local name */
2151 AvahiStringList *pair; /* Current TXT record key/value pair */
2152 char *value; /* Value for "rp" key */
2153 size_t valueLen = 0; /* Length of "rp" key */
2154
2155
2156 DEBUG_printf(("7http_resolve_cb(resolver=%p, "
2157 "interface=%d, protocol=%d, event=%d, name=\"%s\", "
2158 "type=\"%s\", domain=\"%s\", hostTarget=\"%s\", address=%p, "
2159 "port=%d, txt=%p, flags=%d, context=%p)",
2160 resolver, interface, protocol, event, name, type, domain,
2161 hostTarget, address, port, txt, flags, context));
2162
2163 if (event != AVAHI_RESOLVER_FOUND)
2164 {
2165 avahi_service_resolver_free(resolver);
2166 avahi_simple_poll_quit(uribuf->poll);
2167 return;
2168 }
2169
2170 /*
2171 * Figure out the scheme from the full name...
2172 */
2173
2174 if (strstr(type, "_ipp."))
2175 scheme = "ipp";
2176 else if (strstr(type, "_printer."))
2177 scheme = "lpd";
2178 else if (strstr(type, "_pdl-datastream."))
2179 scheme = "socket";
2180 else
2181 scheme = "riousbprint";
2182
2183 if (!strncmp(type, "_ipps.", 6) || !strncmp(type, "_ipp-tls.", 9))
2184 scheme = "ipps";
2185 else if (!strncmp(type, "_ipp.", 5) || !strncmp(type, "_fax-ipp.", 9))
2186 scheme = "ipp";
2187 else if (!strncmp(type, "_http.", 6))
2188 scheme = "http";
2189 else if (!strncmp(type, "_https.", 7))
2190 scheme = "https";
2191 else if (!strncmp(type, "_printer.", 9))
2192 scheme = "lpd";
2193 else if (!strncmp(type, "_pdl-datastream.", 16))
2194 scheme = "socket";
2195 else
2196 {
2197 avahi_service_resolver_free(resolver);
2198 avahi_simple_poll_quit(uribuf->poll);
2199 return;
2200 }
2201
2202 /*
2203 * Extract the remote resource key from the TXT record...
2204 */
2205
2206 if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) &&
2207 (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")))
2208 {
2209 reskey = "rfo";
2210 resdefault = "/ipp/faxout";
2211 }
2212 else
2213 {
2214 reskey = "rp";
2215 resdefault = "/";
2216 }
2217
2218 if ((pair = avahi_string_list_find(txt, reskey)) != NULL)
2219 {
2220 avahi_string_list_get_pair(pair, NULL, &value, &valueLen);
2221
2222 if (value[0] == '/')
2223 {
2224 /*
2225 * Value (incorrectly) has a leading slash already...
2226 */
2227
2228 memcpy(resource, value, valueLen);
2229 resource[valueLen] = '\0';
2230 }
2231 else
2232 {
2233 /*
2234 * Convert to resource by concatenating with a leading "/"...
2235 */
2236
2237 resource[0] = '/';
2238 memcpy(resource + 1, value, valueLen);
2239 resource[valueLen + 1] = '\0';
2240 }
2241 }
2242 else
2243 {
2244 /*
2245 * Use the default value...
2246 */
2247
2248 strlcpy(resource, resdefault, sizeof(resource));
2249 }
2250
2251 /*
2252 * Lookup the FQDN if needed...
2253 */
2254
2255 if ((uribuf->options & _HTTP_RESOLVE_FQDN) &&
2256 (hostptr = hostTarget + strlen(hostTarget) - 6) > hostTarget &&
2257 !_cups_strcasecmp(hostptr, ".local"))
2258 {
2259 /*
2260 * OK, we got a .local name but the caller needs a real domain. Start by
2261 * getting the IP address of the .local name and then do reverse-lookups...
2262 */
2263
2264 http_addrlist_t *addrlist, /* List of addresses */
2265 *addr; /* Current address */
2266
2267 DEBUG_printf(("8http_resolve_cb: Looking up \"%s\".", hostTarget));
2268
2269 snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port));
2270 if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL)
2271 {
2272 for (addr = addrlist; addr; addr = addr->next)
2273 {
2274 int error = getnameinfo(&(addr->addr.addr),
2275 httpAddrLength(&(addr->addr)),
2276 fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD);
2277
2278 if (!error)
2279 {
2280 DEBUG_printf(("8http_resolve_cb: Found \"%s\".", fqdn));
2281
2282 if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn ||
2283 _cups_strcasecmp(hostptr, ".local"))
2284 {
2285 hostTarget = fqdn;
2286 break;
2287 }
2288 }
2289#ifdef DEBUG
2290 else
2291 DEBUG_printf(("8http_resolve_cb: \"%s\" did not resolve: %d",
2292 httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)),
2293 error));
2294#endif /* DEBUG */
2295 }
0fa6c7fa
MS
2296
2297 httpAddrFreeList(addrlist);
f3c17241
MS
2298 }
2299 }
2300
2301 /*
2302 * Assemble the final device URI using the resolved hostname...
2303 */
2304
2305 httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, uribuf->bufsize, scheme,
2306 NULL, hostTarget, port, resource);
2307 DEBUG_printf(("8http_resolve_cb: Resolved URI is \"%s\".", uribuf->buffer));
2308
2309 avahi_simple_poll_quit(uribuf->poll);
2310}
5eb9da71
MS
2311#endif /* HAVE_DNSSD */
2312
2313
ef416fc2 2314/*
b19ccc9e 2315 * End of "$Id: http-support.c 7952 2008-09-17 00:56:20Z mike $".
ef416fc2 2316 */