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