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