/*
- * "$Id: http-support.c 5138 2006-02-21 10:49:06Z mike $"
+ * "$Id: http-support.c 7952 2008-09-17 00:56:20Z mike $"
*
* HTTP support routines for the Common UNIX Printing System (CUPS) scheduler.
*
- * Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ * Copyright 2007-2008 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
- * property of Easy Software Products and are protected by Federal
- * copyright law. Distribution and use rights are outlined in the file
- * "LICENSE.txt" which should have been included with this file. If this
- * file is missing or damaged please contact Easy Software Products
- * at:
- *
- * Attn: CUPS Licensing Information
- * Easy Software Products
- * 44141 Airport View Drive, Suite 204
- * Hollywood, Maryland 20636 USA
- *
- * Voice: (301) 373-9600
- * EMail: cups-info@cups.org
- * WWW: http://www.cups.org
+ * property of Apple Inc. and are protected by Federal copyright
+ * law. Distribution and use rights are outlined in the file "LICENSE.txt"
+ * which should have been included with this file. If this file is
+ * file is missing or damaged, see the license at "http://www.cups.org/".
*
* This file is subject to the Apple OS-Developed Software exception.
*
* httpStatus() - Return a short string describing a HTTP status code.
* _cups_hstrerror() - hstrerror() emulation function for Solaris and
* others...
+ * _httpEncodeURI() - Percent-encode a HTTP request URI.
+ * _httpResolveURI() - Resolve a DNS-SD URI.
* http_copy_decode() - Copy and decode a URI.
* http_copy_encode() - Copy and encode a URI.
+ * resolve_callback() - Build a device URI for the given service name.
*/
/*
#include "debug.h"
#include "globals.h"
#include <stdlib.h>
+#ifdef HAVE_DNSSD
+# include <dns_sd.h>
+#endif /* HAVE_DNSSD */
+
+
+/*
+ * Local types...
+ */
+
+typedef struct _http_uribuf_s /* URI buffer */
+{
+ char *buffer; /* Pointer to buffer */
+ size_t bufsize; /* Size of buffer */
+} _http_uribuf_t;
/*
static const char * const http_days[7] =
{
- "Sun", "Mon",
+ "Sun",
+ "Mon",
"Tue",
"Wed",
"Thu",
static char *http_copy_encode(char *dst, const char *src,
char *dstend, const char *reserved,
const char *term, int encode);
+#ifdef HAVE_DNSSD
+static void resolve_callback(DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *fullName,
+ const char *hostTarget,
+ uint16_t port, uint16_t txtLen,
+ const unsigned char *txtRecord,
+ void *context);
+#endif /* HAVE_DNSSD */
/*
if (!ptr)
goto assemble_overflow;
- if (!strcmp(scheme, "mailto:"))
+ if (!strcmp(scheme, "mailto"))
{
/*
* mailto: only has :, no //...
/*
* Then add the hostname. Since IPv6 is a particular pain to deal
- * with, we have several special cases to deal with... If we get
+ * with, we have several special cases to deal with. If we get
* an IPv6 address with brackets around it, assume it is already in
- * URI format...
+ * URI format. Since DNS-SD service names can sometimes look like
+ * raw IPv6 addresses, we specifically look for "._tcp" in the name,
+ * too...
*/
- if (host[0] != '[' && strchr(host, ':'))
+ if (host[0] != '[' && strchr(host, ':') && !strstr(host, "._tcp"))
{
/*
- * We have an IPv6 address...
+ * We have a raw IPv6 address...
*/
if (strchr(host, '%'))
* Otherwise, just copy the host string...
*/
- ptr = http_copy_encode(ptr, host, end, NULL, NULL,
+ ptr = http_copy_encode(ptr, host, end, ":/?#[]@", NULL,
encoding & HTTP_URI_CODING_HOSTNAME);
if (!ptr)
httpEncode64(char *out, /* I - String to write to */
const char *in) /* I - String to read from */
{
- return (httpEncode64_2(out, 512, in, strlen(in)));
+ return (httpEncode64_2(out, 512, in, (int)strlen(in)));
}
if (outptr < outend)
*outptr ++ = base64[(in[0] & 255) >> 2];
+
if (outptr < outend)
- *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
+ {
+ if (inlen > 1)
+ *outptr ++ = base64[(((in[0] & 255) << 4) | ((in[1] & 255) >> 4)) & 63];
+ else
+ *outptr ++ = base64[((in[0] & 255) << 4) & 63];
+ }
in ++;
inlen --;
}
if (outptr < outend)
- *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
+ {
+ if (inlen > 1)
+ *outptr ++ = base64[(((in[0] & 255) << 2) | ((in[1] & 255) >> 6)) & 63];
+ else
+ *outptr ++ = base64[((in[0] & 255) << 2) & 63];
+ }
in ++;
inlen --;
else
{
/*
- * Grab hostname or IPv4 address...
+ * Validate the hostname or IPv4 address first...
+ */
+
+ for (ptr = (char *)uri; *ptr; ptr ++)
+ if (strchr(":?/", *ptr))
+ break;
+ else if (!strchr("abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "-._~"
+ "%"
+ "!$&'()*+,;=\\", *ptr))
+ {
+ *host = '\0';
+ return (HTTP_URI_BAD_HOSTNAME);
+ }
+
+ /*
+ * Then copy the hostname or IPv4 address to the buffer...
*/
uri = http_copy_decode(host, uri, hostlen, ":?/",
*host = '\0';
return (HTTP_URI_BAD_HOSTNAME);
}
-
- /*
- * Validate value...
- */
-
- for (ptr = host; *ptr; ptr ++)
- if (!strchr("abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789"
- "-._~"
- "!$&'()*+,;=", *ptr))
- {
- *host = '\0';
- return (HTTP_URI_BAD_HOSTNAME);
- }
}
/*
*port = strtol(uri + 1, (char **)&uri, 10);
- if (*uri != '/')
+ if (*uri != '/' && *uri)
{
*port = 0;
return (HTTP_URI_BAD_PORT);
char *resptr = resource + strlen(resource);
- uri = http_copy_decode(resptr, uri, resourcelen - (resptr - resource),
+ uri = http_copy_decode(resptr, uri, resourcelen - (int)(resptr - resource),
NULL, decoding & HTTP_URI_CODING_QUERY);
}
}
/*
* 'httpStatus()' - Return a short string describing a HTTP status code.
+ *
+ * The returned string is localized to the current POSIX locale and is based
+ * on the status strings defined in RFC 2616.
*/
-const char * /* O - String or NULL */
+const char * /* O - Localized status string */
httpStatus(http_status_t status) /* I - HTTP status code */
{
+ const char *s; /* Status string */
+ _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+
+
+ if (!cg->lang_default)
+ cg->lang_default = cupsLangDefault();
+
switch (status)
{
case HTTP_CONTINUE :
- return ("Continue");
+ s = _("Continue");
+ break;
case HTTP_SWITCHING_PROTOCOLS :
- return ("Switching Protocols");
+ s = _("Switching Protocols");
+ break;
case HTTP_OK :
- return ("OK");
+ s = _("OK");
+ break;
case HTTP_CREATED :
- return ("Created");
+ s = _("Created");
+ break;
case HTTP_ACCEPTED :
- return ("Accepted");
+ s = _("Accepted");
+ break;
case HTTP_NO_CONTENT :
- return ("No Content");
+ s = _("No Content");
+ break;
+ case HTTP_MOVED_PERMANENTLY :
+ s = _("Moved Permanently");
+ break;
+ case HTTP_SEE_OTHER :
+ s = _("See Other");
+ break;
case HTTP_NOT_MODIFIED :
- return ("Not Modified");
+ s = _("Not Modified");
+ break;
case HTTP_BAD_REQUEST :
- return ("Bad Request");
+ s = _("Bad Request");
+ break;
case HTTP_UNAUTHORIZED :
- return ("Unauthorized");
+ s = _("Unauthorized");
+ break;
case HTTP_FORBIDDEN :
- return ("Forbidden");
+ s = _("Forbidden");
+ break;
case HTTP_NOT_FOUND :
- return ("Not Found");
+ s = _("Not Found");
+ break;
case HTTP_REQUEST_TOO_LARGE :
- return ("Request Entity Too Large");
+ s = _("Request Entity Too Large");
+ break;
case HTTP_URI_TOO_LONG :
- return ("URI Too Long");
+ s = _("URI Too Long");
+ break;
case HTTP_UPGRADE_REQUIRED :
- return ("Upgrade Required");
+ s = _("Upgrade Required");
+ break;
case HTTP_NOT_IMPLEMENTED :
- return ("Not Implemented");
+ s = _("Not Implemented");
+ break;
case HTTP_NOT_SUPPORTED :
- return ("Not Supported");
+ s = _("Not Supported");
+ break;
+ case HTTP_EXPECTATION_FAILED :
+ s = _("Expectation Failed");
+ break;
+ case HTTP_SERVICE_UNAVAILABLE :
+ s = _("Service Unavailable");
+ break;
+
default :
- return ("Unknown");
+ s = _("Unknown");
+ break;
}
+
+ return (_cupsLangString(cg->lang_default, s));
}
#endif /* !HAVE_HSTRERROR */
+/*
+ * '_httpEncodeURI()' - Percent-encode a HTTP request URI.
+ */
+
+char * /* O - Encoded URI */
+_httpEncodeURI(char *dst, /* I - Destination buffer */
+ const char *src, /* I - Source URI */
+ size_t dstsize) /* I - Size of destination buffer */
+{
+ http_copy_encode(dst, src, dst + dstsize - 1, NULL, NULL, 1);
+ return (dst);
+}
+
+
+/*
+ * '_httpResolveURI()' - Resolve a DNS-SD URI.
+ */
+
+const char * /* O - Resolved URI */
+_httpResolveURI(
+ const char *uri, /* I - DNS-SD URI */
+ char *resolved_uri, /* I - Buffer for resolved URI */
+ size_t resolved_size, /* I - Size of URI buffer */
+ int log) /* I - Log progress to stderr? */
+{
+ char scheme[32], /* URI components... */
+ userpass[256],
+ hostname[1024],
+ resource[1024];
+ int port;
+#ifdef DEBUG
+ http_uri_status_t status; /* URI decode status */
+#endif /* DEBUG */
+
+
+ DEBUG_printf(("_httpResolveURI(uri=\"%s\", resolved_uri=%p, "
+ "resolved_size=" CUPS_LLFMT ")\n", uri, resolved_uri,
+ CUPS_LLCAST resolved_size));
+
+ /*
+ * Get the device URI...
+ */
+
+#ifdef DEBUG
+ if ((status = httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
+ sizeof(scheme), userpass, sizeof(userpass),
+ hostname, sizeof(hostname), &port, resource,
+ sizeof(resource))) < HTTP_URI_OK)
+#else
+ if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, scheme,
+ sizeof(scheme), userpass, sizeof(userpass),
+ hostname, sizeof(hostname), &port, resource,
+ sizeof(resource)) < HTTP_URI_OK)
+#endif /* DEBUG */
+ {
+ if (log)
+ _cupsLangPrintf(stderr, _("Bad device URI \"%s\"!\n"), uri);
+
+ DEBUG_printf(("_httpResolveURI: httpSeparateURI returned %d!\n", status));
+ DEBUG_puts("_httpResolveURI: Returning NULL");
+ return (NULL);
+ }
+
+ /*
+ * Resolve it as needed...
+ */
+
+ if (strstr(hostname, "._tcp"))
+ {
+#ifdef HAVE_DNSSD
+ DNSServiceRef ref; /* DNS-SD service reference */
+ char *regtype, /* Pointer to type in hostname */
+ *domain; /* Pointer to domain in hostname */
+ _http_uribuf_t uribuf; /* URI buffer */
+
+ /*
+ * Separate the hostname into service name, registration type, and domain...
+ */
+
+ for (regtype = strstr(hostname, "._tcp") - 2;
+ regtype > hostname;
+ regtype --)
+ if (regtype[0] == '.' && regtype[1] == '_')
+ {
+ /*
+ * Found ._servicetype in front of ._tcp...
+ */
+
+ *regtype++ = '\0';
+ break;
+ }
+
+ if (regtype <= hostname)
+ {
+ DEBUG_puts("_httpResolveURI: Bad hostname, returning NULL");
+ return (NULL);
+ }
+
+ domain = regtype + strlen(regtype) - 1;
+ if (domain > regtype && *domain == '.')
+ *domain = '\0';
+
+ for (domain = strchr(regtype, '.');
+ domain;
+ domain = strchr(domain + 1, '.'))
+ if (domain[1] != '_')
+ break;
+
+ if (domain)
+ *domain++ = '\0';
+
+ uribuf.buffer = resolved_uri;
+ uribuf.bufsize = resolved_size;
+
+ resolved_uri[0] = '\0';
+
+ DEBUG_printf(("_httpResolveURI: Resolving hostname=\"%s\", regtype=\"%s\", "
+ "domain=\"%s\"\n", hostname, regtype, domain));
+ if (log)
+ {
+ fputs("STATE: +connecting-to-device\n", stderr);
+ _cupsLangPrintf(stderr, _("INFO: Looking for \"%s\"...\n"), hostname);
+ }
+
+ if (DNSServiceResolve(&ref, 0, 0, hostname, regtype, domain,
+ resolve_callback,
+ &uribuf) == kDNSServiceErr_NoError)
+ {
+ if (DNSServiceProcessResult(ref) != kDNSServiceErr_NoError &&
+ resolved_uri[0])
+ uri = NULL;
+ else
+ uri = resolved_uri;
+
+ DNSServiceRefDeallocate(ref);
+ }
+ else
+ uri = NULL;
+
+ if (log)
+ fputs("STATE: -connecting-to-device\n", stderr);
+
+#else
+ /*
+ * No DNS-SD support...
+ */
+
+ uri = NULL;
+#endif /* HAVE_DNSSD */
+
+ if (log && !uri)
+ _cupsLangPuts(stderr, _("Unable to find printer!\n"));
+ }
+
+ DEBUG_printf(("_httpResolveURI: Returning \"%s\"\n", uri));
+
+ return (uri);
+}
+
+
/*
* 'http_copy_decode()' - Copy and decode a URI.
*/
const char *term, /* I - Terminating characters */
int encode) /* I - %-encode reserved chars? */
{
- static const char *hex = "0123456789ABCDEF";
+ static const char hex[] = "0123456789ABCDEF";
while (*src && dst < dstend)
*dst++ = *src++;
}
+ *dst = '\0';
+
if (*src)
return (NULL);
else
}
+#ifdef HAVE_DNSSD
+/*
+ * 'resolve_callback()' - Build a device URI for the given service name.
+ */
+
+static void
+resolve_callback(
+ DNSServiceRef sdRef, /* I - Service reference */
+ DNSServiceFlags flags, /* I - Results flags */
+ uint32_t interfaceIndex, /* I - Interface number */
+ DNSServiceErrorType errorCode, /* I - Error, if any */
+ const char *fullName, /* I - Full service name */
+ const char *hostTarget, /* I - Hostname */
+ uint16_t port, /* I - Port number */
+ uint16_t txtLen, /* I - Length of TXT record */
+ const unsigned char *txtRecord, /* I - TXT record data */
+ void *context) /* I - Pointer to URI buffer */
+{
+ const char *scheme; /* URI scheme */
+ char rp[257]; /* Remote printer */
+ const void *value; /* Value from TXT record */
+ uint8_t valueLen; /* Length of value */
+ _http_uribuf_t *uribuf; /* URI buffer */
+
+
+ DEBUG_printf(("resolve_callback(sdRef=%p, flags=%x, interfaceIndex=%u, "
+ "errorCode=%d, fullName=\"%s\", hostTarget=\"%s\", port=%u, "
+ "txtLen=%u, txtRecord=%p, context=%p)\n", sdRef, flags,
+ interfaceIndex, errorCode, fullName, hostTarget, port, txtLen,
+ txtRecord, context));
+
+ /*
+ * Figure out the scheme from the full name...
+ */
+
+ if (strstr(fullName, "._ipp"))
+ scheme = "ipp";
+ else if (strstr(fullName, "._printer."))
+ scheme = "lpd";
+ else if (strstr(fullName, "._pdl-datastream."))
+ scheme = "socket";
+ else
+ scheme = "riousbprint";
+
+ /*
+ * Extract the "remote printer" key from the TXT record...
+ */
+
+ if ((value = TXTRecordGetValuePtr(txtLen, txtRecord, "rp",
+ &valueLen)) != NULL)
+ {
+ /*
+ * Convert to resource by concatenating with a leading "/"...
+ */
+
+ rp[0] = '/';
+ memcpy(rp + 1, value, valueLen);
+ rp[valueLen + 1] = '\0';
+ }
+ else
+ rp[0] = '\0';
+
+ /*
+ * Assemble the final device URI...
+ */
+
+ uribuf = (_http_uribuf_t *)context;
+
+ httpAssembleURI(HTTP_URI_CODING_ALL, uribuf->buffer, uribuf->bufsize, scheme,
+ NULL, hostTarget, ntohs(port), rp);
+
+ DEBUG_printf(("resolve_callback: Resolved URI is \"%s\"...\n",
+ uribuf->buffer));
+}
+#endif /* HAVE_DNSSD */
+
+
/*
- * End of "$Id: http-support.c 5138 2006-02-21 10:49:06Z mike $".
+ * End of "$Id: http-support.c 7952 2008-09-17 00:56:20Z mike $".
*/