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