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