1/*
2 * "$Id: http-support.c 11528 2014-01-14 20:24:03Z msweet $"
3 *
4 *   HTTP support routines for CUPS.
5 *
6 *   Copyright 2007-2013 by Apple Inc.
7 *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 *   These coded instructions, statements, and computer programs are the
10 *   property of Apple Inc. and are protected by Federal copyright
11 *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12 *   which should have been included with this file.  If this file is
13 *   file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 *   This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
19 *   httpAssembleURI()	  - Assemble a uniform resource identifier from its
20 *			    components.
21 *   httpAssembleURIf()   - Assemble a uniform resource identifier from its
22 *			    components with a formatted resource.
23 *   httpAssembleUUID()   - Assemble a name-based UUID URN conforming to RFC
24 *                          4122.
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.
29 *   httpGetDateString()  - Get a formatted date/time string from a time value.
30 *   httpGetDateString2() - Get a formatted date/time string from a time value.
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.
46 *   http_copy_decode()   - Copy and decode a URI.
47 *   http_copy_encode()   - Copy and encode a URI.
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.
51 */
52
53/*
54 * Include necessary headers...
55 */
56
57#include "cups-private.h"
58#ifdef HAVE_DNSSD
59#  include <dns_sd.h>
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 */
67#elif defined(HAVE_AVAHI)
68#  include <avahi-client/client.h>
69#  include <avahi-client/lookup.h>
70#  include <avahi-common/simple-watch.h>
71#endif /* HAVE_DNSSD */
72
73
74/*
75 * Local types...
76 */
77
78typedef struct _http_uribuf_s		/* URI buffer */
79{
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 */
86  const char		*resource;	/* Resource from URI */
87} _http_uribuf_t;
88
89
90/*
91 * Local globals...
92 */
93
94static const char * const http_days[7] =
95			{
96			  "Sun",
97			  "Mon",
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,
126			                  int dstsize, const char *term,
127					  int decode);
128static char		*http_copy_encode(char *dst, const char *src,
129			                  char *dstend, const char *reserved,
130					  const char *term, int encode);
131#ifdef HAVE_DNSSD
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);
141#endif /* HAVE_DNSSD */
142
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
159
160/*
161 * 'httpAssembleURI()' - Assemble a uniform resource identifier from its
162 *                       components.
163 *
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.
168 *
169 * @since CUPS 1.2/OS X 10.5@
170 */
171
172http_uri_status_t			/* O - URI status */
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 */
182{
183  char		*ptr,			/* Pointer into URI buffer */
184		*end;			/* End of URI buffer */
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_STATUS_BAD_ARGUMENTS);
197  }
198
199 /*
200  * Assemble the URI starting with the scheme...
201  */
202
203  end = uri + urilen - 1;
204  ptr = http_copy_encode(uri, scheme, end, NULL, NULL, 0);
205
206  if (!ptr)
207    goto assemble_overflow;
208
209  if (!strcmp(scheme, "mailto") || !strcmp(scheme, "tel"))
210  {
211   /*
212    * mailto: and tel: only have :, 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: and tel: 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    const char	*hostptr;		/* Pointer into hostname */
243    int		have_ipv6;		/* Do we have an IPv6 address? */
244
245    if (username && *username)
246    {
247     /*
248      * Add username@ first...
249      */
250
251      ptr = http_copy_encode(ptr, username, end, "/?#[]@", NULL,
252                             encoding & HTTP_URI_CODING_USERNAME);
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
265    * with, we have several special cases to deal with.  If we get
266    * an IPv6 address with brackets around it, assume it is already in
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...
270    */
271
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)
283    {
284     /*
285      * We have a raw IPv6 address...
286      */
287
288      if (strchr(host, '%') && !(encoding & HTTP_URI_CODING_RFC6874))
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       /*
307        * We have a normal (or RFC 6874 link-local) address, add "[" prefix...
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 == '%')
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
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     /*
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.
360      */
361
362      ptr = http_copy_encode(ptr, host, end, "\"#/:<>?@[\\]^`{|}", NULL,
363                             encoding & HTTP_URI_CODING_HOSTNAME);
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
387  if (resource)
388  {
389    char	*query;			/* Pointer to query string */
390
391
392   /*
393    * Copy the resource string up to the query string if present...
394    */
395
396    query = strchr(resource, '?');
397    ptr   = http_copy_encode(ptr, resource, end, NULL, "?",
398                             encoding & HTTP_URI_CODING_RESOURCE);
399    if (!ptr)
400      goto assemble_overflow;
401
402    if (query)
403    {
404     /*
405      * Copy query string without encoding...
406      */
407
408      ptr = http_copy_encode(ptr, query, end, NULL, NULL,
409			     encoding & HTTP_URI_CODING_QUERY);
410      if (!ptr)
411	goto assemble_overflow;
412    }
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
425  return (HTTP_URI_STATUS_OK);
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';
435  return (HTTP_URI_STATUS_OVERFLOW);
436}
437
438
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
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.
448 *
449 * @since CUPS 1.2/OS X 10.5@
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
478    return (HTTP_URI_STATUS_BAD_ARGUMENTS);
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';
492    return (HTTP_URI_STATUS_OVERFLOW);
493  }
494  else
495    return (httpAssembleURI(encoding,  uri, urilen, scheme, username, host,
496                            port, resource));
497}
498
499
500/*
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.
506 *
507 * The buffer needs to be at least 46 bytes in size.
508 *
509 * @since CUPS 1.7/OS X 10.9@
510 */
511
512char *					/* I - UUID string */
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 */
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,
534	   (unsigned)CUPS_RAND() & 0xffff, (unsigned)CUPS_RAND() & 0xffff);
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
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
562
563/*
564 * 'httpDecode64()' - Base64-decode a string.
565 *
566 * This function is deprecated. Use the httpDecode64_2() function instead
567 * which provides buffer length arguments.
568 *
569 * @deprecated@
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 *
592 * @since CUPS 1.1.21/OS X 10.4@
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
610  if (!out || !outlen || *outlen < 1 || !in)
611    return (NULL);
612
613  if (!*in)
614  {
615    *out    = '\0';
616    *outlen = 0;
617
618    return (out);
619  }
620
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.
693 *
694 * This function is deprecated. Use the httpEncode64_2() function instead
695 * which provides buffer length arguments.
696 *
697 * @deprecated@
698 */
699
700char *					/* O - Encoded string */
701httpEncode64(char       *out,		/* I - String to write to */
702             const char *in)		/* I - String to read from */
703{
704  return (httpEncode64_2(out, 512, in, (int)strlen(in)));
705}
706
707
708/*
709 * 'httpEncode64_2()' - Base64-encode a string.
710 *
711 * @since CUPS 1.1.21/OS X 10.4@
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];
750
751    if (outptr < outend)
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    }
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)
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    }
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 *
820 * @since CUPS 1.2/OS X 10.5@
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);
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';
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
862  DEBUG_printf(("2httpGetDateTime(s=\"%s\")", s));
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
871  DEBUG_printf(("4httpGetDateTime: day=%d, mon=\"%s\", year=%d, hour=%d, "
872                "min=%d, sec=%d", day, mon, year, hour, min, sec));
873
874 /*
875  * Convert the month name to a number from 0 to 11.
876  */
877
878  for (i = 0; i < 12; i ++)
879    if (!_cups_strcasecmp(mon, http_months[i]))
880      break;
881
882  if (i >= 12)
883    return (0);
884
885  DEBUG_printf(("4httpGetDateTime: i=%d", i));
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
898  DEBUG_printf(("4httpGetDateTime: days=%d", days));
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
905  DEBUG_printf(("4httpGetDateTime: days=%d\n", days));
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.
914 *
915 * This function is deprecated; use the httpSeparateURI() function instead.
916 *
917 * @deprecated@
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{
928  httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username,
929                  HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource,
930		  HTTP_MAX_URI);
931}
932
933
934/*
935 * 'httpSeparate2()' - Separate a Universal Resource Identifier into its
936 *                     components.
937 *
938 * This function is deprecated; use the httpSeparateURI() function instead.
939 *
940 * @since CUPS 1.1.21/OS X 10.4@
941 * @deprecated@
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{
956  httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username,
957                  usernamelen, host, hostlen, port, resource, resourcelen);
958}
959
960
961/*
962 * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its
963 *                       components.
964 *
965 * @since CUPS 1.2/OS X 10.5@
966 */
967
968http_uri_status_t			/* O - Result of separation */
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 */
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)
1014    return (HTTP_URI_STATUS_BAD_ARGUMENTS);
1015
1016  if (!*uri)
1017    return (HTTP_URI_STATUS_BAD_URI);
1018
1019 /*
1020  * Grab the scheme portion of the URI...
1021  */
1022
1023  status = HTTP_URI_STATUS_OK;
1024
1025  if (!strncmp(uri, "//", 2))
1026  {
1027   /*
1028    * Workaround for HP IPP client bug...
1029    */
1030
1031    strlcpy(scheme, "ipp", schemelen);
1032    status = HTTP_URI_STATUS_MISSING_SCHEME;
1033  }
1034  else if (*uri == '/')
1035  {
1036   /*
1037    * Filename...
1038    */
1039
1040    strlcpy(scheme, "file", schemelen);
1041    status = HTTP_URI_STATUS_MISSING_SCHEME;
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;)
1051      if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1052                 "abcdefghijklmnopqrstuvwxyz"
1053		 "0123456789-+.", *uri) != NULL)
1054        *ptr++ = *uri++;
1055      else
1056        break;
1057
1058    *ptr = '\0';
1059
1060    if (*uri != ':')
1061    {
1062      *scheme = '\0';
1063      return (HTTP_URI_STATUS_BAD_SCHEME);
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;
1077  else if (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps"))
1078    *port = 631;
1079  else if (!_cups_strcasecmp(scheme, "lpd"))
1080    *port = 515;
1081  else if (!strcmp(scheme, "socket"))	/* Not yet registered with IANA... */
1082    *port = 9100;
1083  else if (strcmp(scheme, "file") && strcmp(scheme, "mailto") && strcmp(scheme, "tel"))
1084    status = HTTP_URI_STATUS_UNKNOWN_SCHEME;
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
1108      uri = http_copy_decode(username, uri, usernamelen, "@",
1109                             decoding & HTTP_URI_CODING_USERNAME);
1110
1111      if (!uri)
1112      {
1113        *username = '\0';
1114        return (HTTP_URI_STATUS_BAD_USERNAME);
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 ++;
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      }
1150
1151      uri = http_copy_decode(host, uri, hostlen, "]",
1152                             decoding & HTTP_URI_CODING_HOSTNAME);
1153
1154      if (!uri)
1155      {
1156        *host = '\0';
1157        return (HTTP_URI_STATUS_BAD_HOSTNAME);
1158      }
1159
1160     /*
1161      * Validate value...
1162      */
1163
1164      if (*uri != ']')
1165      {
1166        *host = '\0';
1167        return (HTTP_URI_STATUS_BAD_HOSTNAME);
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	}
1182	else if (*ptr == '%')
1183	{
1184	 /*
1185	  * Stop at zone separator (RFC 6874)
1186	  */
1187
1188	  break;
1189	}
1190	else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255))
1191	{
1192	  *host = '\0';
1193	  return (HTTP_URI_STATUS_BAD_HOSTNAME);
1194	}
1195    }
1196    else
1197    {
1198     /*
1199      * Validate the hostname or IPv4 address first...
1200      */
1201
1202      for (ptr = (char *)uri; *ptr; ptr ++)
1203        if (strchr(":?/", *ptr))
1204	  break;
1205        else if (!strchr("abcdefghijklmnopqrstuvwxyz"	/* unreserved */
1206			 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"	/* unreserved */
1207			 "0123456789"			/* unreserved */
1208	        	 "-._~"				/* unreserved */
1209			 "%"				/* pct-encoded */
1210			 "!$&'()*+,;="			/* sub-delims */
1211			 "\\", *ptr))			/* SMB domain */
1212	{
1213	  *host = '\0';
1214	  return (HTTP_URI_STATUS_BAD_HOSTNAME);
1215	}
1216
1217     /*
1218      * Then copy the hostname or IPv4 address to the buffer...
1219      */
1220
1221      uri = http_copy_decode(host, uri, hostlen, ":?/",
1222                             decoding & HTTP_URI_CODING_HOSTNAME);
1223
1224      if (!uri)
1225      {
1226        *host = '\0';
1227        return (HTTP_URI_STATUS_BAD_HOSTNAME);
1228      }
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';
1239      return (HTTP_URI_STATUS_BAD_HOSTNAME);
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
1252      if (!isdigit(uri[1] & 255))
1253      {
1254        *port = 0;
1255        return (HTTP_URI_STATUS_BAD_PORT);
1256      }
1257
1258      *port = strtol(uri + 1, (char **)&uri, 10);
1259
1260      if (*uri != '/' && *uri)
1261      {
1262        *port = 0;
1263        return (HTTP_URI_STATUS_BAD_PORT);
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
1278    status    = HTTP_URI_STATUS_MISSING_RESOURCE;
1279    *resource = '/';
1280
1281   /*
1282    * Copy any query string...
1283    */
1284
1285    if (*uri == '?')
1286      uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL,
1287                             decoding & HTTP_URI_CODING_QUERY);
1288    else
1289      resource[1] = '\0';
1290  }
1291  else
1292  {
1293    uri = http_copy_decode(resource, uri, resourcelen, "?",
1294                           decoding & HTTP_URI_CODING_RESOURCE);
1295
1296    if (uri && *uri == '?')
1297    {
1298     /*
1299      * Concatenate any query string...
1300      */
1301
1302      char *resptr = resource + strlen(resource);
1303
1304      uri = http_copy_decode(resptr, uri,
1305                             resourcelen - (int)(resptr - resource), NULL,
1306                             decoding & HTTP_URI_CODING_QUERY);
1307    }
1308  }
1309
1310  if (!uri)
1311  {
1312    *resource = '\0';
1313    return (HTTP_URI_STATUS_BAD_RESOURCE);
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.
1326 *
1327 * The returned string is localized to the current POSIX locale and is based
1328 * on the status strings defined in RFC 2616.
1329 */
1330
1331const char *				/* O - Localized status string */
1332httpStatus(http_status_t status)	/* I - HTTP status code */
1333{
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
1341  switch (status)
1342  {
1343    case HTTP_STATUS_ERROR :
1344        s = strerror(errno);
1345        break;
1346    case HTTP_STATUS_CONTINUE :
1347        s = _("Continue");
1348	break;
1349    case HTTP_STATUS_SWITCHING_PROTOCOLS :
1350        s = _("Switching Protocols");
1351	break;
1352    case HTTP_STATUS_OK :
1353        s = _("OK");
1354	break;
1355    case HTTP_STATUS_CREATED :
1356        s = _("Created");
1357	break;
1358    case HTTP_STATUS_ACCEPTED :
1359        s = _("Accepted");
1360	break;
1361    case HTTP_STATUS_NO_CONTENT :
1362        s = _("No Content");
1363	break;
1364    case HTTP_STATUS_MOVED_PERMANENTLY :
1365        s = _("Moved Permanently");
1366	break;
1367    case HTTP_STATUS_SEE_OTHER :
1368        s = _("See Other");
1369	break;
1370    case HTTP_STATUS_NOT_MODIFIED :
1371        s = _("Not Modified");
1372	break;
1373    case HTTP_STATUS_BAD_REQUEST :
1374        s = _("Bad Request");
1375	break;
1376    case HTTP_STATUS_UNAUTHORIZED :
1377    case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED :
1378        s = _("Unauthorized");
1379	break;
1380    case HTTP_STATUS_FORBIDDEN :
1381        s = _("Forbidden");
1382	break;
1383    case HTTP_STATUS_NOT_FOUND :
1384        s = _("Not Found");
1385	break;
1386    case HTTP_STATUS_REQUEST_TOO_LARGE :
1387        s = _("Request Entity Too Large");
1388	break;
1389    case HTTP_STATUS_URI_TOO_LONG :
1390        s = _("URI Too Long");
1391	break;
1392    case HTTP_STATUS_UPGRADE_REQUIRED :
1393        s = _("Upgrade Required");
1394	break;
1395    case HTTP_STATUS_NOT_IMPLEMENTED :
1396        s = _("Not Implemented");
1397	break;
1398    case HTTP_STATUS_NOT_SUPPORTED :
1399        s = _("Not Supported");
1400	break;
1401    case HTTP_STATUS_EXPECTATION_FAILED :
1402        s = _("Expectation Failed");
1403	break;
1404    case HTTP_STATUS_SERVICE_UNAVAILABLE :
1405        s = _("Service Unavailable");
1406	break;
1407    case HTTP_STATUS_SERVER_ERROR :
1408        s = _("Internal Server Error");
1409	break;
1410    case HTTP_STATUS_CUPS_PKI_ERROR :
1411        s = _("SSL/TLS Negotiation Error");
1412	break;
1413    case HTTP_STATUS_CUPS_WEBIF_DISABLED :
1414        s = _("Web Interface is Disabled");
1415	break;
1416
1417    default :
1418        s = _("Unknown");
1419	break;
1420  }
1421
1422  return (_cupsLangString(cg->lang_default, s));
1423}
1424
1425
1426#ifndef HAVE_HSTRERROR
1427/*
1428 * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others.
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
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
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
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 */
1490    size_t     resolved_size,		/* I - Size of URI buffer */
1491    int        options,			/* I - Resolve options */
1492    int        (*cb)(void *context),	/* I - Continue callback function */
1493    void       *context)		/* I - Context pointer for callback */
1494{
1495  char			scheme[32],	/* URI components... */
1496			userpass[256],
1497			hostname[1024],
1498			resource[1024];
1499  int			port;
1500#ifdef DEBUG
1501  http_uri_status_t	status;		/* URI decode status */
1502#endif /* DEBUG */
1503
1504
1505  DEBUG_printf(("4_httpResolveURI(uri=\"%s\", resolved_uri=%p, "
1506                "resolved_size=" CUPS_LLFMT ")", uri, resolved_uri,
1507		CUPS_LLCAST resolved_size));
1508
1509 /*
1510  * Get the device URI...
1511  */
1512
1513#ifdef DEBUG
1514  if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1515                                sizeof(scheme), userpass, sizeof(userpass),
1516				hostname, sizeof(hostname), &port, resource,
1517				sizeof(resource))) < HTTP_URI_STATUS_OK)
1518#else
1519  if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1520		      sizeof(scheme), userpass, sizeof(userpass),
1521		      hostname, sizeof(hostname), &port, resource,
1522		      sizeof(resource)) < HTTP_URI_STATUS_OK)
1523#endif /* DEBUG */
1524  {
1525    if (options & _HTTP_RESOLVE_STDERR)
1526      _cupsLangPrintFilter(stderr, "ERROR", _("Bad device-uri \"%s\"."), uri);
1527
1528    DEBUG_printf(("6_httpResolveURI: httpSeparateURI returned %d!", status));
1529    DEBUG_puts("5_httpResolveURI: Returning NULL");
1530    return (NULL);
1531  }
1532
1533 /*
1534  * Resolve it as needed...
1535  */
1536
1537  if (strstr(hostname, "._tcp"))
1538  {
1539#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1540    char		*regtype,	/* Pointer to type in hostname */
1541			*domain;	/* Pointer to domain in hostname */
1542    _http_uribuf_t	uribuf;		/* URI buffer */
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
1553    struct pollfd	polldata;	/* Polling data */
1554#    else /* select() */
1555    fd_set		input_set;	/* Input set for select() */
1556    struct timeval	stimeout;	/* Timeout value for select() */
1557#    endif /* HAVE_POLL */
1558#  elif defined(HAVE_AVAHI)
1559    AvahiClient		*client;	/* Client information */
1560    int			error;		/* Status */
1561#  endif /* HAVE_DNSSD */
1562
1563    if (options & _HTTP_RESOLVE_STDERR)
1564      fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname);
1565
1566   /*
1567    * Separate the hostname into service name, registration type, and domain...
1568    */
1569
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    {
1585      DEBUG_puts("5_httpResolveURI: Bad hostname, returning NULL");
1586      return (NULL);
1587    }
1588
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
1598    uribuf.buffer   = resolved_uri;
1599    uribuf.bufsize  = resolved_size;
1600    uribuf.options  = options;
1601    uribuf.resource = resource;
1602
1603    resolved_uri[0] = '\0';
1604
1605    DEBUG_printf(("6_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", "
1606                  "domain=\"%s\"\n", hostname, regtype, domain));
1607    if (options & _HTTP_RESOLVE_STDERR)
1608    {
1609      fputs("STATE: +connecting-to-device\n", stderr);
1610      fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1611                      "domain=\"local.\"...\n", hostname, regtype);
1612    }
1613
1614    uri = NULL;
1615
1616#  ifdef HAVE_DNSSD
1617    if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError)
1618    {
1619      int myinterface = kDNSServiceInterfaceIndexAny;
1620					/* Lookup on any interface */
1621
1622      if (!strcmp(scheme, "ippusb"))
1623        myinterface = kDNSServiceInterfaceIndexLocalOnly;
1624
1625      localref = ref;
1626      if (DNSServiceResolve(&localref,
1627                            kDNSServiceFlagsShareConnection, myinterface,
1628                            hostname, regtype, "local.", http_resolve_cb,
1629			    &uribuf) == kDNSServiceErr_NoError)
1630      {
1631	int	fds;			/* Number of ready descriptors */
1632	time_t	timeout,		/* Poll timeout */
1633		start_time = time(NULL),/* Start time */
1634		end_time = start_time + 90;
1635					/* End time */
1636
1637	while (time(NULL) < end_time)
1638	{
1639	  if (options & _HTTP_RESOLVE_STDERR)
1640	    _cupsLangPrintFilter(stderr, "INFO", _("Looking for printer."));
1641
1642	  if (cb && !(*cb)(context))
1643	  {
1644	    DEBUG_puts("5_httpResolveURI: callback returned 0 (stop)");
1645	    break;
1646	  }
1647
1648	 /*
1649	  * Wakeup every 2 seconds to emit a "looking for printer" message...
1650	  */
1651
1652	  if ((timeout = end_time - time(NULL)) > 2)
1653	    timeout = 2;
1654
1655#    ifdef HAVE_POLL
1656	  polldata.fd     = DNSServiceRefSockFD(ref);
1657	  polldata.events = POLLIN;
1658
1659	  fds = poll(&polldata, 1, 1000 * timeout);
1660
1661#    else /* select() */
1662	  FD_ZERO(&input_set);
1663	  FD_SET(DNSServiceRefSockFD(ref), &input_set);
1664
1665#      ifdef WIN32
1666	  stimeout.tv_sec  = (long)timeout;
1667#      else
1668	  stimeout.tv_sec  = timeout;
1669#      endif /* WIN32 */
1670	  stimeout.tv_usec = 0;
1671
1672	  fds = select(DNSServiceRefSockFD(ref)+1, &input_set, NULL, NULL,
1673		       &stimeout);
1674#    endif /* HAVE_POLL */
1675
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)
1685	  {
1686	   /*
1687	    * Wait 2 seconds for a response to the local resolve; if nothing
1688	    * comes in, do an additional domain resolution...
1689	    */
1690
1691	    if (domainsent == 0 && domain && _cups_strcasecmp(domain, "local."))
1692	    {
1693	      if (options & _HTTP_RESOLVE_STDERR)
1694		fprintf(stderr,
1695		        "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1696			"domain=\"%s\"...\n", hostname, regtype,
1697			domain ? domain : "");
1698
1699	      domainref = ref;
1700	      if (DNSServiceResolve(&domainref,
1701	                            kDNSServiceFlagsShareConnection,
1702	                            myinterface, hostname, regtype, domain,
1703				    http_resolve_cb,
1704				    &uribuf) == kDNSServiceErr_NoError)
1705		domainsent = 1;
1706	    }
1707
1708	   /*
1709	    * If it hasn't resolved within 5 seconds set the offline-report
1710	    * printer-state-reason...
1711	    */
1712
1713	    if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 &&
1714	        time(NULL) > (start_time + 5))
1715	    {
1716	      fputs("STATE: +offline-report\n", stderr);
1717	      offline = 1;
1718	    }
1719	  }
1720	  else
1721	  {
1722	    if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError)
1723	    {
1724	      uri = resolved_uri;
1725	      break;
1726	    }
1727	  }
1728	}
1729
1730	if (domainsent)
1731	  DNSServiceRefDeallocate(domainref);
1732
1733	DNSServiceRefDeallocate(localref);
1734      }
1735
1736      DNSServiceRefDeallocate(ref);
1737    }
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 */
1806
1807    if (options & _HTTP_RESOLVE_STDERR)
1808    {
1809      if (uri)
1810      {
1811        fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri);
1812	fputs("STATE: -connecting-to-device,offline-report\n", stderr);
1813      }
1814      else
1815      {
1816        fputs("DEBUG: Unable to resolve URI\n", stderr);
1817	fputs("STATE: -connecting-to-device\n", stderr);
1818      }
1819    }
1820
1821#else /* HAVE_DNSSD || HAVE_AVAHI */
1822   /*
1823    * No DNS-SD support...
1824    */
1825
1826    uri = NULL;
1827#endif /* HAVE_DNSSD || HAVE_AVAHI */
1828
1829    if ((options & _HTTP_RESOLVE_STDERR) && !uri)
1830      _cupsLangPrintFilter(stderr, "INFO", _("Unable to find printer."));
1831  }
1832  else
1833  {
1834   /*
1835    * Nothing more to do...
1836    */
1837
1838    strlcpy(resolved_uri, uri, resolved_size);
1839    uri = resolved_uri;
1840  }
1841
1842  DEBUG_printf(("5_httpResolveURI: Returning \"%s\"", uri));
1843
1844  return (uri);
1845}
1846
1847
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
1877/*
1878 * 'http_copy_decode()' - Copy and decode a URI.
1879 */
1880
1881static const char *			/* O - New source pointer or NULL on error */
1882http_copy_decode(char       *dst,	/* O - Destination buffer */
1883                 const char *src,	/* I - Source pointer */
1884		 int        dstsize,	/* I - Destination size */
1885		 const char *term,	/* I - Terminating characters */
1886		 int        decode)	/* I - Decode %-encoded values */
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
1898  for (ptr = dst, end = dst + dstsize - 1;
1899       *src && (!term || !strchr(term, *src));
1900       src ++)
1901    if (ptr < end)
1902    {
1903      if (*src == '%' && decode)
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      }
1935      else if ((*src & 255) <= 0x20 || (*src & 255) >= 0x7f)
1936      {
1937        *ptr = '\0';
1938        return (NULL);
1939      }
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 */
1955http_copy_encode(char       *dst,	/* O - Destination buffer */
1956                 const char *src,	/* I - Source pointer */
1957		 char       *dstend,	/* I - End of destination buffer */
1958                 const char *reserved,	/* I - Extra reserved characters */
1959		 const char *term,	/* I - Terminating characters */
1960		 int        encode)	/* I - %-encode reserved chars? */
1961{
1962  static const char hex[] = "0123456789ABCDEF";
1963
1964
1965  while (*src && dst < dstend)
1966  {
1967    if (term && *src == *term)
1968      return (dst);
1969
1970    if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
1971                   (reserved && strchr(reserved, *src))))
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
1990  *dst = '\0';
1991
1992  if (*src)
1993    return (NULL);
1994  else
1995    return (dst);
1996}
1997
1998
1999#ifdef HAVE_DNSSD
2000/*
2001 * 'http_resolve_cb()' - Build a device URI for the given service name.
2002 */
2003
2004static void DNSSD_API
2005http_resolve_cb(
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{
2017  _http_uribuf_t	*uribuf = (_http_uribuf_t *)context;
2018					/* URI buffer */
2019  const char		*scheme,	/* URI scheme */
2020			*hostptr,	/* Pointer into hostTarget */
2021			*reskey,	/* "rp" or "rfo" */
2022			*resdefault;	/* Default path */
2023  char			resource[257],	/* Remote path */
2024			fqdn[256];	/* FQDN of the .local name */
2025  const void		*value;		/* Value from TXT record */
2026  uint8_t		valueLen;	/* Length of value */
2027
2028
2029  DEBUG_printf(("7http_resolve_cb(sdRef=%p, flags=%x, interfaceIndex=%u, "
2030	        "errorCode=%d, fullName=\"%s\", hostTarget=\"%s\", port=%u, "
2031	        "txtLen=%u, txtRecord=%p, context=%p)", sdRef, flags,
2032	        interfaceIndex, errorCode, fullName, hostTarget, port, txtLen,
2033	        txtRecord, context));
2034
2035 /*
2036  * Figure out the scheme from the full name...
2037  */
2038
2039  if (strstr(fullName, "._ipps") || strstr(fullName, "._ipp-tls"))
2040    scheme = "ipps";
2041  else if (strstr(fullName, "._ipp") || strstr(fullName, "._fax-ipp"))
2042    scheme = "ipp";
2043  else if (strstr(fullName, "._http."))
2044    scheme = "http";
2045  else if (strstr(fullName, "._https."))
2046    scheme = "https";
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
2058  if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) &&
2059      (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
2060      !TXTRecordGetValuePtr(txtLen, txtRecord, "printer-type", &valueLen))
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,
2072                                    &valueLen)) != NULL)
2073  {
2074    if (((char *)value)[0] == '/')
2075    {
2076     /*
2077      * Value (incorrectly) has a leading slash already...
2078      */
2079
2080      memcpy(resource, value, valueLen);
2081      resource[valueLen] = '\0';
2082    }
2083    else
2084    {
2085     /*
2086      * Convert to resource by concatenating with a leading "/"...
2087      */
2088
2089      resource[0] = '/';
2090      memcpy(resource + 1, value, valueLen);
2091      resource[valueLen + 1] = '\0';
2092    }
2093  }
2094  else
2095  {
2096   /*
2097    * Use the default value...
2098    */
2099
2100    strlcpy(resource, resdefault, sizeof(resource));
2101  }
2102
2103 /*
2104  * Lookup the FQDN if needed...
2105  */
2106
2107  if ((uribuf->options & _HTTP_RESOLVE_FQDN) &&
2108      (hostptr = hostTarget + strlen(hostTarget) - 7) > hostTarget &&
2109      !_cups_strcasecmp(hostptr, ".local."))
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 ||
2135	      _cups_strcasecmp(hostptr, ".local"))
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      }
2148
2149      httpAddrFreeList(addrlist);
2150    }
2151  }
2152
2153 /*
2154  * Assemble the final device URI...
2155  */
2156
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);
2165
2166  DEBUG_printf(("8http_resolve_cb: Resolved URI is \"%s\"...", uribuf->buffer));
2167}
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) &&
2276      (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
2277      !avahi_string_list_find(txt, "printer-type"))
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      }
2366
2367      httpAddrFreeList(addrlist);
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}
2381#endif /* HAVE_DNSSD */
2382
2383
2384/*
2385 * End of "$Id: http-support.c 11528 2014-01-14 20:24:03Z msweet $".
2386 */
2387