1 /*
2  * HTTP support routines for CUPS.
3  *
4  * Copyright 2007-2017 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@ @exclude all@
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  * The caller must initialize "outlen" to the maximum size of the decoded
570  * string before calling @code httpDecode64_2@.  On return "outlen" contains the
571  * decoded length of the string.
572  *
573  * @since CUPS 1.1.21/macOS 10.4@
574  */
575 
576 char *					/* O  - Decoded string */
httpDecode64_2(char * out,int * outlen,const char * in)577 httpDecode64_2(char       *out,		/* I  - String to write to */
578 	       int        *outlen,	/* IO - Size of output string */
579                const char *in)		/* I  - String to read from */
580 {
581   int		pos;			/* Bit position */
582   unsigned	base64;			/* Value of this character */
583   char		*outptr,		/* Output pointer */
584 		*outend;		/* End of output buffer */
585 
586 
587  /*
588   * Range check input...
589   */
590 
591   if (!out || !outlen || *outlen < 1 || !in)
592     return (NULL);
593 
594   if (!*in)
595   {
596     *out    = '\0';
597     *outlen = 0;
598 
599     return (out);
600   }
601 
602  /*
603   * Convert from base-64 to bytes...
604   */
605 
606   for (outptr = out, outend = out + *outlen - 1, pos = 0; *in != '\0'; in ++)
607   {
608    /*
609     * Decode this character into a number from 0 to 63...
610     */
611 
612     if (*in >= 'A' && *in <= 'Z')
613       base64 = (unsigned)(*in - 'A');
614     else if (*in >= 'a' && *in <= 'z')
615       base64 = (unsigned)(*in - 'a' + 26);
616     else if (*in >= '0' && *in <= '9')
617       base64 = (unsigned)(*in - '0' + 52);
618     else if (*in == '+')
619       base64 = 62;
620     else if (*in == '/')
621       base64 = 63;
622     else if (*in == '=')
623       break;
624     else
625       continue;
626 
627    /*
628     * Store the result in the appropriate chars...
629     */
630 
631     switch (pos)
632     {
633       case 0 :
634           if (outptr < outend)
635             *outptr = (char)(base64 << 2);
636 	  pos ++;
637 	  break;
638       case 1 :
639           if (outptr < outend)
640             *outptr++ |= (char)((base64 >> 4) & 3);
641           if (outptr < outend)
642 	    *outptr = (char)((base64 << 4) & 255);
643 	  pos ++;
644 	  break;
645       case 2 :
646           if (outptr < outend)
647             *outptr++ |= (char)((base64 >> 2) & 15);
648           if (outptr < outend)
649 	    *outptr = (char)((base64 << 6) & 255);
650 	  pos ++;
651 	  break;
652       case 3 :
653           if (outptr < outend)
654             *outptr++ |= (char)base64;
655 	  pos = 0;
656 	  break;
657     }
658   }
659 
660   *outptr = '\0';
661 
662  /*
663   * Return the decoded string and size...
664   */
665 
666   *outlen = (int)(outptr - out);
667 
668   return (out);
669 }
670 
671 
672 /*
673  * 'httpEncode64()' - Base64-encode a string.
674  *
675  * This function is deprecated. Use the httpEncode64_2() function instead
676  * which provides buffer length arguments.
677  *
678  * @deprecated@ @exclude all@
679  */
680 
681 char *					/* O - Encoded string */
httpEncode64(char * out,const char * in)682 httpEncode64(char       *out,		/* I - String to write to */
683              const char *in)		/* I - String to read from */
684 {
685   return (httpEncode64_2(out, 512, in, (int)strlen(in)));
686 }
687 
688 
689 /*
690  * 'httpEncode64_2()' - Base64-encode a string.
691  *
692  * @since CUPS 1.1.21/macOS 10.4@
693  */
694 
695 char *					/* O - Encoded string */
httpEncode64_2(char * out,int outlen,const char * in,int inlen)696 httpEncode64_2(char       *out,		/* I - String to write to */
697 	       int        outlen,	/* I - Maximum size of output string */
698                const char *in,		/* I - String to read from */
699 	       int        inlen)	/* I - Size of input string */
700 {
701   char		*outptr,		/* Output pointer */
702 		*outend;		/* End of output buffer */
703   static const char base64[] =		/* Base64 characters... */
704   		{
705 		  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
706 		  "abcdefghijklmnopqrstuvwxyz"
707 		  "0123456789"
708 		  "+/"
709   		};
710 
711 
712  /*
713   * Range check input...
714   */
715 
716   if (!out || outlen < 1 || !in)
717     return (NULL);
718 
719  /*
720   * Convert bytes to base-64...
721   */
722 
723   for (outptr = out, outend = out + outlen - 1; inlen > 0; in ++, inlen --)
724   {
725    /*
726     * Encode the up to 3 characters as 4 Base64 numbers...
727     */
728 
729     if (outptr < outend)
730       *outptr ++ = base64[(in[0] & 255) >> 2];
731 
732     if (outptr < outend)
733     {
734       if (inlen > 1)
735         *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
736       else
737         *outptr ++ = base64[((in[0] & 255) << 4) & 63];
738     }
739 
740     in ++;
741     inlen --;
742     if (inlen <= 0)
743     {
744       if (outptr < outend)
745         *outptr ++ = '=';
746       if (outptr < outend)
747         *outptr ++ = '=';
748       break;
749     }
750 
751     if (outptr < outend)
752     {
753       if (inlen > 1)
754         *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
755       else
756         *outptr ++ = base64[((in[0] & 255) << 2) & 63];
757     }
758 
759     in ++;
760     inlen --;
761     if (inlen <= 0)
762     {
763       if (outptr < outend)
764         *outptr ++ = '=';
765       break;
766     }
767 
768     if (outptr < outend)
769       *outptr ++ = base64[in[0] & 63];
770   }
771 
772   *outptr = '\0';
773 
774  /*
775   * Return the encoded string...
776   */
777 
778   return (out);
779 }
780 
781 
782 /*
783  * 'httpGetDateString()' - Get a formatted date/time string from a time value.
784  *
785  * @deprecated@ @exclude all@
786  */
787 
788 const char *				/* O - Date/time string */
httpGetDateString(time_t t)789 httpGetDateString(time_t t)		/* I - Time in seconds */
790 {
791   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
792 
793 
794   return (httpGetDateString2(t, cg->http_date, sizeof(cg->http_date)));
795 }
796 
797 
798 /*
799  * 'httpGetDateString2()' - Get a formatted date/time string from a time value.
800  *
801  * @since CUPS 1.2/macOS 10.5@
802  */
803 
804 const char *				/* O - Date/time string */
httpGetDateString2(time_t t,char * s,int slen)805 httpGetDateString2(time_t t,		/* I - Time in seconds */
806                    char   *s,		/* I - String buffer */
807 		   int    slen)		/* I - Size of string buffer */
808 {
809   struct tm	*tdate;			/* UNIX date/time data */
810 
811 
812   tdate = gmtime(&t);
813   if (tdate)
814     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);
815   else
816     s[0] = '\0';
817 
818   return (s);
819 }
820 
821 
822 /*
823  * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
824  */
825 
826 time_t					/* O - Time in seconds */
httpGetDateTime(const char * s)827 httpGetDateTime(const char *s)		/* I - Date/time string */
828 {
829   int		i;			/* Looping var */
830   char		mon[16];		/* Abbreviated month name */
831   int		day, year;		/* Day of month and year */
832   int		hour, min, sec;		/* Time */
833   int		days;			/* Number of days since 1970 */
834   static const int normal_days[] =	/* Days to a month, normal years */
835 		{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
836   static const int leap_days[] =	/* Days to a month, leap years */
837 		{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
838 
839 
840   DEBUG_printf(("2httpGetDateTime(s=\"%s\")", s));
841 
842  /*
843   * Extract the date and time from the formatted string...
844   */
845 
846   if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
847     return (0);
848 
849   DEBUG_printf(("4httpGetDateTime: day=%d, mon=\"%s\", year=%d, hour=%d, "
850                 "min=%d, sec=%d", day, mon, year, hour, min, sec));
851 
852  /*
853   * Convert the month name to a number from 0 to 11.
854   */
855 
856   for (i = 0; i < 12; i ++)
857     if (!_cups_strcasecmp(mon, http_months[i]))
858       break;
859 
860   if (i >= 12)
861     return (0);
862 
863   DEBUG_printf(("4httpGetDateTime: i=%d", i));
864 
865  /*
866   * Now convert the date and time to a UNIX time value in seconds since
867   * 1970.  We can't use mktime() since the timezone may not be UTC but
868   * the date/time string *is* UTC.
869   */
870 
871   if ((year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0))
872     days = leap_days[i] + day - 1;
873   else
874     days = normal_days[i] + day - 1;
875 
876   DEBUG_printf(("4httpGetDateTime: days=%d", days));
877 
878   days += (year - 1970) * 365 +		/* 365 days per year (normally) */
879           ((year - 1) / 4 - 492) -	/* + leap days */
880 	  ((year - 1) / 100 - 19) +	/* - 100 year days */
881           ((year - 1) / 400 - 4);	/* + 400 year days */
882 
883   DEBUG_printf(("4httpGetDateTime: days=%d\n", days));
884 
885   return (days * 86400 + hour * 3600 + min * 60 + sec);
886 }
887 
888 
889 /*
890  * 'httpSeparate()' - Separate a Universal Resource Identifier into its
891  *                    components.
892  *
893  * This function is deprecated; use the httpSeparateURI() function instead.
894  *
895  * @deprecated@ @exclude all@
896  */
897 
898 void
httpSeparate(const char * uri,char * scheme,char * username,char * host,int * port,char * resource)899 httpSeparate(const char *uri,		/* I - Universal Resource Identifier */
900              char       *scheme,	/* O - Scheme [32] (http, https, etc.) */
901 	     char       *username,	/* O - Username [1024] */
902 	     char       *host,		/* O - Hostname [1024] */
903 	     int        *port,		/* O - Port number to use */
904              char       *resource)	/* O - Resource/filename [1024] */
905 {
906   httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username,
907                   HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource,
908 		  HTTP_MAX_URI);
909 }
910 
911 
912 /*
913  * 'httpSeparate2()' - Separate a Universal Resource Identifier into its
914  *                     components.
915  *
916  * This function is deprecated; use the httpSeparateURI() function instead.
917  *
918  * @since CUPS 1.1.21/macOS 10.4@
919  * @deprecated@ @exclude all@
920  */
921 
922 void
httpSeparate2(const char * uri,char * scheme,int schemelen,char * username,int usernamelen,char * host,int hostlen,int * port,char * resource,int resourcelen)923 httpSeparate2(const char *uri,		/* I - Universal Resource Identifier */
924               char       *scheme,	/* O - Scheme (http, https, etc.) */
925 	      int        schemelen,	/* I - Size of scheme buffer */
926 	      char       *username,	/* O - Username */
927 	      int        usernamelen,	/* I - Size of username buffer */
928 	      char       *host,		/* O - Hostname */
929 	      int        hostlen,	/* I - Size of hostname buffer */
930 	      int        *port,		/* O - Port number to use */
931               char       *resource,	/* O - Resource/filename */
932 	      int        resourcelen)	/* I - Size of resource buffer */
933 {
934   httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username,
935                   usernamelen, host, hostlen, port, resource, resourcelen);
936 }
937 
938 
939 /*
940  * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its
941  *                       components.
942  *
943  * @since CUPS 1.2/macOS 10.5@
944  */
945 
946 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)947 httpSeparateURI(
948     http_uri_coding_t decoding,		/* I - Decoding flags */
949     const char        *uri,		/* I - Universal Resource Identifier */
950     char              *scheme,		/* O - Scheme (http, https, etc.) */
951     int               schemelen,	/* I - Size of scheme buffer */
952     char              *username,	/* O - Username */
953     int               usernamelen,	/* I - Size of username buffer */
954     char              *host,		/* O - Hostname */
955     int               hostlen,		/* I - Size of hostname buffer */
956     int               *port,		/* O - Port number to use */
957     char              *resource,	/* O - Resource/filename */
958     int               resourcelen)	/* I - Size of resource buffer */
959 {
960   char			*ptr,		/* Pointer into string... */
961 			*end;		/* End of string */
962   const char		*sep;		/* Separator character */
963   http_uri_status_t	status;		/* Result of separation */
964 
965 
966  /*
967   * Initialize everything to blank...
968   */
969 
970   if (scheme && schemelen > 0)
971     *scheme = '\0';
972 
973   if (username && usernamelen > 0)
974     *username = '\0';
975 
976   if (host && hostlen > 0)
977     *host = '\0';
978 
979   if (port)
980     *port = 0;
981 
982   if (resource && resourcelen > 0)
983     *resource = '\0';
984 
985  /*
986   * Range check input...
987   */
988 
989   if (!uri || !port || !scheme || schemelen <= 0 || !username ||
990       usernamelen <= 0 || !host || hostlen <= 0 || !resource ||
991       resourcelen <= 0)
992     return (HTTP_URI_STATUS_BAD_ARGUMENTS);
993 
994   if (!*uri)
995     return (HTTP_URI_STATUS_BAD_URI);
996 
997  /*
998   * Grab the scheme portion of the URI...
999   */
1000 
1001   status = HTTP_URI_STATUS_OK;
1002 
1003   if (!strncmp(uri, "//", 2))
1004   {
1005    /*
1006     * Workaround for HP IPP client bug...
1007     */
1008 
1009     strlcpy(scheme, "ipp", (size_t)schemelen);
1010     status = HTTP_URI_STATUS_MISSING_SCHEME;
1011   }
1012   else if (*uri == '/')
1013   {
1014    /*
1015     * Filename...
1016     */
1017 
1018     strlcpy(scheme, "file", (size_t)schemelen);
1019     status = HTTP_URI_STATUS_MISSING_SCHEME;
1020   }
1021   else
1022   {
1023    /*
1024     * Standard URI with scheme...
1025     */
1026 
1027     for (ptr = scheme, end = scheme + schemelen - 1;
1028          *uri && *uri != ':' && ptr < end;)
1029       if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1030                  "abcdefghijklmnopqrstuvwxyz"
1031 		 "0123456789-+.", *uri) != NULL)
1032         *ptr++ = *uri++;
1033       else
1034         break;
1035 
1036     *ptr = '\0';
1037 
1038     if (*uri != ':')
1039     {
1040       *scheme = '\0';
1041       return (HTTP_URI_STATUS_BAD_SCHEME);
1042     }
1043 
1044     uri ++;
1045   }
1046 
1047  /*
1048   * Set the default port number...
1049   */
1050 
1051   if (!strcmp(scheme, "http"))
1052     *port = 80;
1053   else if (!strcmp(scheme, "https"))
1054     *port = 443;
1055   else if (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps"))
1056     *port = 631;
1057   else if (!_cups_strcasecmp(scheme, "lpd"))
1058     *port = 515;
1059   else if (!strcmp(scheme, "socket"))	/* Not yet registered with IANA... */
1060     *port = 9100;
1061   else if (strcmp(scheme, "file") && strcmp(scheme, "mailto") && strcmp(scheme, "tel"))
1062     status = HTTP_URI_STATUS_UNKNOWN_SCHEME;
1063 
1064  /*
1065   * Now see if we have a hostname...
1066   */
1067 
1068   if (!strncmp(uri, "//", 2))
1069   {
1070    /*
1071     * Yes, extract it...
1072     */
1073 
1074     uri += 2;
1075 
1076    /*
1077     * Grab the username, if any...
1078     */
1079 
1080     if ((sep = strpbrk(uri, "@/")) != NULL && *sep == '@')
1081     {
1082      /*
1083       * Get a username:password combo...
1084       */
1085 
1086       uri = http_copy_decode(username, uri, usernamelen, "@",
1087                              decoding & HTTP_URI_CODING_USERNAME);
1088 
1089       if (!uri)
1090       {
1091         *username = '\0';
1092         return (HTTP_URI_STATUS_BAD_USERNAME);
1093       }
1094 
1095       uri ++;
1096     }
1097 
1098    /*
1099     * Then the hostname/IP address...
1100     */
1101 
1102     if (*uri == '[')
1103     {
1104      /*
1105       * Grab IPv6 address...
1106       */
1107 
1108       uri ++;
1109       if (*uri == 'v')
1110       {
1111        /*
1112         * Skip IPvFuture ("vXXXX.") prefix...
1113         */
1114 
1115         uri ++;
1116 
1117         while (isxdigit(*uri & 255))
1118           uri ++;
1119 
1120         if (*uri != '.')
1121         {
1122 	  *host = '\0';
1123 	  return (HTTP_URI_STATUS_BAD_HOSTNAME);
1124         }
1125 
1126         uri ++;
1127       }
1128 
1129       uri = http_copy_decode(host, uri, hostlen, "]",
1130                              decoding & HTTP_URI_CODING_HOSTNAME);
1131 
1132       if (!uri)
1133       {
1134         *host = '\0';
1135         return (HTTP_URI_STATUS_BAD_HOSTNAME);
1136       }
1137 
1138      /*
1139       * Validate value...
1140       */
1141 
1142       if (*uri != ']')
1143       {
1144         *host = '\0';
1145         return (HTTP_URI_STATUS_BAD_HOSTNAME);
1146       }
1147 
1148       uri ++;
1149 
1150       for (ptr = host; *ptr; ptr ++)
1151         if (*ptr == '+')
1152 	{
1153 	 /*
1154 	  * Convert zone separator to % and stop here...
1155 	  */
1156 
1157 	  *ptr = '%';
1158 	  break;
1159 	}
1160 	else if (*ptr == '%')
1161 	{
1162 	 /*
1163 	  * Stop at zone separator (RFC 6874)
1164 	  */
1165 
1166 	  break;
1167 	}
1168 	else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255))
1169 	{
1170 	  *host = '\0';
1171 	  return (HTTP_URI_STATUS_BAD_HOSTNAME);
1172 	}
1173     }
1174     else
1175     {
1176      /*
1177       * Validate the hostname or IPv4 address first...
1178       */
1179 
1180       for (ptr = (char *)uri; *ptr; ptr ++)
1181         if (strchr(":?/", *ptr))
1182 	  break;
1183         else if (!strchr("abcdefghijklmnopqrstuvwxyz"	/* unreserved */
1184 			 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"	/* unreserved */
1185 			 "0123456789"			/* unreserved */
1186 	        	 "-._~"				/* unreserved */
1187 			 "%"				/* pct-encoded */
1188 			 "!$&'()*+,;="			/* sub-delims */
1189 			 "\\", *ptr))			/* SMB domain */
1190 	{
1191 	  *host = '\0';
1192 	  return (HTTP_URI_STATUS_BAD_HOSTNAME);
1193 	}
1194 
1195      /*
1196       * Then copy the hostname or IPv4 address to the buffer...
1197       */
1198 
1199       uri = http_copy_decode(host, uri, hostlen, ":?/",
1200                              decoding & HTTP_URI_CODING_HOSTNAME);
1201 
1202       if (!uri)
1203       {
1204         *host = '\0';
1205         return (HTTP_URI_STATUS_BAD_HOSTNAME);
1206       }
1207     }
1208 
1209    /*
1210     * Validate hostname for file scheme - only empty and localhost are
1211     * acceptable.
1212     */
1213 
1214     if (!strcmp(scheme, "file") && strcmp(host, "localhost") && host[0])
1215     {
1216       *host = '\0';
1217       return (HTTP_URI_STATUS_BAD_HOSTNAME);
1218     }
1219 
1220    /*
1221     * See if we have a port number...
1222     */
1223 
1224     if (*uri == ':')
1225     {
1226      /*
1227       * Yes, collect the port number...
1228       */
1229 
1230       if (!isdigit(uri[1] & 255))
1231       {
1232         *port = 0;
1233         return (HTTP_URI_STATUS_BAD_PORT);
1234       }
1235 
1236       *port = (int)strtol(uri + 1, (char **)&uri, 10);
1237 
1238       if (*port <= 0 || *port > 65535)
1239       {
1240         *port = 0;
1241         return (HTTP_URI_STATUS_BAD_PORT);
1242       }
1243 
1244       if (*uri != '/' && *uri)
1245       {
1246         *port = 0;
1247         return (HTTP_URI_STATUS_BAD_PORT);
1248       }
1249     }
1250   }
1251 
1252  /*
1253   * The remaining portion is the resource string...
1254   */
1255 
1256   if (*uri == '?' || !*uri)
1257   {
1258    /*
1259     * Hostname but no path...
1260     */
1261 
1262     status    = HTTP_URI_STATUS_MISSING_RESOURCE;
1263     *resource = '/';
1264 
1265    /*
1266     * Copy any query string...
1267     */
1268 
1269     if (*uri == '?')
1270       uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL,
1271                              decoding & HTTP_URI_CODING_QUERY);
1272     else
1273       resource[1] = '\0';
1274   }
1275   else
1276   {
1277     uri = http_copy_decode(resource, uri, resourcelen, "?",
1278                            decoding & HTTP_URI_CODING_RESOURCE);
1279 
1280     if (uri && *uri == '?')
1281     {
1282      /*
1283       * Concatenate any query string...
1284       */
1285 
1286       char *resptr = resource + strlen(resource);
1287 
1288       uri = http_copy_decode(resptr, uri,
1289                              resourcelen - (int)(resptr - resource), NULL,
1290                              decoding & HTTP_URI_CODING_QUERY);
1291     }
1292   }
1293 
1294   if (!uri)
1295   {
1296     *resource = '\0';
1297     return (HTTP_URI_STATUS_BAD_RESOURCE);
1298   }
1299 
1300  /*
1301   * Return the URI separation status...
1302   */
1303 
1304   return (status);
1305 }
1306 
1307 
1308 /*
1309  * 'httpStateString()' - Return the string describing a HTTP state value.
1310  *
1311  * @since CUPS 2.0/OS 10.10@
1312  */
1313 
1314 const char *				/* O - State string */
httpStateString(http_state_t state)1315 httpStateString(http_state_t state)	/* I - HTTP state value */
1316 {
1317   if (state < HTTP_STATE_ERROR || state > HTTP_STATE_UNKNOWN_VERSION)
1318     return ("HTTP_STATE_???");
1319   else
1320     return (http_states[state - HTTP_STATE_ERROR]);
1321 }
1322 
1323 
1324 /*
1325  * '_httpStatus()' - Return the localized string describing a HTTP status code.
1326  *
1327  * The returned string is localized using the passed message catalog.
1328  */
1329 
1330 const char *				/* O - Localized status string */
_httpStatus(cups_lang_t * lang,http_status_t status)1331 _httpStatus(cups_lang_t   *lang,	/* I - Language */
1332             http_status_t status)	/* I - HTTP status code */
1333 {
1334   const char	*s;			/* Status string */
1335 
1336 
1337   switch (status)
1338   {
1339     case HTTP_STATUS_ERROR :
1340         s = strerror(errno);
1341         break;
1342     case HTTP_STATUS_CONTINUE :
1343         s = _("Continue");
1344 	break;
1345     case HTTP_STATUS_SWITCHING_PROTOCOLS :
1346         s = _("Switching Protocols");
1347 	break;
1348     case HTTP_STATUS_OK :
1349         s = _("OK");
1350 	break;
1351     case HTTP_STATUS_CREATED :
1352         s = _("Created");
1353 	break;
1354     case HTTP_STATUS_ACCEPTED :
1355         s = _("Accepted");
1356 	break;
1357     case HTTP_STATUS_NO_CONTENT :
1358         s = _("No Content");
1359 	break;
1360     case HTTP_STATUS_MOVED_PERMANENTLY :
1361         s = _("Moved Permanently");
1362 	break;
1363     case HTTP_STATUS_SEE_OTHER :
1364         s = _("See Other");
1365 	break;
1366     case HTTP_STATUS_NOT_MODIFIED :
1367         s = _("Not Modified");
1368 	break;
1369     case HTTP_STATUS_BAD_REQUEST :
1370         s = _("Bad Request");
1371 	break;
1372     case HTTP_STATUS_UNAUTHORIZED :
1373     case HTTP_STATUS_CUPS_AUTHORIZATION_CANCELED :
1374         s = _("Unauthorized");
1375 	break;
1376     case HTTP_STATUS_FORBIDDEN :
1377         s = _("Forbidden");
1378 	break;
1379     case HTTP_STATUS_NOT_FOUND :
1380         s = _("Not Found");
1381 	break;
1382     case HTTP_STATUS_REQUEST_TOO_LARGE :
1383         s = _("Request Entity Too Large");
1384 	break;
1385     case HTTP_STATUS_URI_TOO_LONG :
1386         s = _("URI Too Long");
1387 	break;
1388     case HTTP_STATUS_UPGRADE_REQUIRED :
1389         s = _("Upgrade Required");
1390 	break;
1391     case HTTP_STATUS_NOT_IMPLEMENTED :
1392         s = _("Not Implemented");
1393 	break;
1394     case HTTP_STATUS_NOT_SUPPORTED :
1395         s = _("Not Supported");
1396 	break;
1397     case HTTP_STATUS_EXPECTATION_FAILED :
1398         s = _("Expectation Failed");
1399 	break;
1400     case HTTP_STATUS_SERVICE_UNAVAILABLE :
1401         s = _("Service Unavailable");
1402 	break;
1403     case HTTP_STATUS_SERVER_ERROR :
1404         s = _("Internal Server Error");
1405 	break;
1406     case HTTP_STATUS_CUPS_PKI_ERROR :
1407         s = _("SSL/TLS Negotiation Error");
1408 	break;
1409     case HTTP_STATUS_CUPS_WEBIF_DISABLED :
1410         s = _("Web Interface is Disabled");
1411 	break;
1412 
1413     default :
1414         s = _("Unknown");
1415 	break;
1416   }
1417 
1418   return (_cupsLangString(lang, s));
1419 }
1420 
1421 
1422 /*
1423  * 'httpStatus()' - Return a short string describing a HTTP status code.
1424  *
1425  * The returned string is localized to the current POSIX locale and is based
1426  * on the status strings defined in RFC 7231.
1427  */
1428 
1429 const char *				/* O - Localized status string */
httpStatus(http_status_t status)1430 httpStatus(http_status_t status)	/* I - HTTP status code */
1431 {
1432   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
1433 
1434 
1435   if (!cg->lang_default)
1436     cg->lang_default = cupsLangDefault();
1437 
1438   return (_httpStatus(cg->lang_default, status));
1439 }
1440 
1441 /*
1442  * 'httpURIStatusString()' - Return a string describing a URI status code.
1443  *
1444  * @since CUPS 2.0/OS 10.10@
1445  */
1446 
1447 const char *				/* O - Localized status string */
httpURIStatusString(http_uri_status_t status)1448 httpURIStatusString(
1449     http_uri_status_t status)		/* I - URI status code */
1450 {
1451   const char	*s;			/* Status string */
1452   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
1453 
1454 
1455   if (!cg->lang_default)
1456     cg->lang_default = cupsLangDefault();
1457 
1458   switch (status)
1459   {
1460     case HTTP_URI_STATUS_OVERFLOW :
1461 	s = _("URI too large");
1462 	break;
1463     case HTTP_URI_STATUS_BAD_ARGUMENTS :
1464 	s = _("Bad arguments to function");
1465 	break;
1466     case HTTP_URI_STATUS_BAD_RESOURCE :
1467 	s = _("Bad resource in URI");
1468 	break;
1469     case HTTP_URI_STATUS_BAD_PORT :
1470 	s = _("Bad port number in URI");
1471 	break;
1472     case HTTP_URI_STATUS_BAD_HOSTNAME :
1473 	s = _("Bad hostname/address in URI");
1474 	break;
1475     case HTTP_URI_STATUS_BAD_USERNAME :
1476 	s = _("Bad username in URI");
1477 	break;
1478     case HTTP_URI_STATUS_BAD_SCHEME :
1479 	s = _("Bad scheme in URI");
1480 	break;
1481     case HTTP_URI_STATUS_BAD_URI :
1482 	s = _("Bad/empty URI");
1483 	break;
1484     case HTTP_URI_STATUS_OK :
1485 	s = _("OK");
1486 	break;
1487     case HTTP_URI_STATUS_MISSING_SCHEME :
1488 	s = _("Missing scheme in URI");
1489 	break;
1490     case HTTP_URI_STATUS_UNKNOWN_SCHEME :
1491 	s = _("Unknown scheme in URI");
1492 	break;
1493     case HTTP_URI_STATUS_MISSING_RESOURCE :
1494 	s = _("Missing resource in URI");
1495 	break;
1496 
1497     default:
1498         s = _("Unknown");
1499 	break;
1500   }
1501 
1502   return (_cupsLangString(cg->lang_default, s));
1503 }
1504 
1505 
1506 #ifndef HAVE_HSTRERROR
1507 /*
1508  * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others.
1509  */
1510 
1511 const char *				/* O - Error string */
_cups_hstrerror(int error)1512 _cups_hstrerror(int error)		/* I - Error number */
1513 {
1514   static const char * const errors[] =	/* Error strings */
1515 		{
1516 		  "OK",
1517 		  "Host not found.",
1518 		  "Try again.",
1519 		  "Unrecoverable lookup error.",
1520 		  "No data associated with name."
1521 		};
1522 
1523 
1524   if (error < 0 || error > 4)
1525     return ("Unknown hostname lookup error.");
1526   else
1527     return (errors[error]);
1528 }
1529 #endif /* !HAVE_HSTRERROR */
1530 
1531 
1532 /*
1533  * '_httpDecodeURI()' - Percent-decode a HTTP request URI.
1534  */
1535 
1536 char *					/* O - Decoded URI or NULL on error */
_httpDecodeURI(char * dst,const char * src,size_t dstsize)1537 _httpDecodeURI(char       *dst,		/* I - Destination buffer */
1538                const char *src,		/* I - Source URI */
1539 	       size_t     dstsize)	/* I - Size of destination buffer */
1540 {
1541   if (http_copy_decode(dst, src, (int)dstsize, NULL, 1))
1542     return (dst);
1543   else
1544     return (NULL);
1545 }
1546 
1547 
1548 /*
1549  * '_httpEncodeURI()' - Percent-encode a HTTP request URI.
1550  */
1551 
1552 char *					/* O - Encoded URI */
_httpEncodeURI(char * dst,const char * src,size_t dstsize)1553 _httpEncodeURI(char       *dst,		/* I - Destination buffer */
1554                const char *src,		/* I - Source URI */
1555 	       size_t     dstsize)	/* I - Size of destination buffer */
1556 {
1557   http_copy_encode(dst, src, dst + dstsize - 1, NULL, NULL, 1);
1558   return (dst);
1559 }
1560 
1561 
1562 /*
1563  * '_httpResolveURI()' - Resolve a DNS-SD URI.
1564  */
1565 
1566 const char *				/* O - Resolved URI */
_httpResolveURI(const char * uri,char * resolved_uri,size_t resolved_size,int options,int (* cb)(void * context),void * context)1567 _httpResolveURI(
1568     const char *uri,			/* I - DNS-SD URI */
1569     char       *resolved_uri,		/* I - Buffer for resolved URI */
1570     size_t     resolved_size,		/* I - Size of URI buffer */
1571     int        options,			/* I - Resolve options */
1572     int        (*cb)(void *context),	/* I - Continue callback function */
1573     void       *context)		/* I - Context pointer for callback */
1574 {
1575   char			scheme[32],	/* URI components... */
1576 			userpass[256],
1577 			hostname[1024],
1578 			resource[1024];
1579   int			port;
1580 #ifdef DEBUG
1581   http_uri_status_t	status;		/* URI decode status */
1582 #endif /* DEBUG */
1583 
1584 
1585   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));
1586 
1587  /*
1588   * Get the device URI...
1589   */
1590 
1591 #ifdef DEBUG
1592   if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1593                                 sizeof(scheme), userpass, sizeof(userpass),
1594 				hostname, sizeof(hostname), &port, resource,
1595 				sizeof(resource))) < HTTP_URI_STATUS_OK)
1596 #else
1597   if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1598 		      sizeof(scheme), userpass, sizeof(userpass),
1599 		      hostname, sizeof(hostname), &port, resource,
1600 		      sizeof(resource)) < HTTP_URI_STATUS_OK)
1601 #endif /* DEBUG */
1602   {
1603     if (options & _HTTP_RESOLVE_STDERR)
1604       _cupsLangPrintFilter(stderr, "ERROR", _("Bad device-uri \"%s\"."), uri);
1605 
1606     DEBUG_printf(("2_httpResolveURI: httpSeparateURI returned %d!", status));
1607     DEBUG_puts("2_httpResolveURI: Returning NULL");
1608     return (NULL);
1609   }
1610 
1611  /*
1612   * Resolve it as needed...
1613   */
1614 
1615   if (strstr(hostname, "._tcp"))
1616   {
1617 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1618     char		*regtype,	/* Pointer to type in hostname */
1619 			*domain,	/* Pointer to domain in hostname */
1620 			*uuid,		/* Pointer to UUID in URI */
1621 			*uuidend;	/* Pointer to end of UUID in URI */
1622     _http_uribuf_t	uribuf;		/* URI buffer */
1623     int			offline = 0;	/* offline-report state set? */
1624 #  ifdef HAVE_DNSSD
1625 #    ifdef WIN32
1626 #      pragma comment(lib, "dnssd.lib")
1627 #    endif /* WIN32 */
1628     DNSServiceRef	ref,		/* DNS-SD master service reference */
1629 			domainref = NULL,/* DNS-SD service reference for domain */
1630 			ippref = NULL,	/* DNS-SD service reference for network IPP */
1631 			ippsref = NULL,	/* DNS-SD service reference for network IPPS */
1632 			localref;	/* DNS-SD service reference for .local */
1633     int			extrasent = 0;	/* Send the domain/IPP/IPPS resolves? */
1634 #    ifdef HAVE_POLL
1635     struct pollfd	polldata;	/* Polling data */
1636 #    else /* select() */
1637     fd_set		input_set;	/* Input set for select() */
1638     struct timeval	stimeout;	/* Timeout value for select() */
1639 #    endif /* HAVE_POLL */
1640 #  elif defined(HAVE_AVAHI)
1641     AvahiClient		*client;	/* Client information */
1642     int			error;		/* Status */
1643 #  endif /* HAVE_DNSSD */
1644 
1645     if (options & _HTTP_RESOLVE_STDERR)
1646       fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname);
1647 
1648    /*
1649     * Separate the hostname into service name, registration type, and domain...
1650     */
1651 
1652     for (regtype = strstr(hostname, "._tcp") - 2;
1653          regtype > hostname;
1654 	 regtype --)
1655       if (regtype[0] == '.' && regtype[1] == '_')
1656       {
1657        /*
1658         * Found ._servicetype in front of ._tcp...
1659 	*/
1660 
1661         *regtype++ = '\0';
1662 	break;
1663       }
1664 
1665     if (regtype <= hostname)
1666     {
1667       DEBUG_puts("2_httpResolveURI: Bad hostname, returning NULL");
1668       return (NULL);
1669     }
1670 
1671     for (domain = strchr(regtype, '.');
1672          domain;
1673 	 domain = strchr(domain + 1, '.'))
1674       if (domain[1] != '_')
1675         break;
1676 
1677     if (domain)
1678       *domain++ = '\0';
1679 
1680     if ((uuid = strstr(resource, "?uuid=")) != NULL)
1681     {
1682       *uuid = '\0';
1683       uuid  += 6;
1684       if ((uuidend = strchr(uuid, '&')) != NULL)
1685         *uuidend = '\0';
1686     }
1687 
1688     resolved_uri[0] = '\0';
1689 
1690     uribuf.buffer   = resolved_uri;
1691     uribuf.bufsize  = resolved_size;
1692     uribuf.options  = options;
1693     uribuf.resource = resource;
1694     uribuf.uuid     = uuid;
1695 
1696     DEBUG_printf(("2_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", "
1697                   "domain=\"%s\"\n", hostname, regtype, domain));
1698     if (options & _HTTP_RESOLVE_STDERR)
1699     {
1700       fputs("STATE: +connecting-to-device\n", stderr);
1701       fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1702                       "domain=\"local.\"...\n", hostname, regtype);
1703     }
1704 
1705     uri = NULL;
1706 
1707 #  ifdef HAVE_DNSSD
1708     if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError)
1709     {
1710       uint32_t myinterface = kDNSServiceInterfaceIndexAny;
1711 					/* Lookup on any interface */
1712 
1713       if (!strcmp(scheme, "ippusb"))
1714         myinterface = kDNSServiceInterfaceIndexLocalOnly;
1715 
1716       localref = ref;
1717       if (DNSServiceResolve(&localref,
1718                             kDNSServiceFlagsShareConnection, myinterface,
1719                             hostname, regtype, "local.", http_resolve_cb,
1720 			    &uribuf) == kDNSServiceErr_NoError)
1721       {
1722 	int	fds;			/* Number of ready descriptors */
1723 	time_t	timeout,		/* Poll timeout */
1724 		start_time = time(NULL),/* Start time */
1725 		end_time = start_time + 90;
1726 					/* End time */
1727 
1728 	while (time(NULL) < end_time)
1729 	{
1730 	  if (options & _HTTP_RESOLVE_STDERR)
1731 	    _cupsLangPrintFilter(stderr, "INFO", _("Looking for printer."));
1732 
1733 	  if (cb && !(*cb)(context))
1734 	  {
1735 	    DEBUG_puts("2_httpResolveURI: callback returned 0 (stop)");
1736 	    break;
1737 	  }
1738 
1739 	 /*
1740 	  * Wakeup every 2 seconds to emit a "looking for printer" message...
1741 	  */
1742 
1743 	  if ((timeout = end_time - time(NULL)) > 2)
1744 	    timeout = 2;
1745 
1746 #    ifdef HAVE_POLL
1747 	  polldata.fd     = DNSServiceRefSockFD(ref);
1748 	  polldata.events = POLLIN;
1749 
1750 	  fds = poll(&polldata, 1, (int)(1000 * timeout));
1751 
1752 #    else /* select() */
1753 	  FD_ZERO(&input_set);
1754 	  FD_SET(DNSServiceRefSockFD(ref), &input_set);
1755 
1756 #      ifdef WIN32
1757 	  stimeout.tv_sec  = (long)timeout;
1758 #      else
1759 	  stimeout.tv_sec  = timeout;
1760 #      endif /* WIN32 */
1761 	  stimeout.tv_usec = 0;
1762 
1763 	  fds = select(DNSServiceRefSockFD(ref)+1, &input_set, NULL, NULL,
1764 		       &stimeout);
1765 #    endif /* HAVE_POLL */
1766 
1767 	  if (fds < 0)
1768 	  {
1769 	    if (errno != EINTR && errno != EAGAIN)
1770 	    {
1771 	      DEBUG_printf(("2_httpResolveURI: poll error: %s", strerror(errno)));
1772 	      break;
1773 	    }
1774 	  }
1775 	  else if (fds == 0)
1776 	  {
1777 	   /*
1778 	    * Wait 2 seconds for a response to the local resolve; if nothing
1779 	    * comes in, do an additional domain resolution...
1780 	    */
1781 
1782 	    if (extrasent == 0 && domain && _cups_strcasecmp(domain, "local."))
1783 	    {
1784 	      if (options & _HTTP_RESOLVE_STDERR)
1785 		fprintf(stderr,
1786 		        "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1787 			"domain=\"%s\"...\n", hostname, regtype,
1788 			domain ? domain : "");
1789 
1790 	      domainref = ref;
1791 	      if (DNSServiceResolve(&domainref,
1792 	                            kDNSServiceFlagsShareConnection,
1793 	                            myinterface, hostname, regtype, domain,
1794 				    http_resolve_cb,
1795 				    &uribuf) == kDNSServiceErr_NoError)
1796 		extrasent = 1;
1797 	    }
1798 	    else if (extrasent == 0 && !strcmp(scheme, "ippusb"))
1799 	    {
1800 	      if (options & _HTTP_RESOLVE_STDERR)
1801 		fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"_ipps._tcp\", domain=\"local.\"...\n", hostname);
1802 
1803 	      ippsref = ref;
1804 	      if (DNSServiceResolve(&ippsref,
1805 	                            kDNSServiceFlagsShareConnection,
1806 	                            kDNSServiceInterfaceIndexAny, hostname,
1807 	                            "_ipps._tcp", domain, http_resolve_cb,
1808 				    &uribuf) == kDNSServiceErr_NoError)
1809 		extrasent = 1;
1810 	    }
1811 	    else if (extrasent == 1 && !strcmp(scheme, "ippusb"))
1812 	    {
1813 	      if (options & _HTTP_RESOLVE_STDERR)
1814 		fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"_ipp._tcp\", domain=\"local.\"...\n", hostname);
1815 
1816 	      ippref = ref;
1817 	      if (DNSServiceResolve(&ippref,
1818 	                            kDNSServiceFlagsShareConnection,
1819 	                            kDNSServiceInterfaceIndexAny, hostname,
1820 	                            "_ipp._tcp", domain, http_resolve_cb,
1821 				    &uribuf) == kDNSServiceErr_NoError)
1822 		extrasent = 2;
1823 	    }
1824 
1825 	   /*
1826 	    * If it hasn't resolved within 5 seconds set the offline-report
1827 	    * printer-state-reason...
1828 	    */
1829 
1830 	    if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 &&
1831 	        time(NULL) > (start_time + 5))
1832 	    {
1833 	      fputs("STATE: +offline-report\n", stderr);
1834 	      offline = 1;
1835 	    }
1836 	  }
1837 	  else
1838 	  {
1839 	    if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError &&
1840 	        resolved_uri[0])
1841 	    {
1842 	      uri = resolved_uri;
1843 	      break;
1844 	    }
1845 	  }
1846 	}
1847 
1848 	if (extrasent)
1849 	{
1850 	  if (domainref)
1851 	    DNSServiceRefDeallocate(domainref);
1852 	  if (ippref)
1853 	    DNSServiceRefDeallocate(ippref);
1854 	  if (ippsref)
1855 	    DNSServiceRefDeallocate(ippsref);
1856 	}
1857 
1858 	DNSServiceRefDeallocate(localref);
1859       }
1860 
1861       DNSServiceRefDeallocate(ref);
1862     }
1863 #  else /* HAVE_AVAHI */
1864     if ((uribuf.poll = avahi_simple_poll_new()) != NULL)
1865     {
1866       avahi_simple_poll_set_func(uribuf.poll, http_poll_cb, NULL);
1867 
1868       if ((client = avahi_client_new(avahi_simple_poll_get(uribuf.poll),
1869 				      0, http_client_cb,
1870 				      &uribuf, &error)) != NULL)
1871       {
1872 	if (avahi_service_resolver_new(client, AVAHI_IF_UNSPEC,
1873 				       AVAHI_PROTO_UNSPEC, hostname,
1874 				       regtype, "local.", AVAHI_PROTO_UNSPEC, 0,
1875 				       http_resolve_cb, &uribuf) != NULL)
1876 	{
1877 	  time_t	start_time = time(NULL),
1878 	  				/* Start time */
1879 			end_time = start_time + 90;
1880 					/* End time */
1881           int           pstatus;	/* Poll status */
1882 
1883 	  pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000);
1884 
1885 	  if (pstatus == 0 && !resolved_uri[0] && domain &&
1886 	      _cups_strcasecmp(domain, "local."))
1887 	  {
1888 	   /*
1889 	    * Resolve for .local hasn't returned anything, try the listed
1890 	    * domain...
1891 	    */
1892 
1893 	    avahi_service_resolver_new(client, AVAHI_IF_UNSPEC,
1894 				       AVAHI_PROTO_UNSPEC, hostname,
1895 				       regtype, domain, AVAHI_PROTO_UNSPEC, 0,
1896 				       http_resolve_cb, &uribuf);
1897           }
1898 
1899 	  while (!pstatus && !resolved_uri[0] && time(NULL) < end_time)
1900           {
1901   	    if ((pstatus = avahi_simple_poll_iterate(uribuf.poll, 2000)) != 0)
1902   	      break;
1903 
1904 	   /*
1905 	    * If it hasn't resolved within 5 seconds set the offline-report
1906 	    * printer-state-reason...
1907 	    */
1908 
1909 	    if ((options & _HTTP_RESOLVE_STDERR) && offline == 0 &&
1910 	        time(NULL) > (start_time + 5))
1911 	    {
1912 	      fputs("STATE: +offline-report\n", stderr);
1913 	      offline = 1;
1914 	    }
1915           }
1916 
1917 	 /*
1918 	  * Collect the result (if we got one).
1919 	  */
1920 
1921 	  if (resolved_uri[0])
1922 	    uri = resolved_uri;
1923 	}
1924 
1925 	avahi_client_free(client);
1926       }
1927 
1928       avahi_simple_poll_free(uribuf.poll);
1929     }
1930 #  endif /* HAVE_DNSSD */
1931 
1932     if (options & _HTTP_RESOLVE_STDERR)
1933     {
1934       if (uri)
1935       {
1936         fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri);
1937 	fputs("STATE: -connecting-to-device,offline-report\n", stderr);
1938       }
1939       else
1940       {
1941         fputs("DEBUG: Unable to resolve URI\n", stderr);
1942 	fputs("STATE: -connecting-to-device\n", stderr);
1943       }
1944     }
1945 
1946 #else /* HAVE_DNSSD || HAVE_AVAHI */
1947    /*
1948     * No DNS-SD support...
1949     */
1950 
1951     uri = NULL;
1952 #endif /* HAVE_DNSSD || HAVE_AVAHI */
1953 
1954     if ((options & _HTTP_RESOLVE_STDERR) && !uri)
1955       _cupsLangPrintFilter(stderr, "INFO", _("Unable to find printer."));
1956   }
1957   else
1958   {
1959    /*
1960     * Nothing more to do...
1961     */
1962 
1963     strlcpy(resolved_uri, uri, resolved_size);
1964     uri = resolved_uri;
1965   }
1966 
1967   DEBUG_printf(("2_httpResolveURI: Returning \"%s\"", uri));
1968 
1969   return (uri);
1970 }
1971 
1972 
1973 #ifdef HAVE_AVAHI
1974 /*
1975  * 'http_client_cb()' - Client callback for resolving URI.
1976  */
1977 
1978 static void
http_client_cb(AvahiClient * client,AvahiClientState state,void * context)1979 http_client_cb(
1980     AvahiClient      *client,		/* I - Client information */
1981     AvahiClientState state,		/* I - Current state */
1982     void             *context)		/* I - Pointer to URI buffer */
1983 {
1984   DEBUG_printf(("7http_client_cb(client=%p, state=%d, context=%p)", client,
1985                 state, context));
1986 
1987  /*
1988   * If the connection drops, quit.
1989   */
1990 
1991   if (state == AVAHI_CLIENT_FAILURE)
1992   {
1993     _http_uribuf_t *uribuf = (_http_uribuf_t *)context;
1994 					/* URI buffer */
1995 
1996     avahi_simple_poll_quit(uribuf->poll);
1997   }
1998 }
1999 #endif /* HAVE_AVAHI */
2000 
2001 
2002 /*
2003  * 'http_copy_decode()' - Copy and decode a URI.
2004  */
2005 
2006 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)2007 http_copy_decode(char       *dst,	/* O - Destination buffer */
2008                  const char *src,	/* I - Source pointer */
2009 		 int        dstsize,	/* I - Destination size */
2010 		 const char *term,	/* I - Terminating characters */
2011 		 int        decode)	/* I - Decode %-encoded values */
2012 {
2013   char	*ptr,				/* Pointer into buffer */
2014 	*end;				/* End of buffer */
2015   int	quoted;				/* Quoted character */
2016 
2017 
2018  /*
2019   * Copy the src to the destination until we hit a terminating character
2020   * or the end of the string.
2021   */
2022 
2023   for (ptr = dst, end = dst + dstsize - 1;
2024        *src && (!term || !strchr(term, *src));
2025        src ++)
2026     if (ptr < end)
2027     {
2028       if (*src == '%' && decode)
2029       {
2030         if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255))
2031 	{
2032 	 /*
2033 	  * Grab a hex-encoded character...
2034 	  */
2035 
2036           src ++;
2037 	  if (isalpha(*src))
2038 	    quoted = (tolower(*src) - 'a' + 10) << 4;
2039 	  else
2040 	    quoted = (*src - '0') << 4;
2041 
2042           src ++;
2043 	  if (isalpha(*src))
2044 	    quoted |= tolower(*src) - 'a' + 10;
2045 	  else
2046 	    quoted |= *src - '0';
2047 
2048           *ptr++ = (char)quoted;
2049 	}
2050 	else
2051 	{
2052 	 /*
2053 	  * Bad hex-encoded character...
2054 	  */
2055 
2056 	  *ptr = '\0';
2057 	  return (NULL);
2058 	}
2059       }
2060       else if ((*src & 255) <= 0x20 || (*src & 255) >= 0x7f)
2061       {
2062         *ptr = '\0';
2063         return (NULL);
2064       }
2065       else
2066 	*ptr++ = *src;
2067     }
2068 
2069   *ptr = '\0';
2070 
2071   return (src);
2072 }
2073 
2074 
2075 /*
2076  * 'http_copy_encode()' - Copy and encode a URI.
2077  */
2078 
2079 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)2080 http_copy_encode(char       *dst,	/* O - Destination buffer */
2081                  const char *src,	/* I - Source pointer */
2082 		 char       *dstend,	/* I - End of destination buffer */
2083                  const char *reserved,	/* I - Extra reserved characters */
2084 		 const char *term,	/* I - Terminating characters */
2085 		 int        encode)	/* I - %-encode reserved chars? */
2086 {
2087   static const char hex[] = "0123456789ABCDEF";
2088 
2089 
2090   while (*src && dst < dstend)
2091   {
2092     if (term && *src == *term)
2093       return (dst);
2094 
2095     if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
2096                    (reserved && strchr(reserved, *src))))
2097     {
2098      /*
2099       * Hex encode reserved characters...
2100       */
2101 
2102       if ((dst + 2) >= dstend)
2103         break;
2104 
2105       *dst++ = '%';
2106       *dst++ = hex[(*src >> 4) & 15];
2107       *dst++ = hex[*src & 15];
2108 
2109       src ++;
2110     }
2111     else
2112       *dst++ = *src++;
2113   }
2114 
2115   *dst = '\0';
2116 
2117   if (*src)
2118     return (NULL);
2119   else
2120     return (dst);
2121 }
2122 
2123 
2124 #ifdef HAVE_DNSSD
2125 /*
2126  * 'http_resolve_cb()' - Build a device URI for the given service name.
2127  */
2128 
2129 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)2130 http_resolve_cb(
2131     DNSServiceRef       sdRef,		/* I - Service reference */
2132     DNSServiceFlags     flags,		/* I - Results flags */
2133     uint32_t            interfaceIndex,	/* I - Interface number */
2134     DNSServiceErrorType errorCode,	/* I - Error, if any */
2135     const char          *fullName,	/* I - Full service name */
2136     const char          *hostTarget,	/* I - Hostname */
2137     uint16_t            port,		/* I - Port number */
2138     uint16_t            txtLen,		/* I - Length of TXT record */
2139     const unsigned char *txtRecord,	/* I - TXT record data */
2140     void                *context)	/* I - Pointer to URI buffer */
2141 {
2142   _http_uribuf_t	*uribuf = (_http_uribuf_t *)context;
2143 					/* URI buffer */
2144   const char		*scheme,	/* URI scheme */
2145 			*hostptr,	/* Pointer into hostTarget */
2146 			*reskey,	/* "rp" or "rfo" */
2147 			*resdefault;	/* Default path */
2148   char			resource[257],	/* Remote path */
2149 			fqdn[256];	/* FQDN of the .local name */
2150   const void		*value;		/* Value from TXT record */
2151   uint8_t		valueLen;	/* Length of value */
2152 
2153 
2154   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));
2155 
2156  /*
2157   * If we have a UUID, compare it...
2158   */
2159 
2160   if (uribuf->uuid &&
2161       (value = TXTRecordGetValuePtr(txtLen, txtRecord, "UUID",
2162                                     &valueLen)) != NULL)
2163   {
2164     char	uuid[256];		/* UUID value */
2165 
2166     memcpy(uuid, value, valueLen);
2167     uuid[valueLen] = '\0';
2168 
2169     if (_cups_strcasecmp(uuid, uribuf->uuid))
2170     {
2171       if (uribuf->options & _HTTP_RESOLVE_STDERR)
2172 	fprintf(stderr, "DEBUG: Found UUID %s, looking for %s.", uuid,
2173 		uribuf->uuid);
2174 
2175       DEBUG_printf(("5http_resolve_cb: Found UUID %s, looking for %s.", uuid,
2176                     uribuf->uuid));
2177       return;
2178     }
2179   }
2180 
2181  /*
2182   * Figure out the scheme from the full name...
2183   */
2184 
2185   if (strstr(fullName, "._ipps") || strstr(fullName, "._ipp-tls"))
2186     scheme = "ipps";
2187   else if (strstr(fullName, "._ipp") || strstr(fullName, "._fax-ipp"))
2188     scheme = "ipp";
2189   else if (strstr(fullName, "._http."))
2190     scheme = "http";
2191   else if (strstr(fullName, "._https."))
2192     scheme = "https";
2193   else if (strstr(fullName, "._printer."))
2194     scheme = "lpd";
2195   else if (strstr(fullName, "._pdl-datastream."))
2196     scheme = "socket";
2197   else
2198     scheme = "riousbprint";
2199 
2200  /*
2201   * Extract the "remote printer" key from the TXT record...
2202   */
2203 
2204   if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) &&
2205       (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
2206       !TXTRecordGetValuePtr(txtLen, txtRecord, "printer-type", &valueLen))
2207   {
2208     reskey     = "rfo";
2209     resdefault = "/ipp/faxout";
2210   }
2211   else
2212   {
2213     reskey     = "rp";
2214     resdefault = "/";
2215   }
2216 
2217   if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, reskey,
2218                                     &valueLen)) != NULL)
2219   {
2220     if (((char *)value)[0] == '/')
2221     {
2222      /*
2223       * Value (incorrectly) has a leading slash already...
2224       */
2225 
2226       memcpy(resource, value, valueLen);
2227       resource[valueLen] = '\0';
2228     }
2229     else
2230     {
2231      /*
2232       * Convert to resource by concatenating with a leading "/"...
2233       */
2234 
2235       resource[0] = '/';
2236       memcpy(resource + 1, value, valueLen);
2237       resource[valueLen + 1] = '\0';
2238     }
2239   }
2240   else
2241   {
2242    /*
2243     * Use the default value...
2244     */
2245 
2246     strlcpy(resource, resdefault, sizeof(resource));
2247   }
2248 
2249  /*
2250   * Lookup the FQDN if needed...
2251   */
2252 
2253   if ((uribuf->options & _HTTP_RESOLVE_FQDN) &&
2254       (hostptr = hostTarget + strlen(hostTarget) - 7) > hostTarget &&
2255       !_cups_strcasecmp(hostptr, ".local."))
2256   {
2257    /*
2258     * OK, we got a .local name but the caller needs a real domain.  Start by
2259     * getting the IP address of the .local name and then do reverse-lookups...
2260     */
2261 
2262     http_addrlist_t	*addrlist,	/* List of addresses */
2263 			*addr;		/* Current address */
2264 
2265     DEBUG_printf(("5http_resolve_cb: Looking up \"%s\".", hostTarget));
2266 
2267     snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port));
2268     if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL)
2269     {
2270       for (addr = addrlist; addr; addr = addr->next)
2271       {
2272         int error = getnameinfo(&(addr->addr.addr), (socklen_t)httpAddrLength(&(addr->addr)), fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD);
2273 
2274         if (!error)
2275 	{
2276 	  DEBUG_printf(("5http_resolve_cb: Found \"%s\".", fqdn));
2277 
2278 	  if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn ||
2279 	      _cups_strcasecmp(hostptr, ".local"))
2280 	  {
2281 	    hostTarget = fqdn;
2282 	    break;
2283 	  }
2284 	}
2285 #ifdef DEBUG
2286 	else
2287 	  DEBUG_printf(("5http_resolve_cb: \"%s\" did not resolve: %d",
2288 	                httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)),
2289 			error));
2290 #endif /* DEBUG */
2291       }
2292 
2293       httpAddrFreeList(addrlist);
2294     }
2295   }
2296 
2297  /*
2298   * Assemble the final device URI...
2299   */
2300 
2301   if ((!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
2302       !strcmp(uribuf->resource, "/cups"))
2303     httpAssembleURIf(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme, NULL, hostTarget, ntohs(port), "%s?snmp=false", resource);
2304   else
2305     httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme, NULL, hostTarget, ntohs(port), resource);
2306 
2307   DEBUG_printf(("5http_resolve_cb: Resolved URI is \"%s\"...", uribuf->buffer));
2308 }
2309 
2310 #elif defined(HAVE_AVAHI)
2311 /*
2312  * 'http_poll_cb()' - Wait for input on the specified file descriptors.
2313  *
2314  * Note: This function is needed because avahi_simple_poll_iterate is broken
2315  *       and always uses a timeout of 0 (!) milliseconds.
2316  *       (Avahi Ticket #364)
2317  *
2318  * @private@
2319  */
2320 
2321 static int				/* O - Number of file descriptors matching */
http_poll_cb(struct pollfd * pollfds,unsigned int num_pollfds,int timeout,void * context)2322 http_poll_cb(
2323     struct pollfd *pollfds,		/* I - File descriptors */
2324     unsigned int  num_pollfds,		/* I - Number of file descriptors */
2325     int           timeout,		/* I - Timeout in milliseconds (used) */
2326     void          *context)		/* I - User data (unused) */
2327 {
2328   (void)timeout;
2329   (void)context;
2330 
2331   return (poll(pollfds, num_pollfds, 2000));
2332 }
2333 
2334 
2335 /*
2336  * 'http_resolve_cb()' - Build a device URI for the given service name.
2337  */
2338 
2339 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)2340 http_resolve_cb(
2341     AvahiServiceResolver   *resolver,	/* I - Resolver (unused) */
2342     AvahiIfIndex           interface,	/* I - Interface index (unused) */
2343     AvahiProtocol          protocol,	/* I - Network protocol (unused) */
2344     AvahiResolverEvent     event,	/* I - Event (found, etc.) */
2345     const char             *name,	/* I - Service name */
2346     const char             *type,	/* I - Registration type */
2347     const char             *domain,	/* I - Domain (unused) */
2348     const char             *hostTarget,	/* I - Hostname */
2349     const AvahiAddress     *address,	/* I - Address (unused) */
2350     uint16_t               port,	/* I - Port number */
2351     AvahiStringList        *txt,	/* I - TXT record */
2352     AvahiLookupResultFlags flags,	/* I - Lookup flags (unused) */
2353     void                   *context)	/* I - Pointer to URI buffer */
2354 {
2355   _http_uribuf_t	*uribuf = (_http_uribuf_t *)context;
2356 					/* URI buffer */
2357   const char		*scheme,	/* URI scheme */
2358 			*hostptr,	/* Pointer into hostTarget */
2359 			*reskey,	/* "rp" or "rfo" */
2360 			*resdefault;	/* Default path */
2361   char			resource[257],	/* Remote path */
2362 			fqdn[256];	/* FQDN of the .local name */
2363   AvahiStringList	*pair;		/* Current TXT record key/value pair */
2364   char			*value;		/* Value for "rp" key */
2365   size_t		valueLen = 0;	/* Length of "rp" key */
2366 
2367 
2368   DEBUG_printf(("4http_resolve_cb(resolver=%p, "
2369 		"interface=%d, protocol=%d, event=%d, name=\"%s\", "
2370 		"type=\"%s\", domain=\"%s\", hostTarget=\"%s\", address=%p, "
2371 		"port=%d, txt=%p, flags=%d, context=%p)",
2372 		resolver, interface, protocol, event, name, type, domain,
2373 		hostTarget, address, port, txt, flags, context));
2374 
2375   if (event != AVAHI_RESOLVER_FOUND)
2376   {
2377     avahi_service_resolver_free(resolver);
2378     avahi_simple_poll_quit(uribuf->poll);
2379     return;
2380   }
2381 
2382  /*
2383   * If we have a UUID, compare it...
2384   */
2385 
2386   if (uribuf->uuid && (pair = avahi_string_list_find(txt, "UUID")) != NULL)
2387   {
2388     char	uuid[256];		/* UUID value */
2389 
2390     avahi_string_list_get_pair(pair, NULL, &value, &valueLen);
2391 
2392     memcpy(uuid, value, valueLen);
2393     uuid[valueLen] = '\0';
2394 
2395     if (_cups_strcasecmp(uuid, uribuf->uuid))
2396     {
2397       if (uribuf->options & _HTTP_RESOLVE_STDERR)
2398 	fprintf(stderr, "DEBUG: Found UUID %s, looking for %s.", uuid,
2399 		uribuf->uuid);
2400 
2401       DEBUG_printf(("5http_resolve_cb: Found UUID %s, looking for %s.", uuid,
2402                     uribuf->uuid));
2403       return;
2404     }
2405   }
2406 
2407  /*
2408   * Figure out the scheme from the full name...
2409   */
2410 
2411   if (strstr(type, "_ipp."))
2412     scheme = "ipp";
2413   else if (strstr(type, "_printer."))
2414     scheme = "lpd";
2415   else if (strstr(type, "_pdl-datastream."))
2416     scheme = "socket";
2417   else
2418     scheme = "riousbprint";
2419 
2420   if (!strncmp(type, "_ipps.", 6) || !strncmp(type, "_ipp-tls.", 9))
2421     scheme = "ipps";
2422   else if (!strncmp(type, "_ipp.", 5) || !strncmp(type, "_fax-ipp.", 9))
2423     scheme = "ipp";
2424   else if (!strncmp(type, "_http.", 6))
2425     scheme = "http";
2426   else if (!strncmp(type, "_https.", 7))
2427     scheme = "https";
2428   else if (!strncmp(type, "_printer.", 9))
2429     scheme = "lpd";
2430   else if (!strncmp(type, "_pdl-datastream.", 16))
2431     scheme = "socket";
2432   else
2433   {
2434     avahi_service_resolver_free(resolver);
2435     avahi_simple_poll_quit(uribuf->poll);
2436     return;
2437   }
2438 
2439  /*
2440   * Extract the remote resource key from the TXT record...
2441   */
2442 
2443   if ((uribuf->options & _HTTP_RESOLVE_FAXOUT) &&
2444       (!strcmp(scheme, "ipp") || !strcmp(scheme, "ipps")) &&
2445       !avahi_string_list_find(txt, "printer-type"))
2446   {
2447     reskey     = "rfo";
2448     resdefault = "/ipp/faxout";
2449   }
2450   else
2451   {
2452     reskey     = "rp";
2453     resdefault = "/";
2454   }
2455 
2456   if ((pair = avahi_string_list_find(txt, reskey)) != NULL)
2457   {
2458     avahi_string_list_get_pair(pair, NULL, &value, &valueLen);
2459 
2460     if (value[0] == '/')
2461     {
2462      /*
2463       * Value (incorrectly) has a leading slash already...
2464       */
2465 
2466       memcpy(resource, value, valueLen);
2467       resource[valueLen] = '\0';
2468     }
2469     else
2470     {
2471      /*
2472       * Convert to resource by concatenating with a leading "/"...
2473       */
2474 
2475       resource[0] = '/';
2476       memcpy(resource + 1, value, valueLen);
2477       resource[valueLen + 1] = '\0';
2478     }
2479   }
2480   else
2481   {
2482    /*
2483     * Use the default value...
2484     */
2485 
2486     strlcpy(resource, resdefault, sizeof(resource));
2487   }
2488 
2489  /*
2490   * Lookup the FQDN if needed...
2491   */
2492 
2493   if ((uribuf->options & _HTTP_RESOLVE_FQDN) &&
2494       (hostptr = hostTarget + strlen(hostTarget) - 6) > hostTarget &&
2495       !_cups_strcasecmp(hostptr, ".local"))
2496   {
2497    /*
2498     * OK, we got a .local name but the caller needs a real domain.  Start by
2499     * getting the IP address of the .local name and then do reverse-lookups...
2500     */
2501 
2502     http_addrlist_t	*addrlist,	/* List of addresses */
2503 			*addr;		/* Current address */
2504 
2505     DEBUG_printf(("5http_resolve_cb: Looking up \"%s\".", hostTarget));
2506 
2507     snprintf(fqdn, sizeof(fqdn), "%d", ntohs(port));
2508     if ((addrlist = httpAddrGetList(hostTarget, AF_UNSPEC, fqdn)) != NULL)
2509     {
2510       for (addr = addrlist; addr; addr = addr->next)
2511       {
2512         int error = getnameinfo(&(addr->addr.addr), (socklen_t)httpAddrLength(&(addr->addr)), fqdn, sizeof(fqdn), NULL, 0, NI_NAMEREQD);
2513 
2514         if (!error)
2515 	{
2516 	  DEBUG_printf(("5http_resolve_cb: Found \"%s\".", fqdn));
2517 
2518 	  if ((hostptr = fqdn + strlen(fqdn) - 6) <= fqdn ||
2519 	      _cups_strcasecmp(hostptr, ".local"))
2520 	  {
2521 	    hostTarget = fqdn;
2522 	    break;
2523 	  }
2524 	}
2525 #ifdef DEBUG
2526 	else
2527 	  DEBUG_printf(("5http_resolve_cb: \"%s\" did not resolve: %d",
2528 	                httpAddrString(&(addr->addr), fqdn, sizeof(fqdn)),
2529 			error));
2530 #endif /* DEBUG */
2531       }
2532 
2533       httpAddrFreeList(addrlist);
2534     }
2535   }
2536 
2537  /*
2538   * Assemble the final device URI using the resolved hostname...
2539   */
2540 
2541   httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, (int)uribuf->bufsize, scheme,
2542                   NULL, hostTarget, port, resource);
2543   DEBUG_printf(("5http_resolve_cb: Resolved URI is \"%s\".", uribuf->buffer));
2544 
2545   avahi_simple_poll_quit(uribuf->poll);
2546 }
2547 #endif /* HAVE_DNSSD */
2548