X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=scheduler%2Fclient.c;h=02e5fafb8c9e2afffff519f2ffa9ff67d7f0be0f;hb=503b54c9302c8de6207e079a80a89a787eb612ea;hp=f07a491d3379185d4a2d97acd43efb5b03d100b6;hpb=41e6c1f1fdc13479cc65bb64e9ae7924c35ca08f;p=thirdparty%2Fcups.git diff --git a/scheduler/client.c b/scheduler/client.c index f07a491d3..02e5fafb8 100644 --- a/scheduler/client.c +++ b/scheduler/client.c @@ -1,52 +1,25 @@ /* - * "$Id$" + * Client routines for the CUPS scheduler. * - * Client routines for the CUPS scheduler. + * Copyright 2007-2015 by Apple Inc. + * Copyright 1997-2007 by Easy Software Products, all rights reserved. * - * Copyright 2007-2013 by Apple Inc. - * Copyright 1997-2007 by Easy Software Products, all rights reserved. + * This file contains Kerberos support code, copyright 2006 by + * Jelmer Vernooij. * - * This file contains Kerberos support code, copyright 2006 by - * Jelmer Vernooij. - * - * These coded instructions, statements, and computer programs are the - * 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/". - * - * Contents: - * - * cupsdAcceptClient() - Accept a new client. - * cupsdCloseAllClients() - Close all remote clients immediately. - * cupsdCloseClient() - Close a remote client. - * cupsdFlushHeader() - Flush the header fields to the client. - * cupsdReadClient() - Read data from a client. - * cupsdSendCommand() - Send output from a command via HTTP. - * cupsdSendError() - Send an error message via HTTP. - * cupsdSendHeader() - Send an HTTP request. - * cupsdUpdateCGI() - Read status messages from CGI scripts and - * programs. - * cupsdWriteClient() - Write data to a client as needed. - * check_if_modified() - Decode an "If-Modified-Since" line. - * compare_clients() - Compare two client connections. - * data_ready() - Check whether data is available from a client. - * get_file() - Get a filename and state info. - * install_cupsd_conf() - Install a configuration file. - * is_cgi() - Is the resource a CGI script/program? - * is_path_absolute() - Is a path absolute and free of relative elements - * (i.e. ".."). - * pipe_command() - Pipe the output of a command to the remote - * client. - * valid_host() - Is the Host: field valid? - * write_file() - Send a file via HTTP. - * write_pipe() - Flag that data is available on the CGI pipe. + * These coded instructions, statements, and computer programs are the + * 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/". */ /* * Include necessary headers... */ +#define _CUPS_NO_DEPRECATED +#define _HTTP_NO_PRIVATE #include "cupsd.h" #ifdef __APPLE__ @@ -57,39 +30,6 @@ #endif /* HAVE_TCPD_H */ -/* - * Local globals... - */ - -static const char * const http_states[] = - { /* HTTP state strings */ - "HTTP_STATE_ERROR", - "HTTP_STATE_WAITING", - "HTTP_STATE_OPTIONS", - "HTTP_STATE_GET", - "HTTP_STATE_GET_SEND", - "HTTP_STATE_HEAD", - "HTTP_STATE_POST", - "HTTP_STATE_POST_RECV", - "HTTP_STATE_POST_SEND", - "HTTP_STATE_PUT", - "HTTP_STATE_PUT_RECV", - "HTTP_STATE_DELETE", - "HTTP_STATE_TRACE", - "HTTP_STATE_CONNECT", - "HTTP_STATE_STATUS", - "HTTP_STATE_UNKNOWN_METHOD", - "HTTP_STATE_UNKNOWN_VERSION" - }; -static const char * const ipp_states[] = - { /* IPP state strings */ - "IPP_IDLE", - "IPP_HEADER", - "IPP_ATTRIBUTE", - "IPP_DATA" - }; - - /* * Local functions... */ @@ -98,9 +38,11 @@ static int check_if_modified(cupsd_client_t *con, struct stat *filestats); static int compare_clients(cupsd_client_t *a, cupsd_client_t *b, void *data); -static int data_ready(cupsd_client_t *con); +#ifdef HAVE_SSL +static int cupsd_start_tls(cupsd_client_t *con, http_encryption_t e); +#endif /* HAVE_SSL */ static char *get_file(cupsd_client_t *con, struct stat *filestats, - char *filename, int len); + char *filename, size_t len); static http_status_t install_cupsd_conf(cupsd_client_t *con); static int is_cgi(cupsd_client_t *con, const char *filename, struct stat *filestats, mime_type_t *type); @@ -121,14 +63,12 @@ static void write_pipe(cupsd_client_t *con); void cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ { + const char *hostname; /* Hostname of client */ + char name[256]; /* Hostname of client */ int count; /* Count of connections on a host */ - int val; /* Parameter value */ cupsd_client_t *con, /* New client pointer */ *tempcon; /* Temporary client pointer */ - http_addrlist_t *addrlist, /* List of adddresses for host */ - *addr; /* Current address */ socklen_t addrlen; /* Length of address */ - char *hostname; /* Hostname for address */ http_addr_t temp; /* Temporary address variable */ static time_t last_dos = 0; /* Time of last DoS attack */ #ifdef HAVE_TCPD_H @@ -136,9 +76,7 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ #endif /* HAVE_TCPD_H */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "cupsdAcceptClient(lis=%p(%d)) Clients=%d", - lis, lis->fd, cupsArrayCount(Clients)); + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAcceptClient(lis=%p(%d)) Clients=%d", lis, lis->fd, cupsArrayCount(Clients)); /* * Make sure we don't have a full set of clients already... @@ -180,20 +118,14 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ return; } - con->file = -1; - con->http.activity = time(NULL); - con->http.hostaddr = &(con->clientaddr); - con->http.wait_value = 10000; - con->http.mode = _HTTP_MODE_SERVER; - /* * Accept the client and get the remote address... */ - addrlen = sizeof(http_addr_t); + con->number = ++ LastClientNumber; + con->file = -1; - if ((con->http.fd = accept(lis->fd, (struct sockaddr *)con->http.hostaddr, - &addrlen)) < 0) + if ((con->http = httpAcceptConnection(lis->fd, 0)) == NULL) { if (errno == ENFILE || errno == EMFILE) cupsdPauseListening(); @@ -206,23 +138,15 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ } /* - * Save the connected port number... + * Save the connected address and port number... */ - _httpAddrSetPort(con->http.hostaddr, httpAddrPort(&(lis->address))); + addrlen = sizeof(con->clientaddr); -#ifdef AF_INET6 - /* - * Convert IPv4 over IPv6 addresses (::ffff:n.n.n.n) to IPv4 forms we - * can more easily use... - */ + if (getsockname(httpGetFd(con->http), (struct sockaddr *)&con->clientaddr, &addrlen) || addrlen == 0) + con->clientaddr = lis->address; - if (lis->address.addr.sa_family == AF_INET6 && - con->http.hostaddr->ipv6.sin6_addr.s6_addr32[0] == 0 && - con->http.hostaddr->ipv6.sin6_addr.s6_addr32[1] == 0 && - ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2]) == 0xffff) - con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2] = 0; -#endif /* AF_INET6 */ + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Server address is \"%s\".", httpAddrString(&con->clientaddr, name, sizeof(name))); /* * Check the number of clients on the same address... @@ -231,7 +155,7 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ for (count = 0, tempcon = (cupsd_client_t *)cupsArrayFirst(Clients); tempcon; tempcon = (cupsd_client_t *)cupsArrayNext(Clients)) - if (httpAddrEqual(tempcon->http.hostaddr, con->http.hostaddr)) + if (httpAddrEqual(httpGetAddress(tempcon->http), httpGetAddress(con->http))) { count ++; if (count >= MaxClientsPerHost) @@ -245,18 +169,12 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ last_dos = time(NULL); cupsdLogMessage(CUPSD_LOG_WARN, "Possible DoS attack - more than %d clients connecting " - "from %s!", + "from %s.", MaxClientsPerHost, - httpAddrString(con->http.hostaddr, con->http.hostname, - sizeof(con->http.hostname))); + httpGetHostname(con->http, name, sizeof(name))); } -#ifdef WIN32 - closesocket(con->http.fd); -#else - close(con->http.fd); -#endif /* WIN32 */ - + httpClose(con->http); free(con); return; } @@ -265,31 +183,10 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ * Get the hostname or format the IP address as needed... */ - if (httpAddrLocalhost(con->http.hostaddr)) - { - /* - * Map accesses from the loopback interface to "localhost"... - */ - - strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname)); - hostname = con->http.hostname; - } + if (HostNameLookups) + hostname = httpResolveHostname(con->http, NULL, 0); else - { - /* - * Map accesses from the same host to the server name. - */ - - if (HostNameLookups) - hostname = httpAddrLookup(con->http.hostaddr, con->http.hostname, - sizeof(con->http.hostname)); - else - { - hostname = NULL; - httpAddrString(con->http.hostaddr, con->http.hostname, - sizeof(con->http.hostname)); - } - } + hostname = httpGetHostname(con->http, NULL, 0); if (hostname == NULL && HostNameLookups == 2) { @@ -297,15 +194,11 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ * Can't have an unresolved IP address with double-lookups enabled... */ -#ifdef WIN32 - closesocket(con->http.fd); -#else - close(con->http.fd); -#endif /* WIN32 */ + httpClose(con->http); - cupsdLogMessage(CUPSD_LOG_WARN, + cupsdLogClient(con, CUPSD_LOG_WARN, "Name lookup failed - connection from %s closed!", - con->http.hostname); + httpGetHostname(con->http, NULL, 0)); free(con); return; @@ -317,15 +210,17 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ * Do double lookups as needed... */ - if ((addrlist = httpAddrGetList(con->http.hostname, AF_UNSPEC, NULL)) - != NULL) + http_addrlist_t *addrlist, /* List of addresses */ + *addr; /* Current address */ + + if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, NULL)) != NULL) { /* * See if the hostname maps to the same IP address... */ for (addr = addrlist; addr; addr = addr->next) - if (httpAddrEqual(con->http.hostaddr, &(addr->addr))) + if (httpAddrEqual(httpGetAddress(con->http), &(addr->addr))) break; } else @@ -340,15 +235,11 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ * with double-lookups enabled... */ -#ifdef WIN32 - closesocket(con->http.fd); -#else - close(con->http.fd); -#endif /* WIN32 */ + httpClose(con->http); - cupsdLogMessage(CUPSD_LOG_WARN, + cupsdLogClient(con, CUPSD_LOG_WARN, "IP lookup failed - connection from %s closed!", - con->http.hostname); + httpGetHostname(con->http, NULL, 0)); free(con); return; } @@ -359,74 +250,71 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ * See if the connection is denied by TCP wrappers... */ - request_init(&wrap_req, RQ_DAEMON, "cupsd", RQ_FILE, con->http.fd, NULL); + request_init(&wrap_req, RQ_DAEMON, "cupsd", RQ_FILE, httpGetFd(con->http), + NULL); fromhost(&wrap_req); if (!hosts_access(&wrap_req)) { -#ifdef WIN32 - closesocket(con->http.fd); -#else - close(con->http.fd); -#endif /* WIN32 */ + httpClose(con->http); - cupsdLogMessage(CUPSD_LOG_WARN, + cupsdLogClient(con, CUPSD_LOG_WARN, "Connection from %s refused by /etc/hosts.allow and " - "/etc/hosts.deny rules.", con->http.hostname); + "/etc/hosts.deny rules.", httpGetHostname(con->http, NULL, 0)); free(con); return; } #endif /* HAVE_TCPD_H */ #ifdef AF_LOCAL - if (con->http.hostaddr->addr.sa_family == AF_LOCAL) + if (httpAddrFamily(httpGetAddress(con->http)) == AF_LOCAL) { # ifdef __APPLE__ socklen_t peersize; /* Size of peer credentials */ pid_t peerpid; /* Peer process ID */ - char name[256]; /* Name of process */ + char peername[256]; /* Name of process */ peersize = sizeof(peerpid); - if (!getsockopt(con->http.fd, SOL_LOCAL, LOCAL_PEERPID, &peerpid, - &peersize)) + if (!getsockopt(httpGetFd(con->http), SOL_LOCAL, LOCAL_PEERPID, &peerpid, + &peersize)) { - if (!proc_name(peerpid, name, sizeof(name))) - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] Accepted from %s (Domain ???[%d])", - con->http.fd, con->http.hostname, (int)peerpid); + if (!proc_name((int)peerpid, peername, sizeof(peername))) + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Accepted from %s (Domain ???[%d])", + httpGetHostname(con->http, NULL, 0), (int)peerpid); else - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] Accepted from %s (Domain %s[%d])", - con->http.fd, con->http.hostname, name, (int)peerpid); + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Accepted from %s (Domain %s[%d])", + httpGetHostname(con->http, NULL, 0), peername, (int)peerpid); } else # endif /* __APPLE__ */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Accepted from %s (Domain)", - con->http.fd, con->http.hostname); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Accepted from %s (Domain)", + httpGetHostname(con->http, NULL, 0)); } else #endif /* AF_LOCAL */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Accepted from %s:%d (IPv%d)", - con->http.fd, con->http.hostname, - httpAddrPort(con->http.hostaddr), - _httpAddrFamily(con->http.hostaddr) == AF_INET ? 4 : 6); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Accepted from %s:%d (IPv%d)", + httpGetHostname(con->http, NULL, 0), + httpAddrPort(httpGetAddress(con->http)), + httpAddrFamily(httpGetAddress(con->http)) == AF_INET ? 4 : 6); /* * Get the local address the client connected to... */ addrlen = sizeof(temp); - if (getsockname(con->http.fd, (struct sockaddr *)&temp, &addrlen)) + if (getsockname(httpGetFd(con->http), (struct sockaddr *)&temp, &addrlen)) { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get local address - %s", - strerror(errno)); + cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to get local address - %s", + strerror(errno)); strlcpy(con->servername, "localhost", sizeof(con->servername)); con->serverport = LocalPort; } #ifdef AF_LOCAL - else if (_httpAddrFamily(&temp) == AF_LOCAL) + else if (httpAddrFamily(&temp) == AF_LOCAL) { strlcpy(con->servername, "localhost", sizeof(con->servername)); con->serverport = LocalPort; @@ -450,29 +338,14 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ cupsArrayAdd(Clients, con); - /* - * Using TCP_NODELAY improves responsiveness, especially on systems with a slow - * loopback interface. Since we write large buffers when sending print files - * and requests there shouldn't be any performance penalty for this... - */ - - val = 1; - setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); - - /* - * Close this file on all execs... - */ - - fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC); - /* * Add the socket to the server select. */ - cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con); + cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, NULL, + con); - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Waiting for request.", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for request."); /* * Temporarily suspend accept()'s until we lose a client... @@ -486,15 +359,13 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ * See if we are connecting on a secure port... */ - if (lis->encryption == HTTP_ENCRYPT_ALWAYS) + if (lis->encryption == HTTP_ENCRYPTION_ALWAYS) { /* * https connection; go secure... */ - con->http.encryption = HTTP_ENCRYPT_ALWAYS; - - if (!cupsdStartTLS(con)) + if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS)) cupsdCloseClient(con); } else @@ -513,8 +384,7 @@ cupsdCloseAllClients(void) cupsd_client_t *con; /* Current client */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d", - cupsArrayCount(Clients)); + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d", cupsArrayCount(Clients)); for (con = (cupsd_client_t *)cupsArrayFirst(Clients); con; @@ -532,36 +402,18 @@ int /* O - 1 if partial close, 0 if fully closed */ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ { int partial; /* Do partial close for SSL? */ -#ifdef HAVE_LIBSSL -#elif defined(HAVE_GNUTLS) -# elif defined(HAVE_CDSASSL) -#endif /* HAVE_LIBSSL */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Closing connection.", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing connection."); /* * Flush pending writes before closing... */ - httpFlushWrite(HTTP(con)); + httpFlushWrite(con->http); partial = 0; -#ifdef HAVE_SSL - /* - * Shutdown encryption as needed... - */ - - if (con->http.tls) - { - partial = 1; - - cupsdEndTLS(con); - } -#endif /* HAVE_SSL */ - if (con->pipe_pid != 0) { /* @@ -584,22 +436,31 @@ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ * Close the socket and clear the file from the input set for select()... */ - if (con->http.fd >= 0) + if (httpGetFd(con->http) >= 0) { cupsArrayRemove(ActiveClients, con); cupsdSetBusyState(); +#ifdef HAVE_SSL + /* + * Shutdown encryption as needed... + */ + + if (httpIsEncrypted(con->http)) + partial = 1; +#endif /* HAVE_SSL */ + if (partial) { /* * Only do a partial close so that the encrypted client gets everything. */ - shutdown(con->http.fd, 0); - cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con); + httpShutdown(con->http); + cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, + NULL, con); - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Waiting for socket close.", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for socket close."); } else { @@ -607,9 +468,9 @@ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ * Shut the socket down fully... */ - cupsdRemoveSelect(con->http.fd); - close(con->http.fd); - con->http.fd = -1; + cupsdRemoveSelect(httpGetFd(con->http)); + httpClose(con->http); + con->http = NULL; } } @@ -619,13 +480,16 @@ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ * Free memory... */ - if (con->http.input_set) - free(con->http.input_set); + cupsdRemoveSelect(httpGetFd(con->http)); - httpClearCookie(HTTP(con)); - httpClearFields(HTTP(con)); + httpClose(con->http); + + if (con->filename) + { + unlink(con->filename); + cupsdClearString(&con->filename); + } - cupsdClearString(&con->filename); cupsdClearString(&con->command); cupsdClearString(&con->options); cupsdClearString(&con->query_string); @@ -677,21 +541,6 @@ cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ } -/* - * 'cupsdFlushHeader()' - Flush the header fields to the client. - */ - -int /* I - Bytes written or -1 on error */ -cupsdFlushHeader(cupsd_client_t *con) /* I - Client to flush to */ -{ - int bytes = httpFlushWrite(HTTP(con)); - - con->http.data_encoding = HTTP_ENCODING_LENGTH; - - return (bytes); -} - - /* * 'cupsdReadClient()' - Read data from a client. */ @@ -700,11 +549,8 @@ void cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ { char line[32768], /* Line from client... */ - operation[64], /* Operation code from socket */ - version[64], /* HTTP version number string */ locale[64], /* Locale */ *ptr; /* Pointer into strings */ - int major, minor; /* HTTP version numbers */ http_status_t status; /* Transfer status */ ipp_state_t ipp_state; /* State of IPP transfer */ int bytes; /* Number of bytes to POST */ @@ -716,25 +562,34 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ static unsigned request_id = 0; /* Request ID for temp files */ - status = HTTP_CONTINUE; - - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] cupsdReadClient " - "error=%d, " - "used=%d, " - "state=%s, " - "data_encoding=HTTP_ENCODING_%s, " - "data_remaining=" CUPS_LLFMT ", " - "request=%p(%s), " - "file=%d", - con->http.fd, con->http.error, con->http.used, - http_states[con->http.state + 1], - con->http.data_encoding == HTTP_ENCODING_CHUNKED ? - "CHUNKED" : "LENGTH", - CUPS_LLCAST con->http.data_remaining, - con->request, - con->request ? ipp_states[con->request->state] : "", - con->file); + status = HTTP_STATUS_CONTINUE; + + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cupsdReadClient: error=%d, used=%d, state=%s, data_encoding=HTTP_ENCODING_%s, data_remaining=" CUPS_LLFMT ", request=%p(%s), file=%d", httpError(con->http), (int)httpGetReady(con->http), httpStateString(httpGetState(con->http)), httpIsChunked(con->http) ? "CHUNKED" : "LENGTH", CUPS_LLCAST httpGetRemaining(con->http), con->request, con->request ? ippStateString(ippGetState(con->request)) : "", con->file); + + if (httpGetState(con->http) == HTTP_STATE_GET_SEND || + httpGetState(con->http) == HTTP_STATE_POST_SEND || + httpGetState(con->http) == HTTP_STATE_STATUS) + { + /* + * If we get called in the wrong state, then something went wrong with the + * connection and we need to shut it down... + */ + + if (!httpGetReady(con->http) && recv(httpGetFd(con->http), buf, 1, MSG_PEEK) < 1) + { + /* + * Connection closed... + */ + + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on EOF."); + cupsdCloseClient(con); + return; + } + + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP read state %s.", httpStateString(httpGetState(con->http))); + cupsdCloseClient(con); + return; + } #ifdef HAVE_SSL if (con->auto_ssl) @@ -745,18 +600,16 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ con->auto_ssl = 0; - if (recv(con->http.fd, buf, 1, MSG_PEEK) == 1 && + if (recv(httpGetFd(con->http), buf, 1, MSG_PEEK) == 1 && (!buf[0] || !strchr("DGHOPT", buf[0]))) { /* * Encrypt this connection... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] Saw first byte %02X, auto-negotiating " - "SSL/TLS session.", con->http.fd, buf[0] & 255); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "Saw first byte %02X, auto-negotiating SSL/TLS session.", buf[0] & 255); - if (!cupsdStartTLS(con)) + if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS)) cupsdCloseClient(con); return; @@ -764,24 +617,26 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } #endif /* HAVE_SSL */ - switch (con->http.state) + switch (httpGetState(con->http)) { case HTTP_STATE_WAITING : /* * See if we've received a request line... */ - if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL) + con->operation = httpReadRequest(con->http, con->uri, sizeof(con->uri)); + if (con->operation == HTTP_STATE_ERROR || + con->operation == HTTP_STATE_UNKNOWN_METHOD || + con->operation == HTTP_STATE_UNKNOWN_VERSION) { - if (con->http.error && con->http.error != EPIPE) - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] HTTP_STATE_WAITING Closing for error %d " - "(%s)", con->http.fd, con->http.error, - strerror(con->http.error)); + if (httpError(con->http)) + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "HTTP_STATE_WAITING Closing for error %d (%s)", + httpError(con->http), strerror(httpError(con->http))); else - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] HTTP_STATE_WAITING Closing on EOF", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "HTTP_STATE_WAITING Closing on error: %s", + cupsLastErrorString()); cupsdCloseClient(con); return; @@ -791,29 +646,19 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Ignore blank request lines... */ - if (line[0] == '\0') + if (con->operation == HTTP_STATE_WAITING) break; /* * Clear other state variables... */ - httpClearFields(HTTP(con)); - - con->http.activity = time(NULL); - con->http.version = HTTP_1_0; - con->http.keep_alive = HTTP_KEEPALIVE_OFF; - con->http.data_encoding = HTTP_ENCODING_LENGTH; - con->http.data_remaining = 0; - con->http._data_remaining = 0; - con->operation = HTTP_STATE_WAITING; - con->bytes = 0; - con->file = -1; - con->file_ready = 0; - con->pipe_pid = 0; - con->username[0] = '\0'; - con->password[0] = '\0'; - con->uri[0] = '\0'; + con->bytes = 0; + con->file = -1; + con->file_ready = 0; + con->pipe_pid = 0; + con->username[0] = '\0'; + con->password[0] = '\0'; cupsdClearString(&con->command); cupsdClearString(&con->options); @@ -842,62 +687,6 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ con->gss_uid = 0; #endif /* HAVE_GSSAPI */ - /* - * Grab the request line... - */ - - switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version)) - { - case 1 : - if (line[0]) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Bad request line \"%s\" from %s.", - con->http.fd, - _httpEncodeURI(buf, line, sizeof(buf)), - con->http.hostname); - cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE); - cupsdCloseClient(con); - } - return; - case 2 : - con->http.version = HTTP_0_9; - break; - case 3 : - if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2) - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Bad request line \"%s\" from %s.", - con->http.fd, - _httpEncodeURI(buf, line, sizeof(buf)), - con->http.hostname); - cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE); - cupsdCloseClient(con); - return; - } - - if (major < 2) - { - con->http.version = (http_version_t)(major * 100 + minor); - if (con->http.version == HTTP_1_1 && KeepAlive) - con->http.keep_alive = HTTP_KEEPALIVE_ON; - else - con->http.keep_alive = HTTP_KEEPALIVE_OFF; - } - else - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Unsupported request line \"%s\" " - "from %s.", con->http.fd, - _httpEncodeURI(buf, line, sizeof(buf)), - con->http.hostname); - cupsdSendError(con, HTTP_NOT_SUPPORTED, CUPSD_AUTH_NONE); - cupsdCloseClient(con); - return; - } - break; - } - /* * Handle full URLs in the request line... */ @@ -910,18 +699,24 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ resource[HTTP_MAX_URI]; /* Resource path */ int port; /* Port number */ - /* * Separate the URI into its components... */ - httpSeparateURI(HTTP_URI_CODING_MOST, con->uri, - scheme, sizeof(scheme), - userpass, sizeof(userpass), - hostname, sizeof(hostname), &port, - resource, sizeof(resource)); + if (httpSeparateURI(HTTP_URI_CODING_MOST, con->uri, + scheme, sizeof(scheme), + userpass, sizeof(userpass), + hostname, sizeof(hostname), &port, + resource, sizeof(resource)) < HTTP_URI_STATUS_OK) + { + cupsdLogClient(con, CUPSD_LOG_ERROR, "Bad URI \"%s\" in request.", + con->uri); + cupsdSendError(con, HTTP_STATUS_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE); + cupsdCloseClient(con); + return; + } - /* + /* * Only allow URIs with the servername, localhost, or an IP * address... */ @@ -929,16 +724,16 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (strcmp(scheme, "file") && _cups_strcasecmp(hostname, ServerName) && _cups_strcasecmp(hostname, "localhost") && + !cupsArrayFind(ServerAlias, hostname) && !isdigit(hostname[0]) && hostname[0] != '[') { /* * Nope, we don't do proxies... */ - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Bad URI \"%s\" in request.", - con->http.fd, con->uri); - cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE); + cupsdLogClient(con, CUPSD_LOG_ERROR, "Bad URI \"%s\" in request.", + con->uri); + cupsdSendError(con, HTTP_STATUS_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE); cupsdCloseClient(con); return; } @@ -955,38 +750,12 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Process the request... */ - if (!strcmp(operation, "GET")) - con->http.state = HTTP_STATE_GET; - else if (!strcmp(operation, "PUT")) - con->http.state = HTTP_STATE_PUT; - else if (!strcmp(operation, "POST")) - con->http.state = HTTP_STATE_POST; - else if (!strcmp(operation, "DELETE")) - con->http.state = HTTP_STATE_DELETE; - else if (!strcmp(operation, "TRACE")) - con->http.state = HTTP_STATE_TRACE; - else if (!strcmp(operation, "OPTIONS")) - con->http.state = HTTP_STATE_OPTIONS; - else if (!strcmp(operation, "HEAD")) - con->http.state = HTTP_STATE_HEAD; - else - { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Bad operation \"%s\".", con->http.fd, - operation); - cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE); - cupsdCloseClient(con); - return; - } - gettimeofday(&(con->start), NULL); - con->operation = con->http.state; - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] %s %s HTTP/%d.%d", - con->http.fd, operation, con->uri, - con->http.version / 100, con->http.version % 100); - - con->http.status = HTTP_OK; + cupsdLogClient(con, CUPSD_LOG_DEBUG, "%s %s HTTP/%d.%d", + httpStateString(con->operation) + 11, con->uri, + httpGetVersion(con->http) / 100, + httpGetVersion(con->http) % 100); if (!cupsArrayFind(ActiveClients, con)) { @@ -1005,38 +774,34 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Parse incoming parameters until the status changes... */ - while ((status = httpUpdate(HTTP(con))) == HTTP_CONTINUE) - if (!data_ready(con)) + while ((status = httpUpdate(con->http)) == HTTP_STATUS_CONTINUE) + if (!httpGetReady(con->http)) break; - if (status != HTTP_OK && status != HTTP_CONTINUE) + if (status != HTTP_STATUS_OK && status != HTTP_STATUS_CONTINUE) { - if (con->http.error && con->http.error != EPIPE) - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] Closing for error %d (%s) while " - "reading headers.", - con->http.fd, con->http.error, - strerror(con->http.error)); + if (httpError(con->http) && httpError(con->http) != EPIPE) + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Closing for error %d (%s) while reading headers.", + httpError(con->http), strerror(httpError(con->http))); else - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] Closing on EOF while reading headers.", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Closing on EOF while reading headers."); - cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE); + cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE); cupsdCloseClient(con); return; } break; default : - if (!data_ready(con) && recv(con->http.fd, buf, 1, MSG_PEEK) < 1) + if (!httpGetReady(con->http) && recv(httpGetFd(con->http), buf, 1, MSG_PEEK) < 1) { /* * Connection closed... */ - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] Closing on EOF", con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on EOF."); cupsdCloseClient(con); return; } @@ -1047,24 +812,26 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Handle new transfers... */ - if (status == HTTP_OK) + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Read: status=%d", status); + + if (status == HTTP_STATUS_OK) { - if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0]) + if (httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE)[0]) { /* * Figure out the locale from the Accept-Language and Content-Type * fields... */ - if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], + if ((ptr = strchr(httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE), ',')) != NULL) *ptr = '\0'; - if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], + if ((ptr = strchr(httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE), ';')) != NULL) *ptr = '\0'; - if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE], + if ((ptr = strstr(httpGetField(con->http, HTTP_FIELD_CONTENT_TYPE), "charset=")) != NULL) { /* @@ -1073,14 +840,14 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ */ snprintf(locale, sizeof(locale), "%s.%s", - con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8); + httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE), ptr + 8); if ((ptr = strchr(locale, ',')) != NULL) *ptr = '\0'; } else snprintf(locale, sizeof(locale), "%s.UTF-8", - con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]); + httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE)); con->language = cupsLangGet(locale); } @@ -1089,25 +856,23 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ cupsdAuthorize(con); - if (!_cups_strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], + if (!_cups_strncasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), "Keep-Alive", 10) && KeepAlive) - con->http.keep_alive = HTTP_KEEPALIVE_ON; - else if (!_cups_strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_ON); + else if (!_cups_strncasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), "close", 5)) - con->http.keep_alive = HTTP_KEEPALIVE_OFF; + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); - if (!con->http.fields[HTTP_FIELD_HOST][0] && - con->http.version >= HTTP_1_1) + if (!httpGetField(con->http, HTTP_FIELD_HOST)[0] && + httpGetVersion(con->http) >= HTTP_VERSION_1_1) { /* * HTTP/1.1 and higher require the "Host:" field... */ - if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE)) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Missing Host: field in request.", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_ERROR, "Missing Host: field in request."); cupsdCloseClient(con); return; } @@ -1119,12 +884,11 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * or IPv6 values in the Host: field. */ - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Request from \"%s\" using invalid Host: " - "field \"%s\"", con->http.fd, con->http.hostname, - con->http.fields[HTTP_FIELD_HOST]); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Request from \"%s\" using invalid Host: field \"%s\".", + httpGetHostname(con->http, NULL, 0), httpGetField(con->http, HTTP_FIELD_HOST)); - if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1138,45 +902,37 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (con->best && con->best->type != CUPSD_AUTH_NONE) { - if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE)) + httpClearFields(con->http); + + if (!cupsdSendHeader(con, HTTP_STATUS_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } - if (!_cups_strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") && - con->http.tls == NULL) + if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), "Upgrade") && strstr(httpGetField(con->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(con->http)) { #ifdef HAVE_SSL /* * Do encryption stuff... */ - if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - - httpPrintf(HTTP(con), "Connection: Upgrade\r\n"); - httpPrintf(HTTP(con), "Upgrade: TLS/1.2,TLS/1.1,TLS/1.0\r\n"); - httpPrintf(HTTP(con), "Content-Length: 0\r\n"); - httpPrintf(HTTP(con), "\r\n"); + httpClearFields(con->http); - if (cupsdFlushHeader(con) < 0) - { + if (!cupsdSendHeader(con, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE)) + { cupsdCloseClient(con); return; } - if (!cupsdStartTLS(con)) + if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED)) { cupsdCloseClient(con); return; } #else - if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1184,17 +940,12 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ #endif /* HAVE_SSL */ } - if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } + httpClearFields(con->http); + httpSetField(con->http, HTTP_FIELD_ALLOW, + "GET, HEAD, OPTIONS, POST, PUT"); + httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0"); - httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n"); - httpPrintf(HTTP(con), "Content-Length: 0\r\n"); - httpPrintf(HTTP(con), "\r\n"); - - if (cupsdFlushHeader(con) < 0) + if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1206,11 +957,10 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Protect against malicious users! */ - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Request for non-absolute resource \"%s\".", - con->http.fd, con->uri); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Request for non-absolute resource \"%s\".", con->uri); - if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1218,39 +968,30 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } else { - if (!_cups_strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], - "Upgrade") && con->http.tls == NULL) + if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), + "Upgrade") && !httpIsEncrypted(con->http)) { #ifdef HAVE_SSL /* * Do encryption stuff... */ - if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, + httpClearFields(con->http); + + if (!cupsdSendHeader(con, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - httpPrintf(HTTP(con), "Connection: Upgrade\r\n"); - httpPrintf(HTTP(con), "Upgrade: TLS/1.2,TLS/1.1,TLS/1.0\r\n"); - httpPrintf(HTTP(con), "Content-Length: 0\r\n"); - httpPrintf(HTTP(con), "\r\n"); - - if (cupsdFlushHeader(con) < 0) - { - cupsdCloseClient(con); - return; - } - - if (!cupsdStartTLS(con)) + if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED)) { cupsdCloseClient(con); return; } #else - if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1258,23 +999,23 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ #endif /* HAVE_SSL */ } - if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_OK) + if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_STATUS_OK) { cupsdSendError(con, status, CUPSD_AUTH_NONE); cupsdCloseClient(con); return; } - if (con->http.expect && + if (httpGetExpect(con->http) && (con->operation == HTTP_STATE_POST || con->operation == HTTP_STATE_PUT)) { - if (con->http.expect == HTTP_CONTINUE) + if (httpGetExpect(con->http) == HTTP_STATUS_CONTINUE) { /* * Send 100-continue header... */ - if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL, CUPSD_AUTH_NONE)) + if (httpWriteResponse(con->http, HTTP_STATUS_CONTINUE)) { cupsdCloseClient(con); return; @@ -1286,27 +1027,20 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Send 417-expectation-failed header... */ - if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL, - CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - - httpPrintf(HTTP(con), "Content-Length: 0\r\n"); - httpPrintf(HTTP(con), "\r\n"); + httpClearFields(con->http); + httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0"); - if (cupsdFlushHeader(con) < 0) - { - cupsdCloseClient(con); - return; - } + cupsdSendError(con, HTTP_STATUS_EXPECTATION_FAILED, CUPSD_AUTH_NONE); + cupsdCloseClient(con); + return; } } - switch (con->http.state) + switch (httpGetState(con->http)) { case HTTP_STATE_GET_SEND : + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Processing GET %s", con->uri); + if ((!strncmp(con->uri, "/ppd/", 5) || !strncmp(con->uri, "/printers/", 10) || !strncmp(con->uri, "/classes/", 9)) && @@ -1358,7 +1092,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } else { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1416,7 +1150,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name); else { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1425,29 +1159,28 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ break; } } - else if (!WebInterface) - { - /* - * Web interface is disabled. Show an appropriate message... - */ - - if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - - break; - } - if ((!strncmp(con->uri, "/admin", 6) && - strncmp(con->uri, "/admin/conf/", 12) && - strncmp(con->uri, "/admin/log/", 11)) || + if ((!strncmp(con->uri, "/admin", 6) && strcmp(con->uri, "/admin/conf/cupsd.conf") && strncmp(con->uri, "/admin/log/", 11)) || !strncmp(con->uri, "/printers", 9) || !strncmp(con->uri, "/classes", 8) || !strncmp(con->uri, "/help", 5) || !strncmp(con->uri, "/jobs", 5)) { + if (!WebInterface) + { + /* + * Web interface is disabled. Show an appropriate message... + */ + + if (!cupsdSendError(con, HTTP_STATUS_CUPS_WEBIF_DISABLED, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + + break; + } + /* * Send CGI output... */ @@ -1502,34 +1235,28 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (!cupsdSendCommand(con, con->command, con->options, 0)) { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } else - cupsdLogRequest(con, HTTP_OK); + cupsdLogRequest(con, HTTP_STATUS_OK); - if (con->http.version <= HTTP_1_0) - con->http.keep_alive = HTTP_KEEPALIVE_OFF; + if (httpGetVersion(con->http) <= HTTP_VERSION_1_0) + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); } - else if ((!strncmp(con->uri, "/admin/conf/", 12) && - (strchr(con->uri + 12, '/') || - strlen(con->uri) == 12)) || - (!strncmp(con->uri, "/admin/log/", 11) && - (strchr(con->uri + 11, '/') || - strlen(con->uri) == 11))) + else if (!strncmp(con->uri, "/admin/log/", 11) && (strchr(con->uri + 11, '/') || strlen(con->uri) == 11)) { /* * GET can only be done to configuration files directly under * /admin/conf... */ - cupsdLogMessage(CUPSD_LOG_ERROR, - "Request for subdirectory \"%s\"!", con->uri); + cupsdLogClient(con, CUPSD_LOG_ERROR, "Request for subdirectory \"%s\".", con->uri); - if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1546,7 +1273,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if ((filename = get_file(con, &filestats, buf, sizeof(buf))) == NULL) { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1557,6 +1284,8 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ type = mimeFileType(MimeDatabase, filename, NULL, NULL); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "filename=\"%s\", type=%s/%s", filename, type ? type->super : "", type ? type->type : ""); + if (is_cgi(con, filename, &filestats, type)) { /* @@ -1566,23 +1295,23 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (!cupsdSendCommand(con, con->command, con->options, 0)) { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } else - cupsdLogRequest(con, HTTP_OK); + cupsdLogRequest(con, HTTP_STATUS_OK); - if (con->http.version <= HTTP_1_0) - con->http.keep_alive = HTTP_KEEPALIVE_OFF; + if (httpGetVersion(con->http) <= HTTP_VERSION_1_0) + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); break; } if (!check_if_modified(con, &filestats)) { - if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_MODIFIED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1595,7 +1324,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ else snprintf(line, sizeof(line), "%s/%s", type->super, type->type); - if (!write_file(con, HTTP_OK, filename, line, &filestats)) + if (!write_file(con, HTTP_STATUS_OK, filename, line, &filestats)) { cupsdCloseClient(con); return; @@ -1610,15 +1339,15 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * so check the length against any limits that are set... */ - if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] && + if (httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] && MaxRequestSize > 0 && - con->http.data_remaining > MaxRequestSize) + httpGetLength2(con->http) > MaxRequestSize) { /* * Request too large... */ - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1626,15 +1355,13 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ break; } - else if (con->http.data_remaining < 0 || - (!con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] && - con->http.data_encoding == HTTP_ENCODING_LENGTH)) + else if (httpGetLength2(con->http) < 0) { /* * Negative content lengths are invalid! */ - if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1648,7 +1375,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * content-type field will be "application/ipp"... */ - if (!strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], + if (!strcmp(httpGetField(con->http, HTTP_FIELD_CONTENT_TYPE), "application/ipp")) con->request = ippNew(); else if (!WebInterface) @@ -1657,7 +1384,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Web interface is disabled. Show an appropriate message... */ - if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_CUPS_WEBIF_DISABLED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1665,9 +1392,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ break; } - else if ((!strncmp(con->uri, "/admin", 6) && - strncmp(con->uri, "/admin/conf/", 12) && - strncmp(con->uri, "/admin/log/", 11)) || + else if ((!strncmp(con->uri, "/admin", 6) && strncmp(con->uri, "/admin/log/", 11)) || !strncmp(con->uri, "/printers", 9) || !strncmp(con->uri, "/classes", 8) || !strncmp(con->uri, "/help", 5) || @@ -1725,8 +1450,8 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ cupsdSetString(&con->options, NULL); } - if (con->http.version <= HTTP_1_0) - con->http.keep_alive = HTTP_KEEPALIVE_OFF; + if (httpGetVersion(con->http) <= HTTP_VERSION_1_0) + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); } else { @@ -1737,7 +1462,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if ((filename = get_file(con, &filestats, buf, sizeof(buf))) == NULL) { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1754,7 +1479,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Only POST to CGI's... */ - if (!cupsdSendError(con, HTTP_UNAUTHORIZED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_UNAUTHORIZED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1774,11 +1499,10 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * PUT can only be done to the cupsd.conf file... */ - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Disallowed PUT request for \"%s\".", - con->http.fd, con->uri); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Disallowed PUT request for \"%s\".", con->uri); - if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1792,15 +1516,15 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * so check the length against any limits that are set... */ - if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] && + if (httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] && MaxRequestSize > 0 && - con->http.data_remaining > MaxRequestSize) + httpGetLength2(con->http) > MaxRequestSize) { /* * Request too large... */ - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1808,13 +1532,13 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ break; } - else if (con->http.data_remaining < 0) + else if (httpGetLength2(con->http) < 0) { /* * Negative content lengths are invalid! */ - if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1833,12 +1557,11 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (con->file < 0) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Unable to create request file " - "\"%s\": %s", con->http.fd, con->filename, - strerror(errno)); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Unable to create request file \"%s\": %s", + con->filename, strerror(errno)); - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -1852,7 +1575,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ case HTTP_STATE_DELETE : case HTTP_STATE_TRACE : - cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE); + cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE); cupsdCloseClient(con); return; @@ -1871,12 +1594,13 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); else { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } + cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); break; } } @@ -1894,44 +1618,31 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name); else { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } + cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); break; } } else if (!WebInterface) { - if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } + httpClearFields(con->http); - if (httpPrintf(HTTP(con), "\r\n") < 0) + if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - if (cupsdFlushHeader(con) < 0) - { - cupsdCloseClient(con); - return; - } - - con->http.state = HTTP_STATE_WAITING; - DEBUG_puts("cupsdReadClient: Set state to HTTP_STATE_WAITING " - "after HEAD."); + cupsdLogRequest(con, HTTP_STATUS_OK); break; } - if ((!strncmp(con->uri, "/admin", 6) && - strncmp(con->uri, "/admin/conf/", 12) && - strncmp(con->uri, "/admin/log/", 11)) || + if ((!strncmp(con->uri, "/admin", 6) && strcmp(con->uri, "/admin/conf/cupsd.conf") && strncmp(con->uri, "/admin/log/", 11)) || !strncmp(con->uri, "/printers", 9) || !strncmp(con->uri, "/classes", 8) || !strncmp(con->uri, "/help", 5) || @@ -1941,71 +1652,58 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * CGI output... */ - if (!cupsdSendHeader(con, HTTP_OK, "text/html", CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - - if (httpPrintf(HTTP(con), "\r\n") < 0) - { - cupsdCloseClient(con); - return; - } + httpClearFields(con->http); - if (cupsdFlushHeader(con) < 0) + if (!cupsdSendHeader(con, HTTP_STATUS_OK, "text/html", CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_OK); + cupsdLogRequest(con, HTTP_STATUS_OK); } - else if ((!strncmp(con->uri, "/admin/conf/", 12) && - (strchr(con->uri + 12, '/') || - strlen(con->uri) == 12)) || - (!strncmp(con->uri, "/admin/log/", 11) && - (strchr(con->uri + 11, '/') || - strlen(con->uri) == 11))) + else if (!strncmp(con->uri, "/admin/log/", 11) && (strchr(con->uri + 11, '/') || strlen(con->uri) == 11)) { /* * HEAD can only be done to configuration files under * /admin/conf... */ - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Request for subdirectory \"%s\".", - con->http.fd, con->uri); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Request for subdirectory \"%s\".", con->uri); - if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } + cupsdLogRequest(con, HTTP_STATUS_FORBIDDEN); break; } else if ((filename = get_file(con, &filestats, buf, sizeof(buf))) == NULL) { - if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html", + httpClearFields(con->http); + + if (!cupsdSendHeader(con, HTTP_STATUS_NOT_FOUND, "text/html", CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_NOT_FOUND); + cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND); } else if (!check_if_modified(con, &filestats)) { - if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_MODIFIED, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_NOT_MODIFIED); + cupsdLogRequest(con, HTTP_STATUS_NOT_MODIFIED); } else { @@ -2019,44 +1717,20 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ else snprintf(line, sizeof(line), "%s/%s", type->super, type->type); - if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } + httpClearFields(con->http); - if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", - httpGetDateString(filestats.st_mtime)) < 0) - { - cupsdCloseClient(con); - return; - } + httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED, + httpGetDateString(filestats.st_mtime)); + httpSetLength(con->http, (size_t)filestats.st_size); - if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n", - (unsigned long)filestats.st_size) < 0) + if (!cupsdSendHeader(con, HTTP_STATUS_OK, line, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } - cupsdLogRequest(con, HTTP_OK); - } - - if (httpPrintf(HTTP(con), "\r\n") < 0) - { - cupsdCloseClient(con); - return; - } - - if (cupsdFlushHeader(con) < 0) - { - cupsdCloseClient(con); - return; + cupsdLogRequest(con, HTTP_STATUS_OK); } - - con->http.state = HTTP_STATE_WAITING; - DEBUG_puts("cupsdReadClient: Set state to HTTP_STATE_WAITING " - "after HEAD."); break; default : @@ -2069,22 +1743,20 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Handle any incoming data... */ - switch (con->http.state) + switch (httpGetState(con->http)) { case HTTP_STATE_PUT_RECV : do { - if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0) + if ((bytes = httpRead2(con->http, line, sizeof(line))) < 0) { - if (con->http.error && con->http.error != EPIPE) - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] HTTP_STATE_PUT_RECV Closing for error " - "%d (%s)", con->http.fd, con->http.error, - strerror(con->http.error)); + if (httpError(con->http) && httpError(con->http) != EPIPE) + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "HTTP_STATE_PUT_RECV Closing for error %d (%s)", + httpError(con->http), strerror(httpError(con->http))); else - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] HTTP_STATE_PUT_RECV Closing on EOF", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "HTTP_STATE_PUT_RECV Closing on EOF."); cupsdCloseClient(con); return; @@ -2093,29 +1765,47 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ { con->bytes += bytes; - if (write(con->file, line, bytes) < bytes) + if (MaxRequestSize > 0 && con->bytes > MaxRequestSize) + { + close(con->file); + con->file = -1; + unlink(con->filename); + cupsdClearString(&con->filename); + + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + } + + if (write(con->file, line, (size_t)bytes) < bytes) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Unable to write %d bytes to " - "\"%s\": %s", con->http.fd, bytes, con->filename, - strerror(errno)); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Unable to write %d bytes to \"%s\": %s", bytes, + con->filename, strerror(errno)); close(con->file); con->file = -1; unlink(con->filename); cupsdClearString(&con->filename); - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } } + else if (httpGetState(con->http) == HTTP_STATE_PUT_RECV) + { + cupsdCloseClient(con); + return; + } } - while (con->http.state == HTTP_STATE_PUT_RECV && data_ready(con)); + while (httpGetState(con->http) == HTTP_STATE_PUT_RECV && httpGetReady(con->http)); - if (con->http.state == HTTP_STATE_STATUS) + if (httpGetState(con->http) == HTTP_STATE_STATUS) { /* * End of file, see how big it is... @@ -2136,7 +1826,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ unlink(con->filename); cupsdClearString(&con->filename); - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -2170,41 +1860,43 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ * Grab any request data from the connection... */ - if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR) + if (!httpWait(con->http, 0)) + return; + + if ((ipp_state = ippRead(con->http, con->request)) == IPP_STATE_ERROR) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] IPP read error: %s", con->http.fd, - cupsLastErrorString()); + cupsdLogClient(con, CUPSD_LOG_ERROR, "IPP read error: %s", + cupsLastErrorString()); - cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE); + cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE); cupsdCloseClient(con); return; } - else if (ipp_state != IPP_DATA) + else if (ipp_state != IPP_STATE_DATA) { - if (con->http.state == HTTP_STATE_POST_SEND) + if (httpGetState(con->http) == HTTP_STATE_POST_SEND) { - cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE); + cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE); cupsdCloseClient(con); return; } - if (data_ready(con)) + if (httpGetReady(con->http)) continue; break; } else { - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] %d.%d %s %d", - con->http.fd, con->request->request.op.version[0], + cupsdLogClient(con, CUPSD_LOG_DEBUG, "%d.%d %s %d", + con->request->request.op.version[0], con->request->request.op.version[1], ippOpString(con->request->request.op.operation_id), con->request->request.op.request_id); - con->bytes += ippLength(con->request); + con->bytes += (off_t)ippLength(con->request); } } - if (con->file < 0 && con->http.state != HTTP_STATE_POST_SEND) + if (con->file < 0 && httpGetState(con->http) != HTTP_STATE_POST_SEND) { /* * Create a file as needed for the request data... @@ -2216,12 +1908,11 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ if (con->file < 0) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Unable to create request file " - "\"%s\": %s", con->http.fd, con->filename, - strerror(errno)); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Unable to create request file \"%s\": %s", + con->filename, strerror(errno)); - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -2233,21 +1924,19 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); } - if (con->http.state != HTTP_STATE_POST_SEND) + if (httpGetState(con->http) != HTTP_STATE_POST_SEND) { - if (!httpWait(HTTP(con), 0)) + if (!httpWait(con->http, 0)) return; - else if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0) + else if ((bytes = httpRead2(con->http, line, sizeof(line))) < 0) { - if (con->http.error && con->http.error != EPIPE) - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] HTTP_STATE_POST_SEND Closing for " - "error %d (%s)", con->http.fd, con->http.error, - strerror(con->http.error)); + if (httpError(con->http) && httpError(con->http) != EPIPE) + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "HTTP_STATE_POST_SEND Closing for error %d (%s)", + httpError(con->http), strerror(httpError(con->http))); else - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] HTTP_STATE_POST_SEND Closing on EOF", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "HTTP_STATE_POST_SEND Closing on EOF."); cupsdCloseClient(con); return; @@ -2256,19 +1945,32 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ { con->bytes += bytes; - if (write(con->file, line, bytes) < bytes) + if (MaxRequestSize > 0 && con->bytes > MaxRequestSize) + { + close(con->file); + con->file = -1; + unlink(con->filename); + cupsdClearString(&con->filename); + + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + } + + if (write(con->file, line, (size_t)bytes) < bytes) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Unable to write %d bytes to " - "\"%s\": %s", con->http.fd, bytes, - con->filename, strerror(errno)); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Unable to write %d bytes to \"%s\": %s", + bytes, con->filename, strerror(errno)); close(con->file); con->file = -1; unlink(con->filename); cupsdClearString(&con->filename); - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); @@ -2276,21 +1978,21 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ } } } - else if (con->http.state == HTTP_STATE_POST_RECV) + else if (httpGetState(con->http) == HTTP_STATE_POST_RECV) return; - else if (con->http.state != HTTP_STATE_POST_SEND) + else if (httpGetState(con->http) != HTTP_STATE_POST_SEND) { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] Closing on unexpected state %s.", - con->http.fd, http_states[con->http.state + 1]); + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Closing on unexpected state %s.", + httpStateString(httpGetState(con->http))); cupsdCloseClient(con); return; } } } - while (con->http.state == HTTP_STATE_POST_RECV && data_ready(con)); + while (httpGetState(con->http) == HTTP_STATE_POST_RECV && httpGetReady(con->http)); - if (con->http.state == HTTP_STATE_POST_SEND) + if (httpGetState(con->http) == HTTP_STATE_POST_SEND) { if (con->file >= 0) { @@ -2319,7 +2021,7 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ con->request = NULL; } - if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; @@ -2339,14 +2041,14 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ { if (!cupsdSendCommand(con, con->command, con->options, 0)) { - if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE)) + if (!cupsdSendError(con, HTTP_STATUS_NOT_FOUND, CUPSD_AUTH_NONE)) { cupsdCloseClient(con); return; } } else - cupsdLogRequest(con, HTTP_OK); + cupsdLogRequest(con, HTTP_STATUS_OK); } } @@ -2369,13 +2071,12 @@ cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ break; /* Anti-compiler-warning-code */ } - if (con->http.state == HTTP_STATE_WAITING) + if (httpGetState(con->http) == HTTP_STATE_WAITING) { - if (!con->http.keep_alive) + if (!httpGetKeepAlive(con->http)) { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] Closing because Keep-Alive disabled", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Closing because Keep-Alive is disabled."); cupsdCloseClient(con); } else @@ -2407,10 +2108,10 @@ cupsdSendCommand( if (fd < 0) { - cupsdLogMessage(CUPSD_LOG_ERROR, - "[Client %d] Unable to open \"%s\" for reading: %s", - con->http.fd, con->filename ? con->filename : "/dev/null", - strerror(errno)); + cupsdLogClient(con, CUPSD_LOG_ERROR, + "Unable to open \"%s\" for reading: %s", + con->filename ? con->filename : "/dev/null", + strerror(errno)); return (0); } @@ -2419,16 +2120,16 @@ cupsdSendCommand( else fd = -1; - con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root); + con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root); + con->pipe_status = HTTP_STATUS_OK; + + httpClearFields(con->http); if (fd >= 0) close(fd); - cupsdLogMessage(CUPSD_LOG_INFO, "[Client %d] Started \"%s\" (pid=%d)", - con->http.fd, command, con->pipe_pid); - - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] file=%d", con->http.fd, - con->file); + cupsdLogClient(con, CUPSD_LOG_INFO, "Started \"%s\" (pid=%d, file=%d)", + command, con->pipe_pid, con->file); if (con->pipe_pid == 0) return (0); @@ -2437,8 +2138,7 @@ cupsdSendCommand( cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con); - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Waiting for CGI data.", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for CGI data."); con->sent_header = 0; con->file_ready = 0; @@ -2458,9 +2158,10 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ http_status_t code, /* I - Error code */ int auth_type)/* I - Authentication type */ { - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] cupsdSendError code=%d, auth_type=%d", - con->http.fd, code, auth_type); + char location[HTTP_MAX_VALUE]; /* Location field */ + + + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cupsdSendError code=%d, auth_type=%d", code, auth_type); #ifdef HAVE_SSL /* @@ -2468,12 +2169,12 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ * server is configured... */ - if (code == HTTP_UNAUTHORIZED && - DefaultEncryption == HTTP_ENCRYPT_REQUIRED && - _cups_strcasecmp(con->http.hostname, "localhost") && - !con->http.tls) + if (code == HTTP_STATUS_UNAUTHORIZED && + DefaultEncryption == HTTP_ENCRYPTION_REQUIRED && + _cups_strcasecmp(httpGetHostname(con->http, NULL, 0), "localhost") && + !httpIsEncrypted(con->http)) { - code = HTTP_UPGRADE_REQUIRED; + code = HTTP_STATUS_UPGRADE_REQUIRED; } #endif /* HAVE_SSL */ @@ -2491,34 +2192,20 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ * never disable it in that case. */ - if (code >= HTTP_BAD_REQUEST && con->http.auth_type != CUPSD_AUTH_NEGOTIATE) - con->http.keep_alive = HTTP_KEEPALIVE_OFF; + strlcpy(location, httpGetField(con->http, HTTP_FIELD_LOCATION), sizeof(location)); - /* - * Send an error message back to the client. If the error code is a - * 400 or 500 series, make sure the message contains some text, too! - */ + httpClearFields(con->http); - if (!cupsdSendHeader(con, code, NULL, auth_type)) - return (0); - -#ifdef HAVE_SSL - if (code == HTTP_UPGRADE_REQUIRED) - if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0) - return (0); + httpSetField(con->http, HTTP_FIELD_LOCATION, location); - if (httpPrintf(HTTP(con), "Upgrade: TLS/1.2,TLS/1.1,TLS/1.0\r\n") < 0) - return (0); -#endif /* HAVE_SSL */ + if (code >= HTTP_STATUS_BAD_REQUEST && con->type != CUPSD_AUTH_NEGOTIATE) + httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF); - if (con->http.version >= HTTP_1_1 && - con->http.keep_alive == HTTP_KEEPALIVE_OFF) - { - if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0) - return (0); - } + if (httpGetVersion(con->http) >= HTTP_VERSION_1_1 && + httpGetKeepAlive(con->http) == HTTP_KEEPALIVE_OFF) + httpSetField(con->http, HTTP_FIELD_CONNECTION, "close"); - if (code >= HTTP_BAD_REQUEST) + if (code >= HTTP_STATUS_BAD_REQUEST) { /* * Send a human-readable error message. @@ -2532,13 +2219,13 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ redirect[0] = '\0'; - if (code == HTTP_UNAUTHORIZED) + if (code == HTTP_STATUS_UNAUTHORIZED) text = _cupsLangString(con->language, _("Enter your username and password or the " "root username and password to access this " "page. If you are using Kerberos authentication, " "make sure you have a valid Kerberos ticket.")); - else if (code == HTTP_UPGRADE_REQUIRED) + else if (code == HTTP_STATUS_UPGRADE_REQUIRED) { text = urltext; @@ -2555,7 +2242,7 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ "CONTENT=\"3;URL=https://%s:%d%s\">\n", con->servername, con->serverport, con->uri); } - else if (code == HTTP_WEBIF_DISABLED) + else if (code == HTTP_STATUS_CUPS_WEBIF_DISABLED) text = _cupsLangString(con->language, _("The web interface is currently disabled. Run " "\"cupsctl WebInterface=yes\" to enable it.")); @@ -2582,25 +2269,31 @@ cupsdSendError(cupsd_client_t *con, /* I - Connection */ _httpStatus(con->language, code), redirect, _httpStatus(con->language, code), text); - if (httpPrintf(HTTP(con), "Content-Type: text/html; charset=utf-8\r\n") < 0) - return (0); - if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", - (int)strlen(message)) < 0) + /* + * Send an error message back to the client. If the error code is a + * 400 or 500 series, make sure the message contains some text, too! + */ + + size_t length = strlen(message); /* Length of message */ + + httpSetLength(con->http, length); + + if (!cupsdSendHeader(con, code, "text/html", auth_type)) return (0); - if (httpPrintf(HTTP(con), "\r\n") < 0) + + if (httpWrite2(con->http, message, length) < 0) return (0); - if (httpPrintf(HTTP(con), "%s", message) < 0) + + if (httpFlushWrite(con->http) < 0) return (0); } - else if (httpPrintf(HTTP(con), "\r\n") < 0) - return (0); - - if (cupsdFlushHeader(con) < 0) - return (0); - - con->http.state = HTTP_STATE_WAITING; + else + { + httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0"); - DEBUG_puts("cupsdSendError: Set state to HTTP_STATE_WAITING."); + if (!cupsdSendHeader(con, code, NULL, auth_type)) + return (0); + } return (1); } @@ -2620,54 +2313,29 @@ cupsdSendHeader( char auth_str[1024]; /* Authorization string */ + cupsdLogClient(con, CUPSD_LOG_DEBUG, "cupsdSendHeader: code=%d, type=\"%s\", auth_type=%d", code, type, auth_type); + /* * Send the HTTP status header... */ - if (code == HTTP_CONTINUE) - { - /* - * 100-continue doesn't send any headers... - */ - - return (httpPrintf(HTTP(con), "HTTP/%d.%d 100 Continue\r\n\r\n", - con->http.version / 100, con->http.version % 100) > 0); - } - else if (code == HTTP_WEBIF_DISABLED) + if (code == HTTP_STATUS_CUPS_WEBIF_DISABLED) { /* * Treat our special "web interface is disabled" status as "200 OK" for web * browsers. */ - code = HTTP_OK; + code = HTTP_STATUS_OK; } - httpFlushWrite(HTTP(con)); - - con->http.data_encoding = HTTP_ENCODING_FIELDS; - - if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100, - con->http.version % 100, code, httpStatus(code)) < 0) - return (0); - if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0) - return (0); if (ServerHeader) - if (httpPrintf(HTTP(con), "Server: %s\r\n", ServerHeader) < 0) - return (0); - if (con->http.keep_alive && con->http.version >= HTTP_1_0) - { - if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0) - return (0); - if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", - KeepAliveTimeout) < 0) - return (0); - } - if (code == HTTP_METHOD_NOT_ALLOWED) - if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n") < 0) - return (0); + httpSetField(con->http, HTTP_FIELD_SERVER, ServerHeader); + + if (code == HTTP_STATUS_METHOD_NOT_ALLOWED) + httpSetField(con->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST, PUT"); - if (code == HTTP_UNAUTHORIZED) + if (code == HTTP_STATUS_UNAUTHORIZED) { if (auth_type == CUPSD_AUTH_NONE) { @@ -2679,16 +2347,13 @@ cupsdSendHeader( auth_str[0] = '\0'; - if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST) + if (auth_type == CUPSD_AUTH_BASIC) strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str)); - else if (auth_type == CUPSD_AUTH_DIGEST) - snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"", - con->http.hostname); #ifdef HAVE_GSSAPI else if (auth_type == CUPSD_AUTH_NEGOTIATE) { # ifdef AF_LOCAL - if (_httpAddrFamily(con->http.hostaddr) == AF_LOCAL) + if (httpAddrFamily(httpGetAddress(con->http)) == AF_LOCAL) strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str)); else # endif /* AF_LOCAL */ @@ -2697,7 +2362,7 @@ cupsdSendHeader( #endif /* HAVE_GSSAPI */ if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE && - !_cups_strcasecmp(con->http.hostname, "localhost")) + !_cups_strcasecmp(httpGetHostname(con->http, NULL, 0), "localhost")) { /* * Add a "trc" (try root certification) parameter for local non-Kerberos @@ -2713,7 +2378,7 @@ cupsdSendHeader( size_t auth_size; /* Size of remaining buffer */ auth_key = auth_str + strlen(auth_str); - auth_size = sizeof(auth_str) - (auth_key - auth_str); + auth_size = sizeof(auth_str) - (size_t)(auth_key - auth_str); for (name = (char *)cupsArrayFirst(con->best->names); name; @@ -2746,35 +2411,24 @@ cupsdSendHeader( if (auth_str[0]) { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] WWW-Authenticate: %s", con->http.fd, - auth_str); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "WWW-Authenticate: %s", auth_str); - if (httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0) - return (0); + httpSetField(con->http, HTTP_FIELD_WWW_AUTHENTICATE, auth_str); } } if (con->language && strcmp(con->language->language, "C")) - { - if (httpPrintf(HTTP(con), "Content-Language: %s\r\n", - con->language->language) < 0) - return (0); - } + httpSetField(con->http, HTTP_FIELD_CONTENT_LANGUAGE, con->language->language); if (type) { if (!strcmp(type, "text/html")) - { - if (httpPrintf(HTTP(con), - "Content-Type: text/html; charset=utf-8\r\n") < 0) - return (0); - } - else if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0) - return (0); + httpSetField(con->http, HTTP_FIELD_CONTENT_TYPE, "text/html; charset=utf-8"); + else + httpSetField(con->http, HTTP_FIELD_CONTENT_TYPE, type); } - return (1); + return (!httpWriteResponse(con->http, code)); } @@ -2827,36 +2481,35 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ ipp_state_t ipp_state; /* IPP state value */ - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] cupsdWriteClient " - "error=%d, " - "used=%d, " - "state=%s, " - "data_encoding=HTTP_ENCODING_%s, " - "data_remaining=" CUPS_LLFMT ", " - "response=%p(%s), " - "pipe_pid=%d, " - "file=%d", - con->http.fd, con->http.error, con->http.used, - http_states[con->http.state + 1], - con->http.data_encoding == HTTP_ENCODING_CHUNKED ? - "CHUNKED" : "LENGTH", - CUPS_LLCAST con->http.data_remaining, - con->response, - con->response ? ipp_states[con->response->state] : "", - con->pipe_pid, con->file); - - if (con->http.state != HTTP_STATE_GET_SEND && - con->http.state != HTTP_STATE_POST_SEND) + cupsdLogClient(con, CUPSD_LOG_DEBUG, "con->http=%p", con->http); + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "cupsdWriteClient " + "error=%d, " + "used=%d, " + "state=%s, " + "data_encoding=HTTP_ENCODING_%s, " + "data_remaining=" CUPS_LLFMT ", " + "response=%p(%s), " + "pipe_pid=%d, " + "file=%d", + httpError(con->http), (int)httpGetReady(con->http), + httpStateString(httpGetState(con->http)), + httpIsChunked(con->http) ? "CHUNKED" : "LENGTH", + CUPS_LLCAST httpGetLength2(con->http), + con->response, + con->response ? ippStateString(ippGetState(con->request)) : "", + con->pipe_pid, con->file); + + if (httpGetState(con->http) != HTTP_STATE_GET_SEND && + httpGetState(con->http) != HTTP_STATE_POST_SEND) { /* * If we get called in the wrong state, then something went wrong with the * connection and we need to shut it down... */ - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] Closing on unexpected HTTP state %s.", - con->http.fd, http_states[con->http.state + 1]); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP write state %s.", + httpStateString(httpGetState(con->http))); cupsdCloseClient(con); return; } @@ -2869,8 +2522,7 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con); - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Waiting for CGI data.", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for CGI data."); if (!con->file_ready) { @@ -2878,16 +2530,27 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ * Try again later when there is CGI output available... */ - cupsdRemoveSelect(con->http.fd); + cupsdRemoveSelect(httpGetFd(con->http)); return; } con->file_ready = 0; } - if (con->response && con->response->state != IPP_DATA) + bytes = (ssize_t)(sizeof(con->header) - (size_t)con->header_used); + + if (!con->pipe_pid && bytes > (ssize_t)httpGetRemaining(con->http)) + { + /* + * Limit GET bytes to original size of file (STR #3265)... + */ + + bytes = (ssize_t)httpGetRemaining(con->http); + } + + if (con->response && con->response->state != IPP_STATE_DATA) { - int wused = con->http.wused; /* Previous write buffer use */ + size_t wused = httpGetPending(con->http); /* Previous write buffer use */ do { @@ -2895,40 +2558,35 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ * Write a single attribute or the IPP message header... */ - ipp_state = ippWrite(HTTP(con), con->response); + ipp_state = ippWrite(con->http, con->response); /* * If the write buffer has been flushed, stop buffering up attributes... */ - if (con->http.wused <= wused) + if (httpGetPending(con->http) <= wused) break; } while (ipp_state != IPP_STATE_DATA && ipp_state != IPP_STATE_ERROR); - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] Writing IPP response, ipp_state=%s, old " - "wused=%d, new wused=%d", con->http.fd, - ipp_state == IPP_STATE_ERROR ? "ERROR" : - ipp_state == IPP_STATE_IDLE ? "IDLE" : - ipp_state == IPP_STATE_HEADER ? "HEADER" : - ipp_state == IPP_STATE_ATTRIBUTE ? "ATTRIBUTE" : "DATA", - wused, con->http.wused); + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Writing IPP response, ipp_state=%s, old " + "wused=" CUPS_LLFMT ", new wused=" CUPS_LLFMT, + ippStateString(ipp_state), + CUPS_LLCAST wused, CUPS_LLCAST httpGetPending(con->http)); - if (con->http.wused > 0) - httpFlushWrite(HTTP(con)); + if (httpGetPending(con->http) > 0) + httpFlushWrite(con->http); bytes = ipp_state != IPP_STATE_ERROR && (con->file >= 0 || ipp_state != IPP_STATE_DATA); - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] bytes=%d, http_state=%d, " - "data_remaining=" CUPS_LLFMT, - con->http.fd, (int)bytes, con->http.state, - CUPS_LLCAST con->http.data_remaining); + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "bytes=%d, http_state=%d, data_remaining=" CUPS_LLFMT, + (int)bytes, httpGetState(con->http), + CUPS_LLCAST httpGetLength2(con->http)); } - else if ((bytes = read(con->file, con->header + con->header_used, - sizeof(con->header) - con->header_used)) > 0) + else if ((bytes = read(con->file, con->header + con->header_used, (size_t)bytes)) > 0) { con->header_used += bytes; @@ -2953,7 +2611,7 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ bufptr[-1] = '\0'; *bufptr++ = '\0'; - cupsdLogMessage(CUPSD_LOG_DEBUG, "Script header: %s", con->header); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Script header: %s", con->header); if (!con->sent_header) { @@ -2961,46 +2619,43 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ * Handle redirection and CGI status codes... */ - if (!_cups_strncasecmp(con->header, "Location:", 9)) + http_field_t field; /* HTTP field */ + char *value = strchr(con->header, ':'); + /* Value of field */ + + if (value) { - if (!cupsdSendHeader(con, HTTP_SEE_OTHER, NULL, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } + *value++ = '\0'; + while (isspace(*value & 255)) + value ++; + } - con->sent_header = 2; + field = httpFieldValue(con->header); - if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0) - return; + if (field != HTTP_FIELD_UNKNOWN && value) + { + httpSetField(con->http, field, value); + + if (field == HTTP_FIELD_LOCATION) + { + con->pipe_status = HTTP_STATUS_SEE_OTHER; + con->sent_header = 2; + } + else + con->sent_header = 1; } - else if (!_cups_strncasecmp(con->header, "Status:", 7)) + else if (!_cups_strcasecmp(con->header, "Status") && value) { - cupsdSendError(con, (http_status_t)atoi(con->header + 7), - CUPSD_AUTH_NONE); + con->pipe_status = (http_status_t)atoi(value); con->sent_header = 2; } - else + else if (!_cups_strcasecmp(con->header, "Set-Cookie") && value) { - if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE)) - { - cupsdCloseClient(con); - return; - } - + httpSetCookie(con->http, value); con->sent_header = 1; - - if (con->http.version == HTTP_1_1) - { - if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0) - return; - } - } + } } - if (_cups_strncasecmp(con->header, "Status:", 7)) - httpPrintf(HTTP(con), "%s\r\n", con->header); - /* * Update buffer... */ @@ -3008,7 +2663,7 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ con->header_used -= bufptr - con->header; if (con->header_used > 0) - memmove(con->header, bufptr, con->header_used); + memmove(con->header, bufptr, (size_t)con->header_used); bufptr = con->header - 1; @@ -3020,14 +2675,28 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ { con->got_fields = 1; - if (cupsdFlushHeader(con) < 0) + if (httpGetVersion(con->http) == HTTP_VERSION_1_1 && + !httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0]) + httpSetLength(con->http, 0); + + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending status %d for CGI.", con->pipe_status); + + if (con->pipe_status == HTTP_STATUS_OK) { - cupsdCloseClient(con); - return; + if (!cupsdSendHeader(con, con->pipe_status, NULL, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } + } + else + { + if (!cupsdSendError(con, con->pipe_status, CUPSD_AUTH_NONE)) + { + cupsdCloseClient(con); + return; + } } - - if (con->http.version == HTTP_1_1) - con->http.data_encoding = HTTP_ENCODING_CHUNKED; } else field_col = 0; @@ -3037,30 +2706,25 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ } if (!con->got_fields) - { - con->http.activity = time(NULL); return; - } } if (con->header_used > 0) { - if (httpWrite2(HTTP(con), con->header, con->header_used) < 0) + if (httpWrite2(con->http, con->header, (size_t)con->header_used) < 0) { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] Closing for error %d (%s)", - con->http.fd, con->http.error, - strerror(con->http.error)); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing for error %d (%s)", + httpError(con->http), strerror(httpError(con->http))); cupsdCloseClient(con); return; } - if (con->http.data_encoding == HTTP_ENCODING_CHUNKED) - httpFlushWrite(HTTP(con)); + if (httpIsChunked(con->http)) + httpFlushWrite(con->http); con->bytes += con->header_used; - if (con->http.state == HTTP_STATE_WAITING) + if (httpGetState(con->http) == HTTP_STATE_WAITING) bytes = 0; else bytes = con->header_used; @@ -3070,38 +2734,36 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ } if (bytes <= 0 || - (con->http.state != HTTP_STATE_GET_SEND && - con->http.state != HTTP_STATE_POST_SEND)) + (httpGetState(con->http) != HTTP_STATE_GET_SEND && + httpGetState(con->http) != HTTP_STATE_POST_SEND)) { if (!con->sent_header && con->pipe_pid) - cupsdSendError(con, HTTP_SERVER_ERROR, CUPSD_AUTH_NONE); + cupsdSendError(con, HTTP_STATUS_SERVER_ERROR, CUPSD_AUTH_NONE); else { - cupsdLogRequest(con, HTTP_OK); - - httpFlushWrite(HTTP(con)); + cupsdLogRequest(con, HTTP_STATUS_OK); - if (con->http.data_encoding == HTTP_ENCODING_CHUNKED && - con->sent_header == 1) + if (httpIsChunked(con->http) && (!con->pipe_pid || con->sent_header > 0)) { - if (httpWrite2(HTTP(con), "", 0) < 0) + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending 0-length chunk."); + + if (httpWrite2(con->http, "", 0) < 0) { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] Closing for error %d (%s)", - con->http.fd, con->http.error, - strerror(con->http.error)); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing for error %d (%s)", + httpError(con->http), strerror(httpError(con->http))); cupsdCloseClient(con); return; } } - } - con->http.state = HTTP_STATE_WAITING; + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Flushing write buffer."); + httpFlushWrite(con->http); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "New state is %s", httpStateString(httpGetState(con->http))); + } - cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con); + cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, NULL, con); - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Waiting for request.", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Waiting for request."); if (con->file >= 0) { @@ -3137,11 +2799,10 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ cupsdClearString(&con->options); cupsdClearString(&con->query_string); - if (!con->http.keep_alive) + if (!httpGetKeepAlive(con->http)) { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "[Client %d] Closing because Keep-Alive disabled.", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, + "Closing because Keep-Alive is disabled."); cupsdCloseClient(con); return; } @@ -3151,8 +2812,6 @@ cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ cupsdSetBusyState(); } } - - con->http.activity = time(NULL); } @@ -3165,23 +2824,19 @@ check_if_modified( cupsd_client_t *con, /* I - Client connection */ struct stat *filestats) /* I - File information */ { - char *ptr; /* Pointer into field */ + const char *ptr; /* Pointer into field */ time_t date; /* Time/date value */ off_t size; /* Size/length value */ size = 0; date = 0; - ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE]; + ptr = httpGetField(con->http, HTTP_FIELD_IF_MODIFIED_SINCE); if (*ptr == '\0') return (1); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] check_if_modified " - "filestats=%p(" CUPS_LLFMT ", %d)) If-Modified-Since=\"%s\"", - con->http.fd, filestats, CUPS_LLCAST filestats->st_size, - (int)filestats->st_mtime, ptr); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "check_if_modified: filestats=%p(" CUPS_LLFMT ", %d)) If-Modified-Since=\"%s\"", filestats, CUPS_LLCAST filestats->st_size, (int)filestats->st_mtime, ptr); while (*ptr != '\0') { @@ -3232,35 +2887,26 @@ compare_clients(cupsd_client_t *a, /* I - First client */ } +#ifdef HAVE_SSL /* - * 'data_ready()' - Check whether data is available from a client. + * 'cupsd_start_tls()' - Start encryption on a connection. */ -static int /* O - 1 if data is ready, 0 otherwise */ -data_ready(cupsd_client_t *con) /* I - Client */ +static int /* O - 0 on success, -1 on error */ +cupsd_start_tls(cupsd_client_t *con, /* I - Client connection */ + http_encryption_t e) /* I - Encryption mode */ { - if (con->http.used > 0) - return (1); -#ifdef HAVE_SSL - else if (con->http.tls) + if (httpEncryption(con->http, e)) { -# ifdef HAVE_LIBSSL - if (SSL_pending((SSL *)(con->http.tls))) - return (1); -# elif defined(HAVE_GNUTLS) - if (gnutls_record_check_pending(con->http.tls)) - return (1); -# elif defined(HAVE_CDSASSL) - size_t bytes; /* Bytes that are available */ - - if (!SSLGetBufferedReadSize(con->http.tls, &bytes) && bytes > 0) - return (1); -# endif /* HAVE_LIBSSL */ + cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to encrypt connection: %s", + cupsLastErrorString()); + return (-1); } -#endif /* HAVE_SSL */ + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Connection now encrypted."); return (0); } +#endif /* HAVE_SSL */ /* @@ -3271,12 +2917,14 @@ static char * /* O - Real filename */ get_file(cupsd_client_t *con, /* I - Client connection */ struct stat *filestats, /* O - File information */ char *filename, /* IO - Filename buffer */ - int len) /* I - Buffer length */ + size_t len) /* I - Buffer length */ { int status; /* Status of filesystem calls */ char *ptr; /* Pointer info filename */ - int plen; /* Remaining length after pointer */ - char language[7]; /* Language subdirectory, if any */ + size_t plen; /* Remaining length after pointer */ + char language[7], /* Language subdirectory, if any */ + dest[1024]; /* Destination name */ + int perm_check = 1; /* Do permissions check? */ /* @@ -3286,17 +2934,59 @@ get_file(cupsd_client_t *con, /* I - Client connection */ language[0] = '\0'; if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/')) + { + strlcpy(dest, con->uri + 5, sizeof(dest)); + ptr = dest + strlen(dest) - 4; + + if (ptr <= dest || strcmp(ptr, ".ppd")) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "Disallowed path \"%s\".", con->uri); + return (NULL); + } + + *ptr = '\0'; + if (!cupsdFindPrinter(dest)) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "No printer \"%s\" found.", dest); + return (NULL); + } + snprintf(filename, len, "%s%s", ServerRoot, con->uri); + + perm_check = 0; + } else if (!strncmp(con->uri, "/icons/", 7) && !strchr(con->uri + 7, '/')) { - snprintf(filename, len, "%s/%s", CacheDir, con->uri + 7); + strlcpy(dest, con->uri + 7, sizeof(dest)); + ptr = dest + strlen(dest) - 4; + + if (ptr <= dest || strcmp(ptr, ".png")) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "Disallowed path \"%s\".", con->uri); + return (NULL); + } + + *ptr = '\0'; + if (!cupsdFindDest(dest)) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "No printer \"%s\" found.", dest); + return (NULL); + } + + snprintf(filename, len, "%s/%s.png", CacheDir, dest); if (access(filename, F_OK) < 0) snprintf(filename, len, "%s/images/generic.png", DocumentRoot); + + perm_check = 0; } else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/')) snprintf(filename, len, "%s/rss/%s", CacheDir, con->uri + 5); - else if (!strncmp(con->uri, "/admin/conf/", 12)) - snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11); + else if (!strcmp(con->uri, "/admin/conf/cupsd.conf")) + { + strlcpy(filename, ConfigurationFile, len); + + perm_check = 0; + } else if (!strncmp(con->uri, "/admin/log/", 11)) { if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/') @@ -3307,6 +2997,8 @@ get_file(cupsd_client_t *con, /* I - Client connection */ strlcpy(filename, PageLog, len); else return (NULL); + + perm_check = 0; } else if (con->language) { @@ -3324,7 +3016,7 @@ get_file(cupsd_client_t *con, /* I - Client connection */ * then fallback to the default one... */ - if ((status = stat(filename, filestats)) != 0 && language[0] && + if ((status = lstat(filename, filestats)) != 0 && language[0] && strncmp(con->uri, "/icons/", 7) && strncmp(con->uri, "/ppd/", 5) && strncmp(con->uri, "/rss/", 5) && @@ -3341,7 +3033,7 @@ get_file(cupsd_client_t *con, /* I - Client connection */ if ((ptr = strchr(filename, '?')) != NULL) *ptr = '\0'; - if ((status = stat(filename, filestats)) != 0) + if ((status = lstat(filename, filestats)) != 0) { /* * Drop the language prefix and try the root directory... @@ -3353,12 +3045,33 @@ get_file(cupsd_client_t *con, /* I - Client connection */ if ((ptr = strchr(filename, '?')) != NULL) *ptr = '\0'; - status = stat(filename, filestats); + status = lstat(filename, filestats); } } /* - * If we're found a directory, get the index.html file instead... + * If we've found a symlink, 404 the sucker to avoid disclosing information. + */ + + if (!status && S_ISLNK(filestats->st_mode)) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "Symlinks such as \"%s\" are not allowed.", filename); + return (NULL); + } + + /* + * Similarly, if the file/directory does not have world read permissions, do + * not allow access... + */ + + if (!status && perm_check && !(filestats->st_mode & S_IROTH)) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "Files/directories such as \"%s\" must be world-readable.", filename); + return (NULL); + } + + /* + * If we've found a directory, get the index.html file instead... */ if (!status && S_ISDIR(filestats->st_mode)) @@ -3398,16 +3111,16 @@ get_file(cupsd_client_t *con, /* I - Client connection */ *ptr = '\0'; ptr = filename + strlen(filename); - plen = len - (ptr - filename); + plen = len - (size_t)(ptr - filename); strlcpy(ptr, "index.html", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); #ifdef HAVE_JAVA if (status) { strlcpy(ptr, "index.class", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); } #endif /* HAVE_JAVA */ @@ -3415,7 +3128,7 @@ get_file(cupsd_client_t *con, /* I - Client connection */ if (status) { strlcpy(ptr, "index.pl", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); } #endif /* HAVE_PERL */ @@ -3423,7 +3136,7 @@ get_file(cupsd_client_t *con, /* I - Client connection */ if (status) { strlcpy(ptr, "index.php", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); } #endif /* HAVE_PHP */ @@ -3431,24 +3144,42 @@ get_file(cupsd_client_t *con, /* I - Client connection */ if (status) { strlcpy(ptr, "index.pyc", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); } if (status) { strlcpy(ptr, "index.py", plen); - status = stat(filename, filestats); + status = lstat(filename, filestats); } #endif /* HAVE_PYTHON */ } while (status && language[0]); + + /* + * If we've found a symlink, 404 the sucker to avoid disclosing information. + */ + + if (!status && S_ISLNK(filestats->st_mode)) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "Symlinks such as \"%s\" are not allowed.", filename); + return (NULL); + } + + /* + * Similarly, if the file/directory does not have world read permissions, do + * not allow access... + */ + + if (!status && perm_check && !(filestats->st_mode & S_IROTH)) + { + cupsdLogClient(con, CUPSD_LOG_INFO, "Files/directories such as \"%s\" must be world-readable.", filename); + return (NULL); + } } - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] get_file filestats=%p, filename=%p, len=%d, " - "returning \"%s\".", con->http.fd, filestats, filename, len, - status ? "(null)" : filename); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "get_file: filestats=%p, filename=%p, len=" CUPS_LLFMT ", returning \"%s\".", filestats, filename, CUPS_LLCAST len, status ? "(null)" : filename); if (status) return (NULL); @@ -3477,9 +3208,9 @@ install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ if ((in = cupsFileOpen(con->filename, "rb")) == NULL) { - cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open request file \"%s\": %s", + cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to open request file \"%s\": %s", con->filename, strerror(errno)); - return (HTTP_SERVER_ERROR); + goto server_error; } /* @@ -3489,10 +3220,10 @@ install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ if ((out = cupsdCreateConfFile(ConfigurationFile, ConfigFilePerm)) == NULL) { cupsFileClose(in); - return (HTTP_SERVER_ERROR); + goto server_error; } - cupsdLogMessage(CUPSD_LOG_INFO, "Installing config file \"%s\"...", + cupsdLogClient(con, CUPSD_LOG_INFO, "Installing config file \"%s\"...", ConfigurationFile); /* @@ -3500,9 +3231,9 @@ install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ */ while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0) - if (cupsFileWrite(out, buffer, bytes) < bytes) + if (cupsFileWrite(out, buffer, (size_t)bytes) < bytes) { - cupsdLogMessage(CUPSD_LOG_ERROR, + cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to copy to config file \"%s\": %s", ConfigurationFile, strerror(errno)); @@ -3512,7 +3243,7 @@ install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ snprintf(filename, sizeof(filename), "%s.N", ConfigurationFile); cupsdUnlinkOrRemoveFile(filename); - return (HTTP_SERVER_ERROR); + goto server_error; } /* @@ -3522,7 +3253,7 @@ install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ cupsFileClose(in); if (cupsdCloseCreatedConfFile(out, ConfigurationFile)) - return (HTTP_SERVER_ERROR); + goto server_error; /* * Remove the request file... @@ -3542,7 +3273,18 @@ install_cupsd_conf(cupsd_client_t *con) /* I - Connection */ * Return that the file was created successfully... */ - return (HTTP_CREATED); + return (HTTP_STATUS_CREATED); + + /* + * Common exit for errors... + */ + + server_error: + + cupsdUnlinkOrRemoveFile(con->filename); + cupsdClearString(&con->filename); + + return (HTTP_STATUS_SERVER_ERROR); } @@ -3575,11 +3317,7 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ if (!type || _cups_strcasecmp(type->super, "application")) { - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] is_cgi filename=\"%s\", filestats=%p, " - "type=%s/%s, returning 0", con->http.fd, filename, - filestats, type ? type->super : "unknown", - type ? type->type : "unknown"); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 0.", filename, filestats, type ? type->super : "unknown", type ? type->type : "unknown"); return (0); } @@ -3595,10 +3333,7 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ if (options) cupsdSetStringf(&con->options, " %s", options); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] is_cgi filename=\"%s\", filestats=%p, " - "type=%s/%s, returning 1", con->http.fd, filename, - filestats, type->super, type->type); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type); return (1); } #ifdef HAVE_JAVA @@ -3615,10 +3350,7 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ else cupsdSetStringf(&con->options, " %s", filename); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] is_cgi filename=\"%s\", filestats=%p, " - "type=%s/%s, returning 1", con->http.fd, filename, - filestats, type->super, type->type); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type); return (1); } #endif /* HAVE_JAVA */ @@ -3636,10 +3368,7 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ else cupsdSetStringf(&con->options, " %s", filename); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] is_cgi filename=\"%s\", filestats=%p, " - "type=%s/%s, returning 1", con->http.fd, filename, - filestats, type->super, type->type); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type); return (1); } #endif /* HAVE_PERL */ @@ -3657,10 +3386,7 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ else cupsdSetStringf(&con->options, " %s", filename); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] is_cgi filename=\"%s\", filestats=%p, " - "type=%s/%s, returning 1", con->http.fd, filename, - filestats, type->super, type->type); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type); return (1); } #endif /* HAVE_PHP */ @@ -3678,18 +3404,12 @@ is_cgi(cupsd_client_t *con, /* I - Client connection */ else cupsdSetStringf(&con->options, " %s", filename); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] is_cgi filename=\"%s\", filestats=%p, " - "type=%s/%s, returning 1", con->http.fd, filename, - filestats, type->super, type->type); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type); return (1); } #endif /* HAVE_PYTHON */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] is_cgi filename=\"%s\", filestats=%p, " - "type=%s/%s, returning 0", con->http.fd, filename, - filestats, type->super, type->type); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 0.", filename, filestats, type->super, type->type); return (0); } @@ -3708,6 +3428,14 @@ is_path_absolute(const char *path) /* I - Input path */ if (path[0] != '/') return (0); + /* + * Check for "<" or quotes in the path and reject since this is probably + * someone trying to inject HTML... + */ + + if (strchr(path, '<') != NULL || strchr(path, '\"') != NULL || strchr(path, '\'') != NULL) + return (0); + /* * Check for "/.." in the path... */ @@ -3788,21 +3516,12 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ * be consistent with Apache... */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] pipe_command infile=%d, outfile=%p, " - "command=\"%s\", options=\"%s\", root=%d", - con->http.fd, infile, outfile, command, - options ? options : "(null)", root); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "pipe_command: infile=%d, outfile=%p, command=\"%s\", options=\"%s\", root=%d", infile, outfile, command, options ? options : "(null)", root); argv[0] = command; if (options) - { - commptr = options; - if (*commptr == ' ') - commptr ++; - strlcpy(argbuf, commptr, sizeof(argbuf)); - } + strlcpy(argbuf, options, sizeof(argbuf)); else argbuf[0] = '\0'; @@ -3872,9 +3591,9 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ */ if (commptr[1] >= '0' && commptr[1] <= '9') - *commptr = (commptr[1] - '0') << 4; + *commptr = (char)((commptr[1] - '0') << 4); else - *commptr = (tolower(commptr[1]) - 'a' + 10) << 4; + *commptr = (char)((tolower(commptr[1]) - 'a' + 10) << 4); if (commptr[2] >= '0' && commptr[2] <= '9') *commptr |= commptr[2] - '0'; @@ -3902,7 +3621,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ if (con->username[0]) { snprintf(auth_type, sizeof(auth_type), "AUTH_TYPE=%s", - httpGetField(HTTP(con), HTTP_FIELD_AUTHORIZATION)); + httpGetField(con->http, HTTP_FIELD_AUTHORIZATION)); if ((uriptr = strchr(auth_type + 10, ' ')) != NULL) *uriptr = '\0'; @@ -3953,11 +3672,11 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ strlcpy(lang, "LANG=C", sizeof(lang)); strlcpy(remote_addr, "REMOTE_ADDR=", sizeof(remote_addr)); - httpAddrString(con->http.hostaddr, remote_addr + 12, + httpAddrString(httpGetAddress(con->http), remote_addr + 12, sizeof(remote_addr) - 12); snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", - con->http.hostname); + httpGetHostname(con->http, NULL, 0)); snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri); if ((uriptr = strchr(script_name, '?')) != NULL) @@ -3966,14 +3685,14 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ snprintf(script_filename, sizeof(script_filename), "SCRIPT_FILENAME=%s%s", DocumentRoot, script_name + 12); - sprintf(server_port, "SERVER_PORT=%d", con->serverport); + snprintf(server_port, sizeof(server_port), "SERVER_PORT=%d", con->serverport); - if (con->http.fields[HTTP_FIELD_HOST][0]) + if (httpGetField(con->http, HTTP_FIELD_HOST)[0]) { char *nameptr; /* Pointer to ":port" */ snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", - con->http.fields[HTTP_FIELD_HOST]); + httpGetField(con->http, HTTP_FIELD_HOST)); if ((nameptr = strrchr(server_name, ':')) != NULL && !strchr(nameptr, ']')) *nameptr = '\0'; /* Strip trailing ":port" */ } @@ -4006,31 +3725,31 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ envp[envc ++] = remote_user; } - if (con->http.version == HTTP_1_1) + if (httpGetVersion(con->http) == HTTP_VERSION_1_1) envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1"; - else if (con->http.version == HTTP_1_0) + else if (httpGetVersion(con->http) == HTTP_VERSION_1_0) envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0"; else envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9"; - if (con->http.cookie) + if (httpGetCookie(con->http)) { snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s", - con->http.cookie); + httpGetCookie(con->http)); envp[envc ++] = http_cookie; } - if (con->http.fields[HTTP_FIELD_USER_AGENT][0]) + if (httpGetField(con->http, HTTP_FIELD_USER_AGENT)[0]) { snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s", - con->http.fields[HTTP_FIELD_USER_AGENT]); + httpGetField(con->http, HTTP_FIELD_USER_AGENT)); envp[envc ++] = http_user_agent; } - if (con->http.fields[HTTP_FIELD_REFERER][0]) + if (httpGetField(con->http, HTTP_FIELD_REFERER)[0]) { snprintf(http_referer, sizeof(http_referer), "HTTP_REFERER=%s", - con->http.fields[HTTP_FIELD_REFERER]); + httpGetField(con->http, HTTP_FIELD_REFERER)); envp[envc ++] = http_referer; } @@ -4054,7 +3773,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ sprintf(content_length, "CONTENT_LENGTH=" CUPS_LLFMT, CUPS_LLCAST con->bytes); snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s", - con->http.fields[HTTP_FIELD_CONTENT_TYPE]); + httpGetField(con->http, HTTP_FIELD_CONTENT_TYPE)); envp[envc ++] = "REQUEST_METHOD=POST"; envp[envc ++] = content_length; @@ -4065,7 +3784,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */ * Tell the CGI if we are using encryption... */ - if (con->http.tls) + if (httpIsEncrypted(con->http)) envp[envc ++] = "HTTPS=ON"; /* @@ -4148,7 +3867,7 @@ valid_host(cupsd_client_t *con) /* I - Client connection */ * Copy the Host: header for later use... */ - strlcpy(con->clientname, con->http.fields[HTTP_FIELD_HOST], + strlcpy(con->clientname, httpGetField(con->http, HTTP_FIELD_HOST), sizeof(con->clientname)); if ((ptr = strrchr(con->clientname, ':')) != NULL && !strchr(ptr, ']')) { @@ -4162,7 +3881,7 @@ valid_host(cupsd_client_t *con) /* I - Client connection */ * Then validate... */ - if (httpAddrLocalhost(con->http.hostaddr)) + if (httpAddrLocalhost(httpGetAddress(con->http))) { /* * Only allow "localhost" or the equivalent IPv4 or IPv6 numerical @@ -4314,45 +4033,29 @@ write_file(cupsd_client_t *con, /* I - Client connection */ { con->file = open(filename, O_RDONLY); - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] write_file code=%d, filename=\"%s\" (%d), " - "type=\"%s\", filestats=%p", con->http.fd, - code, filename, con->file, type ? type : "(null)", filestats); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "write_file: code=%d, filename=\"%s\" (%d), type=\"%s\", filestats=%p.", code, filename, con->file, type ? type : "(null)", filestats); if (con->file < 0) return (0); fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); - con->pipe_pid = 0; + con->pipe_pid = 0; + con->sent_header = 1; - if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE)) - return (0); + httpClearFields(con->http); - if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", - httpGetDateString(filestats->st_mtime)) < 0) - return (0); - if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n", - CUPS_LLCAST filestats->st_size) < 0) - return (0); - if (httpPrintf(HTTP(con), "\r\n") < 0) - return (0); - - if (cupsdFlushHeader(con) < 0) - return (0); + httpSetLength(con->http, (size_t)filestats->st_size); - con->http.data_encoding = HTTP_ENCODING_LENGTH; - con->http.data_remaining = filestats->st_size; + httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED, + httpGetDateString(filestats->st_mtime)); - if (con->http.data_remaining <= INT_MAX) - con->http._data_remaining = con->http.data_remaining; - else - con->http._data_remaining = INT_MAX; + if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE)) + return (0); - cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, - (cupsd_selfunc_t)cupsdWriteClient, con); + cupsdAddSelect(httpGetFd(con->http), NULL, (cupsd_selfunc_t)cupsdWriteClient, con); - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Sending file.", con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending file."); return (1); } @@ -4365,20 +4068,12 @@ write_file(cupsd_client_t *con, /* I - Client connection */ static void write_pipe(cupsd_client_t *con) /* I - Client connection */ { - cupsdLogMessage(CUPSD_LOG_DEBUG2, - "[Client %d] write_pipe CGI output on fd %d", - con->http.fd, con->file); + cupsdLogClient(con, CUPSD_LOG_DEBUG2, "write_pipe: CGI output on fd %d.", con->file); con->file_ready = 1; cupsdRemoveSelect(con->file); - cupsdAddSelect(con->http.fd, NULL, (cupsd_selfunc_t)cupsdWriteClient, con); + cupsdAddSelect(httpGetFd(con->http), NULL, (cupsd_selfunc_t)cupsdWriteClient, con); - cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] CGI data ready to be sent.", - con->http.fd); + cupsdLogClient(con, CUPSD_LOG_DEBUG, "CGI data ready to be sent."); } - - -/* - * End of "$Id$". - */