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