1 /*
2  * "$Id: http-support.c 9322 2010-10-01 22:40:38Z mike $"
3  *
4  *   HTTP support routines for the Common UNIX Printing System (CUPS) scheduler.
5  *
6  *   Copyright 2007-2009 by Apple Inc.
7  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
8  *
9  *   These coded instructions, statements, and computer programs are the
10  *   property of Apple Inc. and are protected by Federal copyright
11  *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
12  *   which should have been included with this file.  If this file is
13  *   file is missing or damaged, see the license at "http://www.cups.org/".
14  *
15  *   This file is subject to the Apple OS-Developed Software exception.
16  *
17  * Contents:
18  *
19  *   httpAssembleURI()    - Assemble a uniform resource identifier from its
20  *                          components.
21  *   httpAssembleURIf()   - Assemble a uniform resource identifier from its
22  *                          components with a formatted resource.
23  *   httpDecode64()       - Base64-decode a string.
24  *   httpDecode64_2()     - Base64-decode a string.
25  *   httpEncode64()       - Base64-encode a string.
26  *   httpEncode64_2()     - Base64-encode a string.
27  *   httpGetDateString()  - Get a formatted date/time string from a time value.
28  *   httpGetDateString2() - Get a formatted date/time string from a time value.
29  *   httpGetDateTime()    - Get a time value from a formatted date/time string.
30  *   httpSeparate()       - Separate a Universal Resource Identifier into its
31  *                          components.
32  *   httpSeparate2()      - Separate a Universal Resource Identifier into its
33  *                          components.
34  *   httpSeparateURI()    - Separate a Universal Resource Identifier into its
35  *                          components.
36  *   httpStatus()         - Return a short string describing a HTTP status code.
37  *   _cups_hstrerror()    - hstrerror() emulation function for Solaris and
38  *                          others...
39  *   _httpEncodeURI()     - Percent-encode a HTTP request URI.
40  *   _httpResolveURI()    - Resolve a DNS-SD URI.
41  *   http_copy_decode()   - Copy and decode a URI.
42  *   http_copy_encode()   - Copy and encode a URI.
43  *   resolve_callback()   - Build a device URI for the given service name.
44  */
45 
46 /*
47  * Include necessary headers...
48  */
49 
50 #include "debug.h"
51 #include "globals.h"
52 #include <stdlib.h>
53 #include <errno.h>
54 #ifdef HAVE_DNSSD
55 #  include <dns_sd.h>
56 #  include <poll.h>
57 #endif /* HAVE_DNSSD */
58 
59 
60 /*
61  * Local types...
62  */
63 
64 typedef struct _http_uribuf_s		/* URI buffer */
65 {
66   char		*buffer;		/* Pointer to buffer */
67   size_t	bufsize;		/* Size of buffer */
68 } _http_uribuf_t;
69 
70 
71 /*
72  * Local globals...
73  */
74 
75 static const char * const http_days[7] =
76 			{
77 			  "Sun",
78 			  "Mon",
79 			  "Tue",
80 			  "Wed",
81 			  "Thu",
82 			  "Fri",
83 			  "Sat"
84 			};
85 static const char * const http_months[12] =
86 			{
87 			  "Jan",
88 			  "Feb",
89 			  "Mar",
90 			  "Apr",
91 			  "May",
92 			  "Jun",
93 		          "Jul",
94 			  "Aug",
95 			  "Sep",
96 			  "Oct",
97 			  "Nov",
98 			  "Dec"
99 			};
100 
101 
102 /*
103  * Local functions...
104  */
105 
106 static const char	*http_copy_decode(char *dst, const char *src,
107 			                  int dstsize, const char *term,
108 					  int decode);
109 static char		*http_copy_encode(char *dst, const char *src,
110 			                  char *dstend, const char *reserved,
111 					  const char *term, int encode);
112 #ifdef HAVE_DNSSD
113 static void		resolve_callback(DNSServiceRef sdRef,
114 					 DNSServiceFlags flags,
115 					 uint32_t interfaceIndex,
116 					 DNSServiceErrorType errorCode,
117 					 const char *fullName,
118 					 const char *hostTarget,
119 					 uint16_t port, uint16_t txtLen,
120 					 const unsigned char *txtRecord,
121 					 void *context);
122 #endif /* HAVE_DNSSD */
123 
124 
125 /*
126  * 'httpAssembleURI()' - Assemble a uniform resource identifier from its
127  *                       components.
128  *
129  * This function escapes reserved characters in the URI depending on the
130  * value of the "encoding" argument.  You should use this function in
131  * place of traditional string functions whenever you need to create a
132  * URI string.
133  *
134  * @since CUPS 1.2/Mac OS X 10.5@
135  */
136 
137 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)138 httpAssembleURI(
139     http_uri_coding_t encoding,		/* I - Encoding flags */
140     char              *uri,		/* I - URI buffer */
141     int               urilen,		/* I - Size of URI buffer */
142     const char        *scheme,		/* I - Scheme name */
143     const char        *username,	/* I - Username */
144     const char        *host,		/* I - Hostname or address */
145     int               port,		/* I - Port number */
146     const char        *resource)	/* I - Resource */
147 {
148   char		*ptr,			/* Pointer into URI buffer */
149 		*end;			/* End of URI buffer */
150 
151 
152  /*
153   * Range check input...
154   */
155 
156   if (!uri || urilen < 1 || !scheme || port < 0)
157   {
158     if (uri)
159       *uri = '\0';
160 
161     return (HTTP_URI_BAD_ARGUMENTS);
162   }
163 
164  /*
165   * Assemble the URI starting with the scheme...
166   */
167 
168   end = uri + urilen - 1;
169   ptr = http_copy_encode(uri, scheme, end, NULL, NULL, 0);
170 
171   if (!ptr)
172     goto assemble_overflow;
173 
174   if (!strcmp(scheme, "mailto"))
175   {
176    /*
177     * mailto: only has :, no //...
178     */
179 
180     if (ptr < end)
181       *ptr++ = ':';
182     else
183       goto assemble_overflow;
184   }
185   else
186   {
187    /*
188     * Schemes other than mailto: all have //...
189     */
190 
191     if ((ptr + 2) < end)
192     {
193       *ptr++ = ':';
194       *ptr++ = '/';
195       *ptr++ = '/';
196     }
197     else
198       goto assemble_overflow;
199   }
200 
201  /*
202   * Next the username and hostname, if any...
203   */
204 
205   if (host)
206   {
207     if (username && *username)
208     {
209      /*
210       * Add username@ first...
211       */
212 
213       ptr = http_copy_encode(ptr, username, end, "/?@", NULL,
214                              encoding & HTTP_URI_CODING_USERNAME);
215 
216       if (!ptr)
217         goto assemble_overflow;
218 
219       if (ptr < end)
220 	*ptr++ = '@';
221       else
222         goto assemble_overflow;
223     }
224 
225    /*
226     * Then add the hostname.  Since IPv6 is a particular pain to deal
227     * with, we have several special cases to deal with.  If we get
228     * an IPv6 address with brackets around it, assume it is already in
229     * URI format.  Since DNS-SD service names can sometimes look like
230     * raw IPv6 addresses, we specifically look for "._tcp" in the name,
231     * too...
232     */
233 
234     if (host[0] != '[' && strchr(host, ':') && !strstr(host, "._tcp"))
235     {
236      /*
237       * We have a raw IPv6 address...
238       */
239 
240       if (strchr(host, '%'))
241       {
242        /*
243         * We have a link-local address, add "[v1." prefix...
244 	*/
245 
246 	if ((ptr + 4) < end)
247 	{
248 	  *ptr++ = '[';
249 	  *ptr++ = 'v';
250 	  *ptr++ = '1';
251 	  *ptr++ = '.';
252 	}
253 	else
254           goto assemble_overflow;
255       }
256       else
257       {
258        /*
259         * We have a normal address, add "[" prefix...
260 	*/
261 
262 	if (ptr < end)
263 	  *ptr++ = '[';
264 	else
265           goto assemble_overflow;
266       }
267 
268      /*
269       * Copy the rest of the IPv6 address, and terminate with "]".
270       */
271 
272       while (ptr < end && *host)
273       {
274         if (*host == '%')
275 	{
276           *ptr++ = '+';			/* Convert zone separator */
277 	  host ++;
278 	}
279 	else
280 	  *ptr++ = *host++;
281       }
282 
283       if (*host)
284         goto assemble_overflow;
285 
286       if (ptr < end)
287 	*ptr++ = ']';
288       else
289         goto assemble_overflow;
290     }
291     else
292     {
293      /*
294       * Otherwise, just copy the host string...
295       */
296 
297       ptr = http_copy_encode(ptr, host, end, ":/?#[]@\\", NULL,
298                              encoding & HTTP_URI_CODING_HOSTNAME);
299 
300       if (!ptr)
301         goto assemble_overflow;
302     }
303 
304    /*
305     * Finish things off with the port number...
306     */
307 
308     if (port > 0)
309     {
310       snprintf(ptr, end - ptr + 1, ":%d", port);
311       ptr += strlen(ptr);
312 
313       if (ptr >= end)
314 	goto assemble_overflow;
315     }
316   }
317 
318  /*
319   * Last but not least, add the resource string...
320   */
321 
322   if (resource)
323   {
324     char	*query;			/* Pointer to query string */
325 
326 
327    /*
328     * Copy the resource string up to the query string if present...
329     */
330 
331     query = strchr(resource, '?');
332     ptr   = http_copy_encode(ptr, resource, end, NULL, "?",
333                              encoding & HTTP_URI_CODING_RESOURCE);
334     if (!ptr)
335       goto assemble_overflow;
336 
337     if (query)
338     {
339      /*
340       * Copy query string without encoding...
341       */
342 
343       ptr = http_copy_encode(ptr, query, end, NULL, NULL,
344 			     encoding & HTTP_URI_CODING_QUERY);
345       if (!ptr)
346 	goto assemble_overflow;
347     }
348   }
349   else if (ptr < end)
350     *ptr++ = '/';
351   else
352     goto assemble_overflow;
353 
354  /*
355   * Nul-terminate the URI buffer and return with no errors...
356   */
357 
358   *ptr = '\0';
359 
360   return (HTTP_URI_OK);
361 
362  /*
363   * Clear the URI string and return an overflow error; I don't usually
364   * like goto's, but in this case it makes sense...
365   */
366 
367   assemble_overflow:
368 
369   *uri = '\0';
370   return (HTTP_URI_OVERFLOW);
371 }
372 
373 
374 /*
375  * 'httpAssembleURIf()' - Assemble a uniform resource identifier from its
376  *                        components with a formatted resource.
377  *
378  * This function creates a formatted version of the resource string
379  * argument "resourcef" and escapes reserved characters in the URI
380  * depending on the value of the "encoding" argument.  You should use
381  * this function in place of traditional string functions whenever
382  * you need to create a URI string.
383  *
384  * @since CUPS 1.2/Mac OS X 10.5@
385  */
386 
387 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,...)388 httpAssembleURIf(
389     http_uri_coding_t encoding,		/* I - Encoding flags */
390     char              *uri,		/* I - URI buffer */
391     int               urilen,		/* I - Size of URI buffer */
392     const char        *scheme,		/* I - Scheme name */
393     const char        *username,	/* I - Username */
394     const char        *host,		/* I - Hostname or address */
395     int               port,		/* I - Port number */
396     const char        *resourcef,	/* I - Printf-style resource */
397     ...)				/* I - Additional arguments as needed */
398 {
399   va_list	ap;			/* Pointer to additional arguments */
400   char		resource[1024];		/* Formatted resource string */
401   int		bytes;			/* Bytes in formatted string */
402 
403 
404  /*
405   * Range check input...
406   */
407 
408   if (!uri || urilen < 1 || !scheme || port < 0 || !resourcef)
409   {
410     if (uri)
411       *uri = '\0';
412 
413     return (HTTP_URI_BAD_ARGUMENTS);
414   }
415 
416  /*
417   * Format the resource string and assemble the URI...
418   */
419 
420   va_start(ap, resourcef);
421   bytes = vsnprintf(resource, sizeof(resource), resourcef, ap);
422   va_end(ap);
423 
424   if (bytes >= sizeof(resource))
425   {
426     *uri = '\0';
427     return (HTTP_URI_OVERFLOW);
428   }
429   else
430     return (httpAssembleURI(encoding,  uri, urilen, scheme, username, host,
431                             port, resource));
432 }
433 
434 
435 /*
436  * 'httpDecode64()' - Base64-decode a string.
437  *
438  * This function is deprecated. Use the httpDecode64_2() function instead
439  * which provides buffer length arguments.
440  *
441  * @deprecated@
442  */
443 
444 char *					/* O - Decoded string */
httpDecode64(char * out,const char * in)445 httpDecode64(char       *out,		/* I - String to write to */
446              const char *in)		/* I - String to read from */
447 {
448   int	outlen;				/* Output buffer length */
449 
450 
451  /*
452   * Use the old maximum buffer size for binary compatibility...
453   */
454 
455   outlen = 512;
456 
457   return (httpDecode64_2(out, &outlen, in));
458 }
459 
460 
461 /*
462  * 'httpDecode64_2()' - Base64-decode a string.
463  *
464  * @since CUPS 1.1.21/Mac OS X 10.4@
465  */
466 
467 char *					/* O  - Decoded string */
httpDecode64_2(char * out,int * outlen,const char * in)468 httpDecode64_2(char       *out,		/* I  - String to write to */
469 	       int        *outlen,	/* IO - Size of output string */
470                const char *in)		/* I  - String to read from */
471 {
472   int	pos,				/* Bit position */
473 	base64;				/* Value of this character */
474   char	*outptr,			/* Output pointer */
475 	*outend;			/* End of output buffer */
476 
477 
478  /*
479   * Range check input...
480   */
481 
482   if (!out || !outlen || *outlen < 1 || !in)
483     return (NULL);
484 
485   if (!*in)
486   {
487     *out    = '\0';
488     *outlen = 0;
489 
490     return (out);
491   }
492 
493  /*
494   * Convert from base-64 to bytes...
495   */
496 
497   for (outptr = out, outend = out + *outlen - 1, pos = 0; *in != '\0'; in ++)
498   {
499    /*
500     * Decode this character into a number from 0 to 63...
501     */
502 
503     if (*in >= 'A' && *in <= 'Z')
504       base64 = *in - 'A';
505     else if (*in >= 'a' && *in <= 'z')
506       base64 = *in - 'a' + 26;
507     else if (*in >= '0' && *in <= '9')
508       base64 = *in - '0' + 52;
509     else if (*in == '+')
510       base64 = 62;
511     else if (*in == '/')
512       base64 = 63;
513     else if (*in == '=')
514       break;
515     else
516       continue;
517 
518    /*
519     * Store the result in the appropriate chars...
520     */
521 
522     switch (pos)
523     {
524       case 0 :
525           if (outptr < outend)
526             *outptr = base64 << 2;
527 	  pos ++;
528 	  break;
529       case 1 :
530           if (outptr < outend)
531             *outptr++ |= (base64 >> 4) & 3;
532           if (outptr < outend)
533 	    *outptr = (base64 << 4) & 255;
534 	  pos ++;
535 	  break;
536       case 2 :
537           if (outptr < outend)
538             *outptr++ |= (base64 >> 2) & 15;
539           if (outptr < outend)
540 	    *outptr = (base64 << 6) & 255;
541 	  pos ++;
542 	  break;
543       case 3 :
544           if (outptr < outend)
545             *outptr++ |= base64;
546 	  pos = 0;
547 	  break;
548     }
549   }
550 
551   *outptr = '\0';
552 
553  /*
554   * Return the decoded string and size...
555   */
556 
557   *outlen = (int)(outptr - out);
558 
559   return (out);
560 }
561 
562 
563 /*
564  * 'httpEncode64()' - Base64-encode a string.
565  *
566  * This function is deprecated. Use the httpEncode64_2() function instead
567  * which provides buffer length arguments.
568  *
569  * @deprecated@
570  */
571 
572 char *					/* O - Encoded string */
httpEncode64(char * out,const char * in)573 httpEncode64(char       *out,		/* I - String to write to */
574              const char *in)		/* I - String to read from */
575 {
576   return (httpEncode64_2(out, 512, in, (int)strlen(in)));
577 }
578 
579 
580 /*
581  * 'httpEncode64_2()' - Base64-encode a string.
582  *
583  * @since CUPS 1.1.21/Mac OS X 10.4@
584  */
585 
586 char *					/* O - Encoded string */
httpEncode64_2(char * out,int outlen,const char * in,int inlen)587 httpEncode64_2(char       *out,		/* I - String to write to */
588 	       int        outlen,	/* I - Size of output string */
589                const char *in,		/* I - String to read from */
590 	       int        inlen)	/* I - Size of input string */
591 {
592   char		*outptr,		/* Output pointer */
593 		*outend;		/* End of output buffer */
594   static const char base64[] =		/* Base64 characters... */
595   		{
596 		  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
597 		  "abcdefghijklmnopqrstuvwxyz"
598 		  "0123456789"
599 		  "+/"
600   		};
601 
602 
603  /*
604   * Range check input...
605   */
606 
607   if (!out || outlen < 1 || !in)
608     return (NULL);
609 
610  /*
611   * Convert bytes to base-64...
612   */
613 
614   for (outptr = out, outend = out + outlen - 1; inlen > 0; in ++, inlen --)
615   {
616    /*
617     * Encode the up to 3 characters as 4 Base64 numbers...
618     */
619 
620     if (outptr < outend)
621       *outptr ++ = base64[(in[0] & 255) >> 2];
622 
623     if (outptr < outend)
624     {
625       if (inlen > 1)
626         *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
627       else
628         *outptr ++ = base64[((in[0] & 255) << 4) & 63];
629     }
630 
631     in ++;
632     inlen --;
633     if (inlen <= 0)
634     {
635       if (outptr < outend)
636         *outptr ++ = '=';
637       if (outptr < outend)
638         *outptr ++ = '=';
639       break;
640     }
641 
642     if (outptr < outend)
643     {
644       if (inlen > 1)
645         *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
646       else
647         *outptr ++ = base64[((in[0] & 255) << 2) & 63];
648     }
649 
650     in ++;
651     inlen --;
652     if (inlen <= 0)
653     {
654       if (outptr < outend)
655         *outptr ++ = '=';
656       break;
657     }
658 
659     if (outptr < outend)
660       *outptr ++ = base64[in[0] & 63];
661   }
662 
663   *outptr = '\0';
664 
665  /*
666   * Return the encoded string...
667   */
668 
669   return (out);
670 }
671 
672 
673 /*
674  * 'httpGetDateString()' - Get a formatted date/time string from a time value.
675  *
676  * @deprecated@
677  */
678 
679 const char *				/* O - Date/time string */
httpGetDateString(time_t t)680 httpGetDateString(time_t t)		/* I - UNIX time */
681 {
682   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
683 
684 
685   return (httpGetDateString2(t, cg->http_date, sizeof(cg->http_date)));
686 }
687 
688 
689 /*
690  * 'httpGetDateString2()' - Get a formatted date/time string from a time value.
691  *
692  * @since CUPS 1.2/Mac OS X 10.5@
693  */
694 
695 const char *				/* O - Date/time string */
httpGetDateString2(time_t t,char * s,int slen)696 httpGetDateString2(time_t t,		/* I - UNIX time */
697                    char   *s,		/* I - String buffer */
698 		   int    slen)		/* I - Size of string buffer */
699 {
700   struct tm	*tdate;			/* UNIX date/time data */
701 
702 
703   tdate = gmtime(&t);
704   snprintf(s, slen, "%s, %02d %s %d %02d:%02d:%02d GMT",
705            http_days[tdate->tm_wday], tdate->tm_mday,
706 	   http_months[tdate->tm_mon], tdate->tm_year + 1900,
707 	   tdate->tm_hour, tdate->tm_min, tdate->tm_sec);
708 
709   return (s);
710 }
711 
712 
713 /*
714  * 'httpGetDateTime()' - Get a time value from a formatted date/time string.
715  */
716 
717 time_t					/* O - UNIX time */
httpGetDateTime(const char * s)718 httpGetDateTime(const char *s)		/* I - Date/time string */
719 {
720   int		i;			/* Looping var */
721   char		mon[16];		/* Abbreviated month name */
722   int		day, year;		/* Day of month and year */
723   int		hour, min, sec;		/* Time */
724   int		days;			/* Number of days since 1970 */
725   static const int normal_days[] =	/* Days to a month, normal years */
726 		{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
727   static const int leap_days[] =	/* Days to a month, leap years */
728 		{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
729 
730 
731   DEBUG_printf(("2httpGetDateTime(s=\"%s\")", s));
732 
733  /*
734   * Extract the date and time from the formatted string...
735   */
736 
737   if (sscanf(s, "%*s%d%15s%d%d:%d:%d", &day, mon, &year, &hour, &min, &sec) < 6)
738     return (0);
739 
740   DEBUG_printf(("4httpGetDateTime: day=%d, mon=\"%s\", year=%d, hour=%d, "
741                 "min=%d, sec=%d", day, mon, year, hour, min, sec));
742 
743  /*
744   * Convert the month name to a number from 0 to 11.
745   */
746 
747   for (i = 0; i < 12; i ++)
748     if (!strcasecmp(mon, http_months[i]))
749       break;
750 
751   if (i >= 12)
752     return (0);
753 
754   DEBUG_printf(("4httpGetDateTime: i=%d", i));
755 
756  /*
757   * Now convert the date and time to a UNIX time value in seconds since
758   * 1970.  We can't use mktime() since the timezone may not be UTC but
759   * the date/time string *is* UTC.
760   */
761 
762   if ((year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0))
763     days = leap_days[i] + day - 1;
764   else
765     days = normal_days[i] + day - 1;
766 
767   DEBUG_printf(("4httpGetDateTime: days=%d", days));
768 
769   days += (year - 1970) * 365 +		/* 365 days per year (normally) */
770           ((year - 1) / 4 - 492) -	/* + leap days */
771 	  ((year - 1) / 100 - 19) +	/* - 100 year days */
772           ((year - 1) / 400 - 4);	/* + 400 year days */
773 
774   DEBUG_printf(("4httpGetDateTime: days=%d\n", days));
775 
776   return (days * 86400 + hour * 3600 + min * 60 + sec);
777 }
778 
779 
780 /*
781  * 'httpSeparate()' - Separate a Universal Resource Identifier into its
782  *                    components.
783  *
784  * This function is deprecated; use the httpSeparateURI() function instead.
785  *
786  * @deprecated@
787  */
788 
789 void
httpSeparate(const char * uri,char * scheme,char * username,char * host,int * port,char * resource)790 httpSeparate(const char *uri,		/* I - Universal Resource Identifier */
791              char       *scheme,	/* O - Scheme [32] (http, https, etc.) */
792 	     char       *username,	/* O - Username [1024] */
793 	     char       *host,		/* O - Hostname [1024] */
794 	     int        *port,		/* O - Port number to use */
795              char       *resource)	/* O - Resource/filename [1024] */
796 {
797   httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, 32, username,
798                   HTTP_MAX_URI, host, HTTP_MAX_URI, port, resource,
799 		  HTTP_MAX_URI);
800 }
801 
802 
803 /*
804  * 'httpSeparate2()' - Separate a Universal Resource Identifier into its
805  *                     components.
806  *
807  * This function is deprecated; use the httpSeparateURI() function instead.
808  *
809  * @since CUPS 1.1.21/Mac OS X 10.4@
810  * @deprecated@
811  */
812 
813 void
httpSeparate2(const char * uri,char * scheme,int schemelen,char * username,int usernamelen,char * host,int hostlen,int * port,char * resource,int resourcelen)814 httpSeparate2(const char *uri,		/* I - Universal Resource Identifier */
815               char       *scheme,	/* O - Scheme (http, https, etc.) */
816 	      int        schemelen,	/* I - Size of scheme buffer */
817 	      char       *username,	/* O - Username */
818 	      int        usernamelen,	/* I - Size of username buffer */
819 	      char       *host,		/* O - Hostname */
820 	      int        hostlen,	/* I - Size of hostname buffer */
821 	      int        *port,		/* O - Port number to use */
822               char       *resource,	/* O - Resource/filename */
823 	      int        resourcelen)	/* I - Size of resource buffer */
824 {
825   httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme, schemelen, username,
826                   usernamelen, host, hostlen, port, resource, resourcelen);
827 }
828 
829 
830 /*
831  * 'httpSeparateURI()' - Separate a Universal Resource Identifier into its
832  *                       components.
833  *
834  * @since CUPS 1.2/Mac OS X 10.5@
835  */
836 
837 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)838 httpSeparateURI(
839     http_uri_coding_t decoding,		/* I - Decoding flags */
840     const char        *uri,		/* I - Universal Resource Identifier */
841     char              *scheme,		/* O - Scheme (http, https, etc.) */
842     int               schemelen,	/* I - Size of scheme buffer */
843     char              *username,	/* O - Username */
844     int               usernamelen,	/* I - Size of username buffer */
845     char              *host,		/* O - Hostname */
846     int               hostlen,		/* I - Size of hostname buffer */
847     int               *port,		/* O - Port number to use */
848     char              *resource,	/* O - Resource/filename */
849     int               resourcelen)	/* I - Size of resource buffer */
850 {
851   char			*ptr,		/* Pointer into string... */
852 			*end;		/* End of string */
853   const char		*sep;		/* Separator character */
854   http_uri_status_t	status;		/* Result of separation */
855 
856 
857  /*
858   * Initialize everything to blank...
859   */
860 
861   if (scheme && schemelen > 0)
862     *scheme = '\0';
863 
864   if (username && usernamelen > 0)
865     *username = '\0';
866 
867   if (host && hostlen > 0)
868     *host = '\0';
869 
870   if (port)
871     *port = 0;
872 
873   if (resource && resourcelen > 0)
874     *resource = '\0';
875 
876  /*
877   * Range check input...
878   */
879 
880   if (!uri || !port || !scheme || schemelen <= 0 || !username ||
881       usernamelen <= 0 || !host || hostlen <= 0 || !resource ||
882       resourcelen <= 0)
883     return (HTTP_URI_BAD_ARGUMENTS);
884 
885   if (!*uri)
886     return (HTTP_URI_BAD_URI);
887 
888  /*
889   * Grab the scheme portion of the URI...
890   */
891 
892   status = HTTP_URI_OK;
893 
894   if (!strncmp(uri, "//", 2))
895   {
896    /*
897     * Workaround for HP IPP client bug...
898     */
899 
900     strlcpy(scheme, "ipp", schemelen);
901     status = HTTP_URI_MISSING_SCHEME;
902   }
903   else if (*uri == '/')
904   {
905    /*
906     * Filename...
907     */
908 
909     strlcpy(scheme, "file", schemelen);
910     status = HTTP_URI_MISSING_SCHEME;
911   }
912   else
913   {
914    /*
915     * Standard URI with scheme...
916     */
917 
918     for (ptr = scheme, end = scheme + schemelen - 1;
919          *uri && *uri != ':' && ptr < end;)
920       if (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
921                  "abcdefghijklmnopqrstuvwxyz"
922 		 "0123456789-+.", *uri) != NULL)
923         *ptr++ = *uri++;
924       else
925         break;
926 
927     *ptr = '\0';
928 
929     if (*uri != ':')
930     {
931       *scheme = '\0';
932       return (HTTP_URI_BAD_SCHEME);
933     }
934 
935     uri ++;
936   }
937 
938  /*
939   * Set the default port number...
940   */
941 
942   if (!strcmp(scheme, "http"))
943     *port = 80;
944   else if (!strcmp(scheme, "https"))
945     *port = 443;
946   else if (!strcmp(scheme, "ipp"))
947     *port = 631;
948   else if (!strcasecmp(scheme, "lpd"))
949     *port = 515;
950   else if (!strcmp(scheme, "socket"))	/* Not yet registered with IANA... */
951     *port = 9100;
952   else if (strcmp(scheme, "file") && strcmp(scheme, "mailto"))
953     status = HTTP_URI_UNKNOWN_SCHEME;
954 
955  /*
956   * Now see if we have a hostname...
957   */
958 
959   if (!strncmp(uri, "//", 2))
960   {
961    /*
962     * Yes, extract it...
963     */
964 
965     uri += 2;
966 
967    /*
968     * Grab the username, if any...
969     */
970 
971     if ((sep = strpbrk(uri, "@/")) != NULL && *sep == '@')
972     {
973      /*
974       * Get a username:password combo...
975       */
976 
977       uri = http_copy_decode(username, uri, usernamelen, "@",
978                              decoding & HTTP_URI_CODING_USERNAME);
979 
980       if (!uri)
981       {
982         *username = '\0';
983         return (HTTP_URI_BAD_USERNAME);
984       }
985 
986       uri ++;
987     }
988 
989    /*
990     * Then the hostname/IP address...
991     */
992 
993     if (*uri == '[')
994     {
995      /*
996       * Grab IPv6 address...
997       */
998 
999       uri ++;
1000       if (!strncmp(uri, "v1.", 3))
1001         uri += 3;			/* Skip IPvN leader... */
1002 
1003       uri = http_copy_decode(host, uri, hostlen, "]",
1004                              decoding & HTTP_URI_CODING_HOSTNAME);
1005 
1006       if (!uri)
1007       {
1008         *host = '\0';
1009         return (HTTP_URI_BAD_HOSTNAME);
1010       }
1011 
1012      /*
1013       * Validate value...
1014       */
1015 
1016       if (*uri != ']')
1017       {
1018         *host = '\0';
1019         return (HTTP_URI_BAD_HOSTNAME);
1020       }
1021 
1022       uri ++;
1023 
1024       for (ptr = host; *ptr; ptr ++)
1025         if (*ptr == '+')
1026 	{
1027 	 /*
1028 	  * Convert zone separator to % and stop here...
1029 	  */
1030 
1031 	  *ptr = '%';
1032 	  break;
1033 	}
1034 	else if (*ptr != ':' && *ptr != '.' && !isxdigit(*ptr & 255))
1035 	{
1036 	  *host = '\0';
1037 	  return (HTTP_URI_BAD_HOSTNAME);
1038 	}
1039     }
1040     else
1041     {
1042      /*
1043       * Validate the hostname or IPv4 address first...
1044       */
1045 
1046       for (ptr = (char *)uri; *ptr; ptr ++)
1047         if (strchr(":?/", *ptr))
1048 	  break;
1049         else if (!strchr("abcdefghijklmnopqrstuvwxyz"
1050 			 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1051 			 "0123456789"
1052 	        	 "-._~"
1053 			 "%"
1054 			 "!$&'()*+,;=\\", *ptr))
1055 	{
1056 	  *host = '\0';
1057 	  return (HTTP_URI_BAD_HOSTNAME);
1058 	}
1059 
1060      /*
1061       * Then copy the hostname or IPv4 address to the buffer...
1062       */
1063 
1064       uri = http_copy_decode(host, uri, hostlen, ":?/",
1065                              decoding & HTTP_URI_CODING_HOSTNAME);
1066 
1067       if (!uri)
1068       {
1069         *host = '\0';
1070         return (HTTP_URI_BAD_HOSTNAME);
1071       }
1072     }
1073 
1074    /*
1075     * Validate hostname for file scheme - only empty and localhost are
1076     * acceptable.
1077     */
1078 
1079     if (!strcmp(scheme, "file") && strcmp(host, "localhost") && host[0])
1080     {
1081       *host = '\0';
1082       return (HTTP_URI_BAD_HOSTNAME);
1083     }
1084 
1085    /*
1086     * See if we have a port number...
1087     */
1088 
1089     if (*uri == ':')
1090     {
1091      /*
1092       * Yes, collect the port number...
1093       */
1094 
1095       if (!isdigit(uri[1] & 255))
1096       {
1097         *port = 0;
1098         return (HTTP_URI_BAD_PORT);
1099       }
1100 
1101       *port = strtol(uri + 1, (char **)&uri, 10);
1102 
1103       if (*uri != '/' && *uri)
1104       {
1105         *port = 0;
1106         return (HTTP_URI_BAD_PORT);
1107       }
1108     }
1109   }
1110 
1111  /*
1112   * The remaining portion is the resource string...
1113   */
1114 
1115   if (*uri == '?' || !*uri)
1116   {
1117    /*
1118     * Hostname but no path...
1119     */
1120 
1121     status    = HTTP_URI_MISSING_RESOURCE;
1122     *resource = '/';
1123 
1124    /*
1125     * Copy any query string...
1126     */
1127 
1128     if (*uri == '?')
1129       uri = http_copy_decode(resource + 1, uri, resourcelen - 1, NULL,
1130                              decoding & HTTP_URI_CODING_QUERY);
1131     else
1132       resource[1] = '\0';
1133   }
1134   else
1135   {
1136     uri = http_copy_decode(resource, uri, resourcelen, "?",
1137                            decoding & HTTP_URI_CODING_RESOURCE);
1138 
1139     if (uri && *uri == '?')
1140     {
1141      /*
1142       * Concatenate any query string...
1143       */
1144 
1145       char *resptr = resource + strlen(resource);
1146 
1147       uri = http_copy_decode(resptr, uri, resourcelen - (int)(resptr - resource),
1148                              NULL, decoding & HTTP_URI_CODING_QUERY);
1149     }
1150   }
1151 
1152   if (!uri)
1153   {
1154     *resource = '\0';
1155     return (HTTP_URI_BAD_RESOURCE);
1156   }
1157 
1158  /*
1159   * Return the URI separation status...
1160   */
1161 
1162   return (status);
1163 }
1164 
1165 
1166 /*
1167  * 'httpStatus()' - Return a short string describing a HTTP status code.
1168  *
1169  * The returned string is localized to the current POSIX locale and is based
1170  * on the status strings defined in RFC 2616.
1171  */
1172 
1173 const char *				/* O - Localized status string */
httpStatus(http_status_t status)1174 httpStatus(http_status_t status)	/* I - HTTP status code */
1175 {
1176   const char	*s;			/* Status string */
1177   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
1178 
1179 
1180   if (!cg->lang_default)
1181     cg->lang_default = cupsLangDefault();
1182 
1183   switch (status)
1184   {
1185     case HTTP_CONTINUE :
1186         s = _("Continue");
1187 	break;
1188     case HTTP_SWITCHING_PROTOCOLS :
1189         s = _("Switching Protocols");
1190 	break;
1191     case HTTP_OK :
1192         s = _("OK");
1193 	break;
1194     case HTTP_CREATED :
1195         s = _("Created");
1196 	break;
1197     case HTTP_ACCEPTED :
1198         s = _("Accepted");
1199 	break;
1200     case HTTP_NO_CONTENT :
1201         s = _("No Content");
1202 	break;
1203     case HTTP_MOVED_PERMANENTLY :
1204         s = _("Moved Permanently");
1205 	break;
1206     case HTTP_SEE_OTHER :
1207         s = _("See Other");
1208 	break;
1209     case HTTP_NOT_MODIFIED :
1210         s = _("Not Modified");
1211 	break;
1212     case HTTP_BAD_REQUEST :
1213         s = _("Bad Request");
1214 	break;
1215     case HTTP_UNAUTHORIZED :
1216     case HTTP_AUTHORIZATION_CANCELED :
1217         s = _("Unauthorized");
1218 	break;
1219     case HTTP_FORBIDDEN :
1220         s = _("Forbidden");
1221 	break;
1222     case HTTP_NOT_FOUND :
1223         s = _("Not Found");
1224 	break;
1225     case HTTP_REQUEST_TOO_LARGE :
1226         s = _("Request Entity Too Large");
1227 	break;
1228     case HTTP_URI_TOO_LONG :
1229         s = _("URI Too Long");
1230 	break;
1231     case HTTP_UPGRADE_REQUIRED :
1232         s = _("Upgrade Required");
1233 	break;
1234     case HTTP_NOT_IMPLEMENTED :
1235         s = _("Not Implemented");
1236 	break;
1237     case HTTP_NOT_SUPPORTED :
1238         s = _("Not Supported");
1239 	break;
1240     case HTTP_EXPECTATION_FAILED :
1241         s = _("Expectation Failed");
1242 	break;
1243     case HTTP_SERVICE_UNAVAILABLE :
1244         s = _("Service Unavailable");
1245 	break;
1246     case HTTP_SERVER_ERROR :
1247         s = _("Internal Server Error");
1248 	break;
1249 
1250     default :
1251         s = _("Unknown");
1252 	break;
1253   }
1254 
1255   return (_cupsLangString(cg->lang_default, s));
1256 }
1257 
1258 
1259 #ifndef HAVE_HSTRERROR
1260 /*
1261  * '_cups_hstrerror()' - hstrerror() emulation function for Solaris and others...
1262  */
1263 
1264 const char *				/* O - Error string */
_cups_hstrerror(int error)1265 _cups_hstrerror(int error)		/* I - Error number */
1266 {
1267   static const char * const errors[] =	/* Error strings */
1268 		{
1269 		  "OK",
1270 		  "Host not found.",
1271 		  "Try again.",
1272 		  "Unrecoverable lookup error.",
1273 		  "No data associated with name."
1274 		};
1275 
1276 
1277   if (error < 0 || error > 4)
1278     return ("Unknown hostname lookup error.");
1279   else
1280     return (errors[error]);
1281 }
1282 #endif /* !HAVE_HSTRERROR */
1283 
1284 
1285 /*
1286  * '_httpEncodeURI()' - Percent-encode a HTTP request URI.
1287  */
1288 
1289 char *					/* O - Encoded URI */
_httpEncodeURI(char * dst,const char * src,size_t dstsize)1290 _httpEncodeURI(char       *dst,		/* I - Destination buffer */
1291                const char *src,		/* I - Source URI */
1292 	       size_t     dstsize)	/* I - Size of destination buffer */
1293 {
1294   http_copy_encode(dst, src, dst + dstsize - 1, NULL, NULL, 1);
1295   return (dst);
1296 }
1297 
1298 
1299 /*
1300  * '_httpResolveURI()' - Resolve a DNS-SD URI.
1301  */
1302 
1303 const char *				/* O - Resolved URI */
_httpResolveURI(const char * uri,char * resolved_uri,size_t resolved_size,int logit)1304 _httpResolveURI(
1305     const char *uri,			/* I - DNS-SD URI */
1306     char       *resolved_uri,		/* I - Buffer for resolved URI */
1307     size_t     resolved_size,		/* I - Size of URI buffer */
1308     int        logit)			/* I - Log progress to stderr? */
1309 {
1310   char			scheme[32],	/* URI components... */
1311 			userpass[256],
1312 			hostname[1024],
1313 			resource[1024];
1314   int			port;
1315 #ifdef DEBUG
1316   http_uri_status_t	status;		/* URI decode status */
1317 #endif /* DEBUG */
1318 
1319 
1320   DEBUG_printf(("4_httpResolveURI(uri=\"%s\", resolved_uri=%p, "
1321                 "resolved_size=" CUPS_LLFMT ")", uri, resolved_uri,
1322 		CUPS_LLCAST resolved_size));
1323 
1324  /*
1325   * Get the device URI...
1326   */
1327 
1328 #ifdef DEBUG
1329   if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1330                                 sizeof(scheme), userpass, sizeof(userpass),
1331 				hostname, sizeof(hostname), &port, resource,
1332 				sizeof(resource))) < HTTP_URI_OK)
1333 #else
1334   if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
1335 		      sizeof(scheme), userpass, sizeof(userpass),
1336 		      hostname, sizeof(hostname), &port, resource,
1337 		      sizeof(resource)) < HTTP_URI_OK)
1338 #endif /* DEBUG */
1339   {
1340     if (logit)
1341       _cupsLangPrintf(stderr, _("Bad device URI \"%s\"!\n"), uri);
1342 
1343     DEBUG_printf(("6_httpResolveURI: httpSeparateURI returned %d!", status));
1344     DEBUG_puts("5_httpResolveURI: Returning NULL");
1345     return (NULL);
1346   }
1347 
1348  /*
1349   * Resolve it as needed...
1350   */
1351 
1352   if (strstr(hostname, "._tcp"))
1353   {
1354 #ifdef HAVE_DNSSD
1355     DNSServiceRef	ref,		/* DNS-SD master service reference */
1356 			domainref,	/* DNS-SD service reference for domain */
1357 			localref;	/* DNS-SD service reference for .local */
1358     int			domainsent = 0,	/* Send the domain resolve? */
1359 			offline = 0;	/* offline-report state set? */
1360     char		*regtype,	/* Pointer to type in hostname */
1361 			*domain;	/* Pointer to domain in hostname */
1362     _http_uribuf_t	uribuf;		/* URI buffer */
1363     struct pollfd	polldata;	/* Polling data */
1364 
1365 
1366     if (logit)
1367       fprintf(stderr, "DEBUG: Resolving \"%s\"...\n", hostname);
1368 
1369    /*
1370     * Separate the hostname into service name, registration type, and domain...
1371     */
1372 
1373     for (regtype = strstr(hostname, "._tcp") - 2;
1374          regtype > hostname;
1375 	 regtype --)
1376       if (regtype[0] == '.' && regtype[1] == '_')
1377       {
1378        /*
1379         * Found ._servicetype in front of ._tcp...
1380 	*/
1381 
1382         *regtype++ = '\0';
1383 	break;
1384       }
1385 
1386     if (regtype <= hostname)
1387     {
1388       DEBUG_puts("5_httpResolveURI: Bad hostname, returning NULL");
1389       return (NULL);
1390     }
1391 
1392     for (domain = strchr(regtype, '.');
1393          domain;
1394 	 domain = strchr(domain + 1, '.'))
1395       if (domain[1] != '_')
1396         break;
1397 
1398     if (domain)
1399       *domain++ = '\0';
1400 
1401     uribuf.buffer  = resolved_uri;
1402     uribuf.bufsize = resolved_size;
1403 
1404     resolved_uri[0] = '\0';
1405 
1406     DEBUG_printf(("6_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", "
1407                   "domain=\"%s\"\n", hostname, regtype, domain));
1408     if (logit)
1409     {
1410       fputs("STATE: +connecting-to-device\n", stderr);
1411       fprintf(stderr, "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1412                       "domain=\"local.\"...\n", hostname, regtype);
1413     }
1414 
1415     uri = NULL;
1416 
1417     if (DNSServiceCreateConnection(&ref) == kDNSServiceErr_NoError)
1418     {
1419       localref = ref;
1420       if (DNSServiceResolve(&localref, kDNSServiceFlagsShareConnection, 0,
1421 			    hostname, regtype, "local.", resolve_callback,
1422 			    &uribuf) == kDNSServiceErr_NoError)
1423       {
1424 	int	fds;			/* Number of ready descriptors */
1425 	time_t	timeout,		/* Poll timeout */
1426 		start_time = time(NULL);/* Start time */
1427 
1428 	for (;;)
1429 	{
1430 	  if (logit)
1431 	    _cupsLangPuts(stderr, _("INFO: Looking for printer...\n"));
1432 
1433 	 /*
1434 	  * For the first minute, wakeup every 2 seconds to emit a
1435 	  * "looking for printer" message...
1436 	  */
1437 
1438 	  timeout = (time(NULL) < (start_time + 60)) ? 2000 : -1;
1439 
1440 	  polldata.fd     = DNSServiceRefSockFD(ref);
1441 	  polldata.events = POLLIN;
1442 
1443 	  fds = poll(&polldata, 1, timeout);
1444 
1445 	  if (fds < 0)
1446 	  {
1447 	    if (errno != EINTR && errno != EAGAIN)
1448 	    {
1449 	      DEBUG_printf(("5_httpResolveURI: poll error: %s", strerror(errno)));
1450 	      break;
1451 	    }
1452 	  }
1453 	  else if (fds == 0)
1454 	  {
1455 	   /*
1456 	    * Wait 2 seconds for a response to the local resolve; if nothing
1457 	    * comes in, do an additional domain resolution...
1458 	    */
1459 
1460 	    if (domainsent == 0 && strcasecmp(domain, "local."))
1461 	    {
1462 	      if (logit)
1463 		fprintf(stderr,
1464 		        "DEBUG: Resolving \"%s\", regtype=\"%s\", "
1465 			"domain=\"%s\"...\n", hostname, regtype, domain);
1466 
1467 	      domainref = ref;
1468 	      if (DNSServiceResolve(&domainref, kDNSServiceFlagsShareConnection, 0,
1469 				    hostname, regtype, domain, resolve_callback,
1470 				    &uribuf) == kDNSServiceErr_NoError)
1471 		domainsent = 1;
1472 	    }
1473 
1474 	   /*
1475 	    * If it hasn't resolved within 5 seconds set the offline-report
1476 	    * printer-state-reason...
1477 	    */
1478 
1479 	    if (logit && offline == 0 && time(NULL) > (start_time + 5))
1480 	    {
1481 	      fputs("STATE: +offline-report\n", stderr);
1482 	      offline = 1;
1483 	    }
1484 	  }
1485 	  else
1486 	  {
1487 	    if (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError)
1488 	    {
1489 	      uri = resolved_uri;
1490 	      break;
1491 	    }
1492 	  }
1493 	}
1494 
1495 	if (domainsent)
1496 	  DNSServiceRefDeallocate(domainref);
1497 
1498 	DNSServiceRefDeallocate(localref);
1499       }
1500 
1501       DNSServiceRefDeallocate(ref);
1502     }
1503 
1504     if (logit)
1505     {
1506       if (uri)
1507         fprintf(stderr, "DEBUG: Resolved as \"%s\"...\n", uri);
1508       else
1509         fputs("DEBUG: Unable to resolve URI!\n", stderr);
1510 
1511       fputs("STATE: -connecting-to-device,offline-report\n", stderr);
1512     }
1513 
1514 #else
1515    /*
1516     * No DNS-SD support...
1517     */
1518 
1519     uri = NULL;
1520 #endif /* HAVE_DNSSD */
1521 
1522     if (logit && !uri)
1523       _cupsLangPuts(stderr, _("Unable to find printer!\n"));
1524   }
1525 
1526   DEBUG_printf(("5_httpResolveURI: Returning \"%s\"", uri));
1527 
1528   return (uri);
1529 }
1530 
1531 
1532 /*
1533  * 'http_copy_decode()' - Copy and decode a URI.
1534  */
1535 
1536 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)1537 http_copy_decode(char       *dst,	/* O - Destination buffer */
1538                  const char *src,	/* I - Source pointer */
1539 		 int        dstsize,	/* I - Destination size */
1540 		 const char *term,	/* I - Terminating characters */
1541 		 int        decode)	/* I - Decode %-encoded values */
1542 {
1543   char	*ptr,				/* Pointer into buffer */
1544 	*end;				/* End of buffer */
1545   int	quoted;				/* Quoted character */
1546 
1547 
1548  /*
1549   * Copy the src to the destination until we hit a terminating character
1550   * or the end of the string.
1551   */
1552 
1553   for (ptr = dst, end = dst + dstsize - 1;
1554        *src && (!term || !strchr(term, *src));
1555        src ++)
1556     if (ptr < end)
1557     {
1558       if (*src == '%' && decode)
1559       {
1560         if (isxdigit(src[1] & 255) && isxdigit(src[2] & 255))
1561 	{
1562 	 /*
1563 	  * Grab a hex-encoded character...
1564 	  */
1565 
1566           src ++;
1567 	  if (isalpha(*src))
1568 	    quoted = (tolower(*src) - 'a' + 10) << 4;
1569 	  else
1570 	    quoted = (*src - '0') << 4;
1571 
1572           src ++;
1573 	  if (isalpha(*src))
1574 	    quoted |= tolower(*src) - 'a' + 10;
1575 	  else
1576 	    quoted |= *src - '0';
1577 
1578           *ptr++ = quoted;
1579 	}
1580 	else
1581 	{
1582 	 /*
1583 	  * Bad hex-encoded character...
1584 	  */
1585 
1586 	  *ptr = '\0';
1587 	  return (NULL);
1588 	}
1589       }
1590       else
1591 	*ptr++ = *src;
1592     }
1593 
1594   *ptr = '\0';
1595 
1596   return (src);
1597 }
1598 
1599 
1600 /*
1601  * 'http_copy_encode()' - Copy and encode a URI.
1602  */
1603 
1604 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)1605 http_copy_encode(char       *dst,	/* O - Destination buffer */
1606                  const char *src,	/* I - Source pointer */
1607 		 char       *dstend,	/* I - End of destination buffer */
1608                  const char *reserved,	/* I - Extra reserved characters */
1609 		 const char *term,	/* I - Terminating characters */
1610 		 int        encode)	/* I - %-encode reserved chars? */
1611 {
1612   static const char hex[] = "0123456789ABCDEF";
1613 
1614 
1615   while (*src && dst < dstend)
1616   {
1617     if (term && *src == *term)
1618       return (dst);
1619 
1620     if (encode && (*src == '%' || *src <= ' ' || *src & 128 ||
1621                    (reserved && strchr(reserved, *src))))
1622     {
1623      /*
1624       * Hex encode reserved characters...
1625       */
1626 
1627       if ((dst + 2) >= dstend)
1628         break;
1629 
1630       *dst++ = '%';
1631       *dst++ = hex[(*src >> 4) & 15];
1632       *dst++ = hex[*src & 15];
1633 
1634       src ++;
1635     }
1636     else
1637       *dst++ = *src++;
1638   }
1639 
1640   *dst = '\0';
1641 
1642   if (*src)
1643     return (NULL);
1644   else
1645     return (dst);
1646 }
1647 
1648 
1649 #ifdef HAVE_DNSSD
1650 /*
1651  * 'resolve_callback()' - Build a device URI for the given service name.
1652  */
1653 
1654 static void
resolve_callback(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)1655 resolve_callback(
1656     DNSServiceRef       sdRef,		/* I - Service reference */
1657     DNSServiceFlags     flags,		/* I - Results flags */
1658     uint32_t            interfaceIndex,	/* I - Interface number */
1659     DNSServiceErrorType errorCode,	/* I - Error, if any */
1660     const char          *fullName,	/* I - Full service name */
1661     const char          *hostTarget,	/* I - Hostname */
1662     uint16_t            port,		/* I - Port number */
1663     uint16_t            txtLen,		/* I - Length of TXT record */
1664     const unsigned char *txtRecord,	/* I - TXT record data */
1665     void                *context)	/* I - Pointer to URI buffer */
1666 {
1667   const char		*scheme;	/* URI scheme */
1668   char			rp[257];	/* Remote printer */
1669   const void		*value;		/* Value from TXT record */
1670   uint8_t		valueLen;	/* Length of value */
1671   _http_uribuf_t	*uribuf;	/* URI buffer */
1672 
1673 
1674   DEBUG_printf(("7resolve_callback(sdRef=%p, flags=%x, interfaceIndex=%u, "
1675 	        "errorCode=%d, fullName=\"%s\", hostTarget=\"%s\", port=%u, "
1676 	        "txtLen=%u, txtRecord=%p, context=%p)", sdRef, flags,
1677 	        interfaceIndex, errorCode, fullName, hostTarget, port, txtLen,
1678 	        txtRecord, context));
1679 
1680  /*
1681   * Figure out the scheme from the full name...
1682   */
1683 
1684   if (strstr(fullName, "._ipp") || strstr(fullName, "._fax-ipp"))
1685     scheme = "ipp";
1686   else if (strstr(fullName, "._printer."))
1687     scheme = "lpd";
1688   else if (strstr(fullName, "._pdl-datastream."))
1689     scheme = "socket";
1690   else
1691     scheme = "riousbprint";
1692 
1693  /*
1694   * Extract the "remote printer" key from the TXT record...
1695   */
1696 
1697   if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, "rp",
1698                                     &valueLen)) != NULL)
1699   {
1700    /*
1701     * Convert to resource by concatenating with a leading "/"...
1702     */
1703 
1704     rp[0] = '/';
1705     memcpy(rp + 1, value, valueLen);
1706     rp[valueLen + 1] = '\0';
1707   }
1708   else
1709     rp[0] = '\0';
1710 
1711  /*
1712   * Assemble the final device URI...
1713   */
1714 
1715   uribuf = (_http_uribuf_t *)context;
1716 
1717   httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, uribuf->bufsize, scheme,
1718                   NULL, hostTarget, ntohs(port), rp);
1719 
1720   DEBUG_printf(("8resolve_callback: Resolved URI is \"%s\"...",
1721                 uribuf->buffer));
1722 }
1723 #endif /* HAVE_DNSSD */
1724 
1725 
1726 /*
1727  * End of "$Id: http-support.c 9322 2010-10-01 22:40:38Z mike $".
1728  */
1729