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