Coverage Report

Created: 2026-04-01 07:17

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