2 * "$Id: client.c 10338 2012-03-07 06:05:39Z mike $"
4 * Client routines for the CUPS scheduler.
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
9 * This file contains Kerberos support code, copyright 2006 by
12 * These coded instructions, statements, and computer programs are the
13 * property of Apple Inc. and are protected by Federal copyright
14 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
15 * which should have been included with this file. If this file is
16 * file is missing or damaged, see the license at "http://www.cups.org/".
20 * cupsdAcceptClient() - Accept a new client.
21 * cupsdCloseAllClients() - Close all remote clients immediately.
22 * cupsdCloseClient() - Close a remote client.
23 * cupsdFlushHeader() - Flush the header fields to the client.
24 * cupsdReadClient() - Read data from a client.
25 * cupsdSendCommand() - Send output from a command via HTTP.
26 * cupsdSendError() - Send an error message via HTTP.
27 * cupsdSendHeader() - Send an HTTP request.
28 * cupsdUpdateCGI() - Read status messages from CGI scripts and
30 * cupsdWriteClient() - Write data to a client as needed.
31 * check_if_modified() - Decode an "If-Modified-Since" line.
32 * compare_clients() - Compare two client connections.
33 * copy_cdsa_certificate() - Copy a SSL/TLS certificate from the System
35 * data_ready() - Check whether data is available from a client.
36 * encrypt_client() - Enable encryption for the client...
37 * get_file() - Get a filename and state info.
38 * install_conf_file() - Install a configuration file.
39 * is_cgi() - Is the resource a CGI script/program?
40 * is_path_absolute() - Is a path absolute and free of relative elements
42 * make_certificate() - Make a self-signed SSL/TLS certificate.
43 * pipe_command() - Pipe the output of a command to the remote
45 * valid_host() - Is the Host: field valid?
46 * write_file() - Send a file via HTTP.
47 * write_pipe() - Flag that data is available on the CGI pipe.
51 * Include necessary headers...
58 #endif /* HAVE_TCPD_H */
65 static int check_if_modified(cupsd_client_t *con,
66 struct stat *filestats);
67 static int compare_clients(cupsd_client_t *a, cupsd_client_t *b,
70 static CFArrayRef copy_cdsa_certificate(cupsd_client_t *con);
71 #endif /* HAVE_CDSASSL */
72 static int data_ready(cupsd_client_t *con);
74 static int encrypt_client(cupsd_client_t *con);
76 static char *get_file(cupsd_client_t *con, struct stat *filestats,
77 char *filename, int len);
78 static http_status_t install_conf_file(cupsd_client_t *con);
79 static int is_cgi(cupsd_client_t *con, const char *filename,
80 struct stat *filestats, mime_type_t *type);
81 static int is_path_absolute(const char *path);
83 static int make_certificate(cupsd_client_t *con);
85 static int pipe_command(cupsd_client_t *con, int infile, int *outfile,
86 char *command, char *options, int root);
87 static int valid_host(cupsd_client_t *con);
88 static int write_file(cupsd_client_t *con, http_status_t code,
89 char *filename, char *type,
90 struct stat *filestats);
91 static void write_pipe(cupsd_client_t *con);
95 * 'cupsdAcceptClient()' - Accept a new client.
99 cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
101 int count; /* Count of connections on a host */
102 int val; /* Parameter value */
103 cupsd_client_t *con, /* New client pointer */
104 *tempcon; /* Temporary client pointer */
105 http_addrlist_t *addrlist, /* List of adddresses for host */
106 *addr; /* Current address */
107 socklen_t addrlen; /* Length of address */
108 char *hostname; /* Hostname for address */
109 http_addr_t temp; /* Temporary address variable */
110 static time_t last_dos = 0; /* Time of last DoS attack */
112 struct request_info wrap_req; /* TCP wrappers request information */
113 #endif /* HAVE_TCPD_H */
116 cupsdLogMessage(CUPSD_LOG_DEBUG2,
117 "cupsdAcceptClient(lis=%p(%d)) Clients=%d",
118 lis, lis->fd, cupsArrayCount(Clients));
121 * Make sure we don't have a full set of clients already...
124 if (cupsArrayCount(Clients) == MaxClients)
128 * Get a pointer to the next available client...
132 Clients = cupsArrayNew(NULL, NULL);
136 cupsdLogMessage(CUPSD_LOG_ERROR,
137 "Unable to allocate memory for clients array!");
138 cupsdPauseListening();
143 ActiveClients = cupsArrayNew((cups_array_func_t)compare_clients, NULL);
147 cupsdLogMessage(CUPSD_LOG_ERROR,
148 "Unable to allocate memory for active clients array!");
149 cupsdPauseListening();
153 if ((con = calloc(1, sizeof(cupsd_client_t))) == NULL)
155 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate memory for client!");
156 cupsdPauseListening();
160 con->http.activity = time(NULL);
162 con->http.hostaddr = &(con->clientaddr);
163 con->http.wait_value = 10000;
166 * Accept the client and get the remote address...
169 addrlen = sizeof(http_addr_t);
171 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)con->http.hostaddr,
174 if (errno == ENFILE || errno == EMFILE)
175 cupsdPauseListening();
177 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to accept client connection - %s.",
185 * Save the connected port number...
188 _httpAddrSetPort(con->http.hostaddr, _httpAddrPort(&(lis->address)));
192 * Convert IPv4 over IPv6 addresses (::ffff:n.n.n.n) to IPv4 forms we
193 * can more easily use...
196 if (lis->address.addr.sa_family == AF_INET6 &&
197 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[0] == 0 &&
198 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[1] == 0 &&
199 ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2]) == 0xffff)
200 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2] = 0;
201 #endif /* AF_INET6 */
204 * Check the number of clients on the same address...
207 for (count = 0, tempcon = (cupsd_client_t *)cupsArrayFirst(Clients);
209 tempcon = (cupsd_client_t *)cupsArrayNext(Clients))
210 if (httpAddrEqual(tempcon->http.hostaddr, con->http.hostaddr))
213 if (count >= MaxClientsPerHost)
217 if (count >= MaxClientsPerHost)
219 if ((time(NULL) - last_dos) >= 60)
221 last_dos = time(NULL);
222 cupsdLogMessage(CUPSD_LOG_WARN,
223 "Possible DoS attack - more than %d clients connecting "
226 httpAddrString(con->http.hostaddr, con->http.hostname,
227 sizeof(con->http.hostname)));
231 closesocket(con->http.fd);
241 * Get the hostname or format the IP address as needed...
244 if (httpAddrLocalhost(con->http.hostaddr))
247 * Map accesses from the loopback interface to "localhost"...
250 strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname));
251 hostname = con->http.hostname;
256 * Map accesses from the same host to the server name.
260 hostname = httpAddrLookup(con->http.hostaddr, con->http.hostname,
261 sizeof(con->http.hostname));
265 httpAddrString(con->http.hostaddr, con->http.hostname,
266 sizeof(con->http.hostname));
270 if (hostname == NULL && HostNameLookups == 2)
273 * Can't have an unresolved IP address with double-lookups enabled...
277 closesocket(con->http.fd);
282 cupsdLogMessage(CUPSD_LOG_WARN,
283 "Name lookup failed - connection from %s closed!",
290 if (HostNameLookups == 2)
293 * Do double lookups as needed...
296 if ((addrlist = httpAddrGetList(con->http.hostname, AF_UNSPEC, NULL))
300 * See if the hostname maps to the same IP address...
303 for (addr = addrlist; addr; addr = addr->next)
304 if (httpAddrEqual(con->http.hostaddr, &(addr->addr)))
310 httpAddrFreeList(addrlist);
315 * Can't have a hostname that doesn't resolve to the same IP address
316 * with double-lookups enabled...
320 closesocket(con->http.fd);
325 cupsdLogMessage(CUPSD_LOG_WARN,
326 "IP lookup failed - connection from %s closed!",
335 * See if the connection is denied by TCP wrappers...
338 request_init(&wrap_req, RQ_DAEMON, "cupsd", RQ_FILE, con->http.fd, NULL);
341 if (!hosts_access(&wrap_req))
344 closesocket(con->http.fd);
349 cupsdLogMessage(CUPSD_LOG_WARN,
350 "Connection from %s refused by /etc/hosts.allow and "
351 "/etc/hosts.deny rules.", con->http.hostname);
355 #endif /* HAVE_TCPD_H */
358 if (con->http.hostaddr->addr.sa_family == AF_LOCAL)
359 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s (Domain)",
360 con->http.fd, con->http.hostname);
362 #endif /* AF_LOCAL */
363 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv%d)",
364 con->http.fd, con->http.hostname,
365 _httpAddrPort(con->http.hostaddr),
366 _httpAddrFamily(con->http.hostaddr) == AF_INET ? 4 : 6);
369 * Get the local address the client connected to...
372 addrlen = sizeof(temp);
373 if (getsockname(con->http.fd, (struct sockaddr *)&temp, &addrlen))
375 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get local address - %s",
378 strcpy(con->servername, "localhost");
379 con->serverport = LocalPort;
382 else if (_httpAddrFamily(&temp) == AF_LOCAL)
384 strcpy(con->servername, "localhost");
385 con->serverport = LocalPort;
387 #endif /* AF_LOCAL */
390 if (httpAddrLocalhost(&temp))
391 strlcpy(con->servername, "localhost", sizeof(con->servername));
392 else if (HostNameLookups)
393 httpAddrLookup(&temp, con->servername, sizeof(con->servername));
395 httpAddrString(&temp, con->servername, sizeof(con->servername));
397 con->serverport = _httpAddrPort(&(lis->address));
400 cupsArrayAdd(Clients, con);
403 * Using TCP_NODELAY improves responsiveness, especially on systems with a slow
404 * loopback interface. Since we write large buffers when sending print files
405 * and requests there shouldn't be any performance penalty for this...
409 setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
412 * Close this file on all execs...
415 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
418 * Add the socket to the server select.
421 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
424 * Temporarily suspend accept()'s until we lose a client...
427 if (cupsArrayCount(Clients) == MaxClients)
428 cupsdPauseListening();
432 * See if we are connecting on a secure port...
435 if (lis->encryption == HTTP_ENCRYPT_ALWAYS)
438 * https connection; go secure...
441 con->http.encryption = HTTP_ENCRYPT_ALWAYS;
443 if (!encrypt_client(con))
444 cupsdCloseClient(con);
448 #endif /* HAVE_SSL */
453 * 'cupsdCloseAllClients()' - Close all remote clients immediately.
457 cupsdCloseAllClients(void)
459 cupsd_client_t *con; /* Current client */
462 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d",
463 cupsArrayCount(Clients));
465 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
467 con = (cupsd_client_t *)cupsArrayNext(Clients))
468 if (cupsdCloseClient(con))
469 cupsdCloseClient(con);
474 * 'cupsdCloseClient()' - Close a remote client.
477 int /* O - 1 if partial close, 0 if fully closed */
478 cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */
480 int partial; /* Do partial close for SSL? */
482 SSL_CTX *context; /* Context for encryption */
483 unsigned long error; /* Error code */
484 #elif defined(HAVE_GNUTLS)
485 int error; /* Error code */
486 gnutls_certificate_server_credentials *credentials;
487 /* TLS credentials */
488 # elif defined(HAVE_CDSASSL)
489 #endif /* HAVE_LIBSSL */
492 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCloseClient: %d", con->http.fd);
495 * Flush pending writes before closing...
498 httpFlushWrite(HTTP(con));
504 * Shutdown encryption as needed...
512 context = SSL_get_SSL_CTX(con->http.tls);
514 switch (SSL_shutdown(con->http.tls))
517 cupsdLogMessage(CUPSD_LOG_DEBUG,
518 "SSL shutdown successful!");
521 cupsdLogMessage(CUPSD_LOG_ERROR,
522 "Fatal error during SSL shutdown!");
524 while ((error = ERR_get_error()) != 0)
525 cupsdLogMessage(CUPSD_LOG_ERROR, "SSL shutdown failed: %s",
526 ERR_error_string(error, NULL));
530 SSL_CTX_free(context);
531 SSL_free(con->http.tls);
533 # elif defined(HAVE_GNUTLS)
534 credentials = (gnutls_certificate_server_credentials *)(con->http.tls_credentials);
536 error = gnutls_bye(con->http.tls, GNUTLS_SHUT_WR);
539 case GNUTLS_E_SUCCESS:
540 cupsdLogMessage(CUPSD_LOG_DEBUG,
541 "SSL shutdown successful!");
544 cupsdLogMessage(CUPSD_LOG_ERROR,
545 "SSL shutdown failed: %s", gnutls_strerror(error));
549 gnutls_deinit(con->http.tls);
550 gnutls_certificate_free_credentials(*credentials);
553 # elif defined(HAVE_CDSASSL)
554 while (SSLClose(con->http.tls) == errSSLWouldBlock)
557 SSLDisposeContext(con->http.tls);
559 if (con->http.tls_credentials)
560 CFRelease(con->http.tls_credentials);
562 # endif /* HAVE_LIBSSL */
564 con->http.tls = NULL;
566 #endif /* HAVE_SSL */
568 if (con->pipe_pid != 0)
571 * Stop any CGI process...
574 cupsdEndProcess(con->pipe_pid, 1);
580 cupsdRemoveSelect(con->file);
587 * Close the socket and clear the file from the input set for select()...
590 if (con->http.fd >= 0)
592 cupsArrayRemove(ActiveClients, con);
598 * Only do a partial close so that the encrypted client gets everything.
601 shutdown(con->http.fd, 0);
602 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
607 * Shut the socket down fully...
610 cupsdRemoveSelect(con->http.fd);
622 if (con->http.input_set)
623 free(con->http.input_set);
625 httpClearCookie(HTTP(con));
626 httpClearFields(HTTP(con));
628 cupsdClearString(&con->filename);
629 cupsdClearString(&con->command);
630 cupsdClearString(&con->options);
631 cupsdClearString(&con->query_string);
635 ippDelete(con->request);
641 ippDelete(con->response);
642 con->response = NULL;
647 cupsLangFree(con->language);
648 con->language = NULL;
651 #ifdef HAVE_AUTHORIZATION_H
654 AuthorizationFree(con->authref, kAuthorizationFlagDefaults);
657 #endif /* HAVE_AUTHORIZATION_H */
660 * Re-enable new client connections if we are going back under the
664 if (cupsArrayCount(Clients) == MaxClients)
665 cupsdResumeListening();
668 * Compact the list of clients as necessary...
671 cupsArrayRemove(Clients, con);
681 * 'cupsdFlushHeader()' - Flush the header fields to the client.
684 int /* I - Bytes written or -1 on error */
685 cupsdFlushHeader(cupsd_client_t *con) /* I - Client to flush to */
687 int bytes = httpFlushWrite(HTTP(con));
689 con->http.data_encoding = HTTP_ENCODE_LENGTH;
696 * 'cupsdReadClient()' - Read data from a client.
700 cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */
702 char line[32768], /* Line from client... */
703 operation[64], /* Operation code from socket */
704 version[64], /* HTTP version number string */
705 locale[64], /* Locale */
706 *ptr; /* Pointer into strings */
707 int major, minor; /* HTTP version numbers */
708 http_status_t status; /* Transfer status */
709 ipp_state_t ipp_state; /* State of IPP transfer */
710 int bytes; /* Number of bytes to POST */
711 char *filename; /* Name of file for GET/HEAD */
712 char buf[1024]; /* Buffer for real filename */
713 struct stat filestats; /* File information */
714 mime_type_t *type; /* MIME type of file */
715 cupsd_printer_t *p; /* Printer */
716 static unsigned request_id = 0; /* Request ID for temp files */
719 status = HTTP_CONTINUE;
721 cupsdLogMessage(CUPSD_LOG_DEBUG2,
722 "cupsdReadClient(con=%p(%d)) "
723 "con->http.error=%d "
724 "con->http.used=%d, "
725 "con->http.state=%d "
726 "con->data_encoding=HTTP_ENCODE_%s, "
727 "con->data_remaining=" CUPS_LLFMT ", "
729 con, con->http.fd, con->http.error, con->http.used,
731 con->http.data_encoding == HTTP_ENCODE_CHUNKED ?
732 "CHUNKED" : "LENGTH",
733 CUPS_LLCAST con->http.data_remaining, con->file);
739 * Automatically check for a SSL/TLS handshake...
744 if (recv(con->http.fd, buf, 1, MSG_PEEK) == 1 &&
745 (!buf[0] || !strchr("DGHOPT", buf[0])))
748 * Encrypt this connection...
751 cupsdLogMessage(CUPSD_LOG_DEBUG2,
752 "cupsdReadClient: Saw first byte %02X, auto-negotiating "
753 "SSL/TLS session...", buf[0] & 255);
755 if (!encrypt_client(con))
756 cupsdCloseClient(con);
761 #endif /* HAVE_SSL */
763 switch (con->http.state)
767 * See if we've received a request line...
770 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
772 if (con->http.error && con->http.error != EPIPE)
773 cupsdLogMessage(CUPSD_LOG_DEBUG,
774 "cupsdReadClient: %d WAITING Closing for error %d "
775 "(%s)", con->http.fd, con->http.error,
776 strerror(con->http.error));
778 cupsdLogMessage(CUPSD_LOG_DEBUG,
779 "cupsdReadClient: %d WAITING Closing on EOF",
782 cupsdCloseClient(con);
787 * Ignore blank request lines...
794 * Clear other state variables...
797 httpClearFields(HTTP(con));
799 con->http.activity = time(NULL);
800 con->http.version = HTTP_1_0;
801 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
802 con->http.data_encoding = HTTP_ENCODE_LENGTH;
803 con->http.data_remaining = 0;
804 con->http._data_remaining = 0;
805 con->operation = HTTP_WAITING;
810 con->username[0] = '\0';
811 con->password[0] = '\0';
814 cupsdClearString(&con->command);
815 cupsdClearString(&con->options);
816 cupsdClearString(&con->query_string);
820 ippDelete(con->request);
826 ippDelete(con->response);
827 con->response = NULL;
832 cupsLangFree(con->language);
833 con->language = NULL;
839 #endif /* HAVE_GSSAPI */
842 * Grab the request line...
845 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
850 cupsdLogMessage(CUPSD_LOG_ERROR,
851 "Bad request line \"%s\" from %s!",
852 _httpEncodeURI(buf, line, sizeof(buf)),
854 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
855 cupsdCloseClient(con);
859 con->http.version = HTTP_0_9;
862 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
864 cupsdLogMessage(CUPSD_LOG_ERROR,
865 "Bad request line \"%s\" from %s!",
866 _httpEncodeURI(buf, line, sizeof(buf)),
868 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
869 cupsdCloseClient(con);
875 con->http.version = (http_version_t)(major * 100 + minor);
876 if (con->http.version == HTTP_1_1 && KeepAlive)
877 con->http.keep_alive = HTTP_KEEPALIVE_ON;
879 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
883 cupsdLogMessage(CUPSD_LOG_ERROR,
884 "Unsupported request line \"%s\" from %s!",
885 _httpEncodeURI(buf, line, sizeof(buf)),
887 cupsdSendError(con, HTTP_NOT_SUPPORTED, CUPSD_AUTH_NONE);
888 cupsdCloseClient(con);
895 * Handle full URLs in the request line...
898 if (strcmp(con->uri, "*"))
900 char scheme[HTTP_MAX_URI], /* Method/scheme */
901 userpass[HTTP_MAX_URI], /* Username:password */
902 hostname[HTTP_MAX_URI], /* Hostname */
903 resource[HTTP_MAX_URI]; /* Resource path */
904 int port; /* Port number */
908 * Separate the URI into its components...
911 httpSeparateURI(HTTP_URI_CODING_MOST, con->uri,
912 scheme, sizeof(scheme),
913 userpass, sizeof(userpass),
914 hostname, sizeof(hostname), &port,
915 resource, sizeof(resource));
918 * Only allow URIs with the servername, localhost, or an IP
922 if (strcmp(scheme, "file") &&
923 _cups_strcasecmp(hostname, ServerName) &&
924 _cups_strcasecmp(hostname, "localhost") &&
925 !isdigit(hostname[0]) && hostname[0] != '[')
928 * Nope, we don't do proxies...
931 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad URI \"%s\" in request!",
933 cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE);
934 cupsdCloseClient(con);
939 * Copy the resource portion back into the URI; both resource and
940 * con->uri are HTTP_MAX_URI bytes in size...
943 strcpy(con->uri, resource);
947 * Process the request...
950 if (!strcmp(operation, "GET"))
951 con->http.state = HTTP_GET;
952 else if (!strcmp(operation, "PUT"))
953 con->http.state = HTTP_PUT;
954 else if (!strcmp(operation, "POST"))
955 con->http.state = HTTP_POST;
956 else if (!strcmp(operation, "DELETE"))
957 con->http.state = HTTP_DELETE;
958 else if (!strcmp(operation, "TRACE"))
959 con->http.state = HTTP_TRACE;
960 else if (!strcmp(operation, "OPTIONS"))
961 con->http.state = HTTP_OPTIONS;
962 else if (!strcmp(operation, "HEAD"))
963 con->http.state = HTTP_HEAD;
966 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad operation \"%s\"!", operation);
967 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
968 cupsdCloseClient(con);
972 gettimeofday(&(con->start), NULL);
973 con->operation = con->http.state;
975 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %s %s HTTP/%d.%d",
976 con->http.fd, operation, con->uri,
977 con->http.version / 100, con->http.version % 100);
979 con->http.status = HTTP_OK;
981 if (!cupsArrayFind(ActiveClients, con))
983 cupsArrayAdd(ActiveClients, con);
995 * Parse incoming parameters until the status changes...
998 while ((status = httpUpdate(HTTP(con))) == HTTP_CONTINUE)
999 if (!data_ready(con))
1002 if (status != HTTP_OK && status != HTTP_CONTINUE)
1004 if (con->http.error && con->http.error != EPIPE)
1005 cupsdLogMessage(CUPSD_LOG_DEBUG,
1006 "cupsdReadClient: %d FIELDS Closing for error %d "
1007 "(%s)", con->http.fd, con->http.error,
1008 strerror(con->http.error));
1010 cupsdLogMessage(CUPSD_LOG_DEBUG,
1011 "cupsdReadClient: %d FIELDS Closing on EOF",
1014 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
1015 cupsdCloseClient(con);
1021 if (!data_ready(con) && recv(con->http.fd, buf, 1, MSG_PEEK) < 1)
1024 * Connection closed...
1027 cupsdLogMessage(CUPSD_LOG_DEBUG,
1028 "cupsdReadClient: %d Closing on EOF", con->http.fd);
1029 cupsdCloseClient(con);
1032 break; /* Anti-compiler-warning-code */
1036 * Handle new transfers...
1039 if (status == HTTP_OK)
1041 if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
1044 * Figure out the locale from the Accept-Language and Content-Type
1048 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
1052 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
1056 if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
1057 "charset=")) != NULL)
1060 * Combine language and charset, and trim any extra params in the
1064 snprintf(locale, sizeof(locale), "%s.%s",
1065 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8);
1067 if ((ptr = strchr(locale, ',')) != NULL)
1071 snprintf(locale, sizeof(locale), "%s.UTF-8",
1072 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
1074 con->language = cupsLangGet(locale);
1077 con->language = cupsLangGet(DefaultLocale);
1079 cupsdAuthorize(con);
1081 if (!_cups_strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive",
1083 con->http.keep_alive = HTTP_KEEPALIVE_ON;
1084 else if (!_cups_strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "close", 5))
1085 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1087 if (!con->http.fields[HTTP_FIELD_HOST][0] &&
1088 con->http.version >= HTTP_1_1)
1091 * HTTP/1.1 and higher require the "Host:" field...
1094 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1096 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing Host: field in request!");
1097 cupsdCloseClient(con);
1101 else if (!valid_host(con))
1104 * Access to localhost must use "localhost" or the corresponding IPv4
1105 * or IPv6 values in the Host: field.
1108 cupsdLogMessage(CUPSD_LOG_ERROR,
1109 "Request from \"%s\" using invalid Host: field \"%s\"",
1110 con->http.hostname, con->http.fields[HTTP_FIELD_HOST]);
1112 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1114 cupsdCloseClient(con);
1118 else if (con->operation == HTTP_OPTIONS)
1121 * Do OPTIONS command...
1124 if (con->best && con->best->type != CUPSD_AUTH_NONE)
1126 if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE))
1128 cupsdCloseClient(con);
1133 if (!_cups_strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
1134 con->http.tls == NULL)
1138 * Do encryption stuff...
1141 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
1143 cupsdCloseClient(con);
1147 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1148 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1149 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1150 httpPrintf(HTTP(con), "\r\n");
1152 if (cupsdFlushHeader(con) < 0)
1154 cupsdCloseClient(con);
1158 if (!encrypt_client(con))
1160 cupsdCloseClient(con);
1164 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
1166 cupsdCloseClient(con);
1169 #endif /* HAVE_SSL */
1172 if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE))
1174 cupsdCloseClient(con);
1178 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
1179 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1180 httpPrintf(HTTP(con), "\r\n");
1182 if (cupsdFlushHeader(con) < 0)
1184 cupsdCloseClient(con);
1188 else if (!is_path_absolute(con->uri))
1191 * Protect against malicious users!
1194 cupsdLogMessage(CUPSD_LOG_ERROR,
1195 "Request for non-absolute resource \"%s\"!", con->uri);
1197 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
1199 cupsdCloseClient(con);
1205 if (!_cups_strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
1206 con->http.tls == NULL)
1210 * Do encryption stuff...
1213 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
1215 cupsdCloseClient(con);
1219 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1220 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1221 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1222 httpPrintf(HTTP(con), "\r\n");
1224 if (cupsdFlushHeader(con) < 0)
1226 cupsdCloseClient(con);
1230 if (!encrypt_client(con))
1232 cupsdCloseClient(con);
1236 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
1238 cupsdCloseClient(con);
1241 #endif /* HAVE_SSL */
1244 if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_OK)
1246 cupsdSendError(con, status, CUPSD_AUTH_NONE);
1247 cupsdCloseClient(con);
1251 if (con->http.expect &&
1252 (con->operation == HTTP_POST || con->operation == HTTP_PUT))
1254 if (con->http.expect == HTTP_CONTINUE)
1257 * Send 100-continue header...
1260 if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL, CUPSD_AUTH_NONE))
1262 cupsdCloseClient(con);
1269 * Send 417-expectation-failed header...
1272 if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL,
1275 cupsdCloseClient(con);
1279 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1280 httpPrintf(HTTP(con), "\r\n");
1282 if (cupsdFlushHeader(con) < 0)
1284 cupsdCloseClient(con);
1290 switch (con->http.state)
1292 case HTTP_GET_SEND :
1293 if (!strncmp(con->uri, "/printers/", 10) &&
1294 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd"))
1297 * Send PPD file - get the real printer name since printer
1298 * names are not case sensitive but filenames can be...
1301 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1303 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1304 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1307 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1309 cupsdCloseClient(con);
1316 else if ((!strncmp(con->uri, "/printers/", 10) ||
1317 !strncmp(con->uri, "/classes/", 9)) &&
1318 !strcmp(con->uri + strlen(con->uri) - 4, ".png"))
1321 * Send icon file - get the real queue name since queue names are
1322 * not case sensitive but filenames can be...
1325 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".png" */
1327 if (!strncmp(con->uri, "/printers/", 10))
1328 p = cupsdFindPrinter(con->uri + 10);
1330 p = cupsdFindClass(con->uri + 9);
1333 snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name);
1336 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1338 cupsdCloseClient(con);
1345 else if (!WebInterface)
1348 * Web interface is disabled. Show an appropriate message...
1351 if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE))
1353 cupsdCloseClient(con);
1360 if ((!strncmp(con->uri, "/admin", 6) &&
1361 strncmp(con->uri, "/admin/conf/", 12) &&
1362 strncmp(con->uri, "/admin/log/", 11)) ||
1363 !strncmp(con->uri, "/printers", 9) ||
1364 !strncmp(con->uri, "/classes", 8) ||
1365 !strncmp(con->uri, "/help", 5) ||
1366 !strncmp(con->uri, "/jobs", 5))
1369 * Send CGI output...
1372 if (!strncmp(con->uri, "/admin", 6))
1374 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi",
1377 cupsdSetString(&con->options, strchr(con->uri + 6, '?'));
1379 else if (!strncmp(con->uri, "/printers", 9))
1381 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1384 if (con->uri[9] && con->uri[10])
1385 cupsdSetString(&con->options, con->uri + 9);
1387 cupsdSetString(&con->options, NULL);
1389 else if (!strncmp(con->uri, "/classes", 8))
1391 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1394 if (con->uri[8] && con->uri[9])
1395 cupsdSetString(&con->options, con->uri + 8);
1397 cupsdSetString(&con->options, NULL);
1399 else if (!strncmp(con->uri, "/jobs", 5))
1401 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1404 if (con->uri[5] && con->uri[6])
1405 cupsdSetString(&con->options, con->uri + 5);
1407 cupsdSetString(&con->options, NULL);
1411 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1414 if (con->uri[5] && con->uri[6])
1415 cupsdSetString(&con->options, con->uri + 5);
1417 cupsdSetString(&con->options, NULL);
1420 if (!cupsdSendCommand(con, con->command, con->options, 0))
1422 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1424 cupsdCloseClient(con);
1429 cupsdLogRequest(con, HTTP_OK);
1431 if (con->http.version <= HTTP_1_0)
1432 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1434 else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
1435 (strchr(con->uri + 12, '/') ||
1436 strlen(con->uri) == 12)) ||
1437 (!strncmp(con->uri, "/admin/log/", 11) &&
1438 (strchr(con->uri + 11, '/') ||
1439 strlen(con->uri) == 11)))
1442 * GET can only be done to configuration files directly under
1446 cupsdLogMessage(CUPSD_LOG_ERROR,
1447 "Request for subdirectory \"%s\"!", con->uri);
1449 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
1451 cupsdCloseClient(con);
1463 if ((filename = get_file(con, &filestats, buf,
1464 sizeof(buf))) == NULL)
1466 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1468 cupsdCloseClient(con);
1475 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
1477 if (is_cgi(con, filename, &filestats, type))
1480 * Note: con->command and con->options were set by
1484 if (!cupsdSendCommand(con, con->command, con->options, 0))
1486 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1488 cupsdCloseClient(con);
1493 cupsdLogRequest(con, HTTP_OK);
1495 if (con->http.version <= HTTP_1_0)
1496 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1500 if (!check_if_modified(con, &filestats))
1502 if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
1504 cupsdCloseClient(con);
1511 strcpy(line, "text/plain");
1513 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1515 if (!write_file(con, HTTP_OK, filename, line, &filestats))
1517 cupsdCloseClient(con);
1524 case HTTP_POST_RECV :
1526 * See if the POST request includes a Content-Length field, and if
1527 * so check the length against any limits that are set...
1530 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1531 MaxRequestSize > 0 &&
1532 con->http.data_remaining > MaxRequestSize)
1535 * Request too large...
1538 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
1540 cupsdCloseClient(con);
1546 else if (con->http.data_remaining < 0 ||
1547 (!con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1548 con->http.data_encoding == HTTP_ENCODE_LENGTH))
1551 * Negative content lengths are invalid!
1554 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1556 cupsdCloseClient(con);
1564 * See what kind of POST request this is; for IPP requests the
1565 * content-type field will be "application/ipp"...
1568 if (!strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
1570 con->request = ippNew();
1571 else if (!WebInterface)
1574 * Web interface is disabled. Show an appropriate message...
1577 if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE))
1579 cupsdCloseClient(con);
1585 else if ((!strncmp(con->uri, "/admin", 6) &&
1586 strncmp(con->uri, "/admin/conf/", 12) &&
1587 strncmp(con->uri, "/admin/log/", 11)) ||
1588 !strncmp(con->uri, "/printers", 9) ||
1589 !strncmp(con->uri, "/classes", 8) ||
1590 !strncmp(con->uri, "/help", 5) ||
1591 !strncmp(con->uri, "/jobs", 5))
1597 if (!strncmp(con->uri, "/admin", 6))
1599 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi",
1602 cupsdSetString(&con->options, strchr(con->uri + 6, '?'));
1604 else if (!strncmp(con->uri, "/printers", 9))
1606 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1609 if (con->uri[9] && con->uri[10])
1610 cupsdSetString(&con->options, con->uri + 9);
1612 cupsdSetString(&con->options, NULL);
1614 else if (!strncmp(con->uri, "/classes", 8))
1616 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1619 if (con->uri[8] && con->uri[9])
1620 cupsdSetString(&con->options, con->uri + 8);
1622 cupsdSetString(&con->options, NULL);
1624 else if (!strncmp(con->uri, "/jobs", 5))
1626 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1629 if (con->uri[5] && con->uri[6])
1630 cupsdSetString(&con->options, con->uri + 5);
1632 cupsdSetString(&con->options, NULL);
1636 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1639 if (con->uri[5] && con->uri[6])
1640 cupsdSetString(&con->options, con->uri + 5);
1642 cupsdSetString(&con->options, NULL);
1645 if (con->http.version <= HTTP_1_0)
1646 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1654 if ((filename = get_file(con, &filestats, buf,
1655 sizeof(buf))) == NULL)
1657 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1659 cupsdCloseClient(con);
1666 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
1668 if (!is_cgi(con, filename, &filestats, type))
1671 * Only POST to CGI's...
1674 if (!cupsdSendError(con, HTTP_UNAUTHORIZED, CUPSD_AUTH_NONE))
1676 cupsdCloseClient(con);
1683 case HTTP_PUT_RECV :
1685 * Validate the resource name...
1688 if (strncmp(con->uri, "/admin/conf/", 12) ||
1689 strchr(con->uri + 12, '/') ||
1690 strlen(con->uri) == 12)
1693 * PUT can only be done to configuration files under
1697 cupsdLogMessage(CUPSD_LOG_ERROR,
1698 "Request for subdirectory \"%s\"!", con->uri);
1700 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
1702 cupsdCloseClient(con);
1710 * See if the PUT request includes a Content-Length field, and if
1711 * so check the length against any limits that are set...
1714 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1715 MaxRequestSize > 0 &&
1716 con->http.data_remaining > MaxRequestSize)
1719 * Request too large...
1722 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
1724 cupsdCloseClient(con);
1730 else if (con->http.data_remaining < 0)
1733 * Negative content lengths are invalid!
1736 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1738 cupsdCloseClient(con);
1746 * Open a temporary file to hold the request...
1749 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
1751 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1755 cupsdLogMessage(CUPSD_LOG_ERROR,
1756 "Unable to create request file %s: %s",
1757 con->filename, strerror(errno));
1759 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
1761 cupsdCloseClient(con);
1766 fchmod(con->file, 0640);
1767 fchown(con->file, RunUser, Group);
1768 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1773 cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE);
1774 cupsdCloseClient(con);
1778 if (!strncmp(con->uri, "/printers/", 10) &&
1779 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd"))
1782 * Send PPD file - get the real printer name since printer
1783 * names are not case sensitive but filenames can be...
1786 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1788 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1789 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1792 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1794 cupsdCloseClient(con);
1801 else if (!strncmp(con->uri, "/printers/", 10) &&
1802 !strcmp(con->uri + strlen(con->uri) - 4, ".png"))
1805 * Send PNG file - get the real printer name since printer
1806 * names are not case sensitive but filenames can be...
1809 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1811 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1812 snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name);
1815 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1817 cupsdCloseClient(con);
1824 else if (!WebInterface)
1826 if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
1828 cupsdCloseClient(con);
1832 if (httpPrintf(HTTP(con), "\r\n") < 0)
1834 cupsdCloseClient(con);
1838 if (cupsdFlushHeader(con) < 0)
1840 cupsdCloseClient(con);
1844 con->http.state = HTTP_WAITING;
1848 if ((!strncmp(con->uri, "/admin", 6) &&
1849 strncmp(con->uri, "/admin/conf/", 12) &&
1850 strncmp(con->uri, "/admin/log/", 11)) ||
1851 !strncmp(con->uri, "/printers", 9) ||
1852 !strncmp(con->uri, "/classes", 8) ||
1853 !strncmp(con->uri, "/help", 5) ||
1854 !strncmp(con->uri, "/jobs", 5))
1860 if (!cupsdSendHeader(con, HTTP_OK, "text/html", CUPSD_AUTH_NONE))
1862 cupsdCloseClient(con);
1866 if (httpPrintf(HTTP(con), "\r\n") < 0)
1868 cupsdCloseClient(con);
1872 if (cupsdFlushHeader(con) < 0)
1874 cupsdCloseClient(con);
1878 cupsdLogRequest(con, HTTP_OK);
1880 else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
1881 (strchr(con->uri + 12, '/') ||
1882 strlen(con->uri) == 12)) ||
1883 (!strncmp(con->uri, "/admin/log/", 11) &&
1884 (strchr(con->uri + 11, '/') ||
1885 strlen(con->uri) == 11)))
1888 * HEAD can only be done to configuration files under
1892 cupsdLogMessage(CUPSD_LOG_ERROR,
1893 "Request for subdirectory \"%s\"!", con->uri);
1895 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
1897 cupsdCloseClient(con);
1903 else if ((filename = get_file(con, &filestats, buf,
1904 sizeof(buf))) == NULL)
1906 if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html",
1909 cupsdCloseClient(con);
1913 cupsdLogRequest(con, HTTP_NOT_FOUND);
1915 else if (!check_if_modified(con, &filestats))
1917 if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
1919 cupsdCloseClient(con);
1923 cupsdLogRequest(con, HTTP_NOT_MODIFIED);
1931 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
1933 strcpy(line, "text/plain");
1935 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1937 if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
1939 cupsdCloseClient(con);
1943 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1944 httpGetDateString(filestats.st_mtime)) < 0)
1946 cupsdCloseClient(con);
1950 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1951 (unsigned long)filestats.st_size) < 0)
1953 cupsdCloseClient(con);
1957 cupsdLogRequest(con, HTTP_OK);
1960 if (httpPrintf(HTTP(con), "\r\n") < 0)
1962 cupsdCloseClient(con);
1966 if (cupsdFlushHeader(con) < 0)
1968 cupsdCloseClient(con);
1972 con->http.state = HTTP_WAITING;
1976 break; /* Anti-compiler-warning-code */
1982 * Handle any incoming data...
1985 switch (con->http.state)
1987 case HTTP_PUT_RECV :
1990 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
1992 if (con->http.error && con->http.error != EPIPE)
1993 cupsdLogMessage(CUPSD_LOG_DEBUG,
1994 "cupsdReadClient: %d PUT_RECV Closing for error "
1995 "%d (%s)", con->http.fd, con->http.error,
1996 strerror(con->http.error));
1998 cupsdLogMessage(CUPSD_LOG_DEBUG,
1999 "cupsdReadClient: %d PUT_RECV Closing on EOF",
2002 cupsdCloseClient(con);
2007 con->bytes += bytes;
2009 if (write(con->file, line, bytes) < bytes)
2011 cupsdLogMessage(CUPSD_LOG_ERROR,
2012 "cupsdReadClient: Unable to write %d bytes to %s: %s",
2013 bytes, con->filename, strerror(errno));
2017 unlink(con->filename);
2018 cupsdClearString(&con->filename);
2020 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
2022 cupsdCloseClient(con);
2028 while (con->http.state == HTTP_PUT_RECV && data_ready(con));
2030 if (con->http.state == HTTP_WAITING)
2033 * End of file, see how big it is...
2036 fstat(con->file, &filestats);
2041 if (filestats.st_size > MaxRequestSize &&
2045 * Request is too big; remove it and send an error...
2048 unlink(con->filename);
2049 cupsdClearString(&con->filename);
2051 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
2053 cupsdCloseClient(con);
2059 * Install the configuration file...
2062 status = install_conf_file(con);
2065 * Return the status to the client...
2068 if (!cupsdSendError(con, status, CUPSD_AUTH_NONE))
2070 cupsdCloseClient(con);
2076 case HTTP_POST_RECV :
2079 if (con->request && con->file < 0)
2082 * Grab any request data from the connection...
2085 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
2087 cupsdLogMessage(CUPSD_LOG_ERROR,
2088 "cupsdReadClient: %d IPP Read Error!",
2091 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
2092 cupsdCloseClient(con);
2095 else if (ipp_state != IPP_DATA)
2097 if (con->http.state == HTTP_POST_SEND)
2099 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
2100 cupsdCloseClient(con);
2108 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %d.%d %s %d",
2109 con->http.fd, con->request->request.op.version[0],
2110 con->request->request.op.version[1],
2111 ippOpString(con->request->request.op.operation_id),
2112 con->request->request.op.request_id);
2113 con->bytes += ippLength(con->request);
2117 if (con->file < 0 && con->http.state != HTTP_POST_SEND)
2120 * Create a file as needed for the request data...
2123 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
2125 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
2129 cupsdLogMessage(CUPSD_LOG_ERROR,
2130 "Unable to create request file %s: %s",
2131 con->filename, strerror(errno));
2133 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
2135 cupsdCloseClient(con);
2140 fchmod(con->file, 0640);
2141 fchown(con->file, RunUser, Group);
2142 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2145 if (con->http.state != HTTP_POST_SEND)
2147 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
2149 if (con->http.error && con->http.error != EPIPE)
2150 cupsdLogMessage(CUPSD_LOG_DEBUG,
2151 "cupsdReadClient: %d POST_SEND Closing for "
2152 "error %d (%s)", con->http.fd, con->http.error,
2153 strerror(con->http.error));
2155 cupsdLogMessage(CUPSD_LOG_DEBUG,
2156 "cupsdReadClient: %d POST_SEND Closing on EOF",
2159 cupsdCloseClient(con);
2164 con->bytes += bytes;
2166 if (write(con->file, line, bytes) < bytes)
2168 cupsdLogMessage(CUPSD_LOG_ERROR,
2169 "cupsdReadClient: Unable to write %d bytes to "
2170 "%s: %s", bytes, con->filename,
2175 unlink(con->filename);
2176 cupsdClearString(&con->filename);
2178 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE,
2181 cupsdCloseClient(con);
2186 else if (con->http.state == HTTP_POST_RECV)
2188 else if (con->http.state != HTTP_POST_SEND)
2190 cupsdLogMessage(CUPSD_LOG_DEBUG,
2191 "cupsdReadClient: %d Closing on unknown HTTP "
2192 "state %d", con->http.fd, con->http.state);
2193 cupsdCloseClient(con);
2198 while (con->http.state == HTTP_POST_RECV && data_ready(con));
2200 if (con->http.state == HTTP_POST_SEND)
2204 fstat(con->file, &filestats);
2209 if (filestats.st_size > MaxRequestSize &&
2213 * Request is too big; remove it and send an error...
2216 unlink(con->filename);
2217 cupsdClearString(&con->filename);
2222 * Delete any IPP request data...
2225 ippDelete(con->request);
2226 con->request = NULL;
2229 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
2231 cupsdCloseClient(con);
2235 else if (filestats.st_size == 0)
2238 * Don't allow empty file...
2241 unlink(con->filename);
2242 cupsdClearString(&con->filename);
2247 if (!cupsdSendCommand(con, con->command, con->options, 0))
2249 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
2251 cupsdCloseClient(con);
2256 cupsdLogRequest(con, HTTP_OK);
2262 cupsdProcessIPPRequest(con);
2266 unlink(con->filename);
2267 cupsdClearString(&con->filename);
2276 break; /* Anti-compiler-warning-code */
2279 if (con->http.state == HTTP_WAITING)
2281 if (!con->http.keep_alive)
2283 cupsdLogMessage(CUPSD_LOG_DEBUG,
2284 "cupsdReadClient: %d Closing because Keep-Alive disabled",
2286 cupsdCloseClient(con);
2290 cupsArrayRemove(ActiveClients, con);
2291 cupsdSetBusyState();
2298 * 'cupsdSendCommand()' - Send output from a command via HTTP.
2301 int /* O - 1 on success, 0 on failure */
2303 cupsd_client_t *con, /* I - Client connection */
2304 char *command, /* I - Command to run */
2305 char *options, /* I - Command-line options */
2306 int root) /* I - Run as root? */
2308 int fd; /* Standard input file descriptor */
2313 fd = open(con->filename, O_RDONLY);
2317 cupsdLogMessage(CUPSD_LOG_ERROR,
2318 "cupsdSendCommand: %d Unable to open \"%s\" for reading: %s",
2319 con->http.fd, con->filename ? con->filename : "/dev/null",
2324 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
2329 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root);
2334 cupsdLogMessage(CUPSD_LOG_INFO, "Started \"%s\" (pid=%d)", command,
2337 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendCommand: %d file=%d",
2338 con->http.fd, con->file);
2340 if (con->pipe_pid == 0)
2343 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2345 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
2347 con->sent_header = 0;
2348 con->file_ready = 0;
2349 con->got_fields = 0;
2350 con->header_used = 0;
2357 * 'cupsdSendError()' - Send an error message via HTTP.
2360 int /* O - 1 if successful, 0 otherwise */
2361 cupsdSendError(cupsd_client_t *con, /* I - Connection */
2362 http_status_t code, /* I - Error code */
2363 int auth_type)/* I - Authentication type */
2365 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2366 "cupsdSendError(con=%p(%d), code=%d, auth_type=%d", con,
2367 con->http.fd, code, auth_type);
2371 * Force client to upgrade for authentication if that is how the
2372 * server is configured...
2375 if (code == HTTP_UNAUTHORIZED &&
2376 DefaultEncryption == HTTP_ENCRYPT_REQUIRED &&
2377 _cups_strcasecmp(con->http.hostname, "localhost") &&
2380 code = HTTP_UPGRADE_REQUIRED;
2382 #endif /* HAVE_SSL */
2385 * Put the request in the access_log file...
2388 cupsdLogRequest(con, code);
2391 * To work around bugs in some proxies, don't use Keep-Alive for some
2394 * Kerberos authentication doesn't work without Keep-Alive, so
2395 * never disable it in that case.
2398 if (code >= HTTP_BAD_REQUEST && con->http.auth_type != CUPSD_AUTH_NEGOTIATE)
2399 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
2402 * Send an error message back to the client. If the error code is a
2403 * 400 or 500 series, make sure the message contains some text, too!
2406 if (!cupsdSendHeader(con, code, NULL, auth_type))
2410 if (code == HTTP_UPGRADE_REQUIRED)
2411 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
2414 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0)
2416 #endif /* HAVE_SSL */
2418 if (con->http.version >= HTTP_1_1 &&
2419 con->http.keep_alive == HTTP_KEEPALIVE_OFF)
2421 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
2425 if (code >= HTTP_BAD_REQUEST)
2428 * Send a human-readable error message.
2431 char message[4096], /* Message for user */
2432 urltext[1024], /* URL redirection text */
2433 redirect[1024]; /* Redirection link */
2434 const char *text; /* Status-specific text */
2439 if (code == HTTP_UNAUTHORIZED)
2440 text = _cupsLangString(con->language,
2441 _("Enter your username and password or the "
2442 "root username and password to access this "
2443 "page. If you are using Kerberos authentication, "
2444 "make sure you have a valid Kerberos ticket."));
2445 else if (code == HTTP_UPGRADE_REQUIRED)
2449 snprintf(urltext, sizeof(urltext),
2450 _cupsLangString(con->language,
2451 _("You must access this page using the URL "
2452 "<A HREF=\"https://%s:%d%s\">"
2453 "https://%s:%d%s</A>.")),
2454 con->servername, con->serverport, con->uri,
2455 con->servername, con->serverport, con->uri);
2457 snprintf(redirect, sizeof(redirect),
2458 "<META HTTP-EQUIV=\"Refresh\" "
2459 "CONTENT=\"3;URL=https://%s:%d%s\">\n",
2460 con->servername, con->serverport, con->uri);
2462 else if (code == HTTP_WEBIF_DISABLED)
2463 text = _cupsLangString(con->language,
2464 _("The web interface is currently disabled. Run "
2465 "\"cupsctl WebInterface=yes\" to enable it."));
2469 snprintf(message, sizeof(message),
2470 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2471 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2474 "\t<META HTTP-EQUIV=\"Content-Type\" "
2475 "CONTENT=\"text/html; charset=utf-8\">\n"
2476 "\t<TITLE>%s - " CUPS_SVERSION "</TITLE>\n"
2477 "\t<LINK REL=\"STYLESHEET\" TYPE=\"text/css\" "
2478 "HREF=\"/cups.css\">\n"
2486 httpStatus(code), redirect, httpStatus(code), text);
2488 if (httpPrintf(HTTP(con), "Content-Type: text/html; charset=utf-8\r\n") < 0)
2490 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
2491 (int)strlen(message)) < 0)
2493 if (httpPrintf(HTTP(con), "\r\n") < 0)
2495 if (httpPrintf(HTTP(con), "%s", message) < 0)
2498 else if (httpPrintf(HTTP(con), "\r\n") < 0)
2501 if (cupsdFlushHeader(con) < 0)
2504 con->http.state = HTTP_WAITING;
2511 * 'cupsdSendHeader()' - Send an HTTP request.
2514 int /* O - 1 on success, 0 on failure */
2516 cupsd_client_t *con, /* I - Client to send to */
2517 http_status_t code, /* I - HTTP status code */
2518 char *type, /* I - MIME type of document */
2519 int auth_type) /* I - Type of authentication */
2521 char auth_str[1024]; /* Authorization string */
2522 #if 0 /* def HAVE_GSSAPI */
2523 static char *gss_buf = NULL; /* Kerberos auth data buffer */
2524 static int gss_bufsize = 0; /* Size of Kerberos auth data buffer */
2525 #endif /* HAVE_GSSAPI */
2529 * Send the HTTP status header...
2532 if (code == HTTP_CONTINUE)
2535 * 100-continue doesn't send any headers...
2538 return (httpPrintf(HTTP(con), "HTTP/%d.%d 100 Continue\r\n\r\n",
2539 con->http.version / 100, con->http.version % 100) > 0);
2541 else if (code == HTTP_WEBIF_DISABLED)
2544 * Treat our special "web interface is disabled" status as "200 OK" for web
2551 httpFlushWrite(HTTP(con));
2553 con->http.data_encoding = HTTP_ENCODE_FIELDS;
2555 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
2556 con->http.version % 100, code, httpStatus(code)) < 0)
2558 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
2561 if (httpPrintf(HTTP(con), "Server: %s\r\n", ServerHeader) < 0)
2563 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
2565 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
2567 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n",
2568 KeepAliveTimeout) < 0)
2571 if (code == HTTP_METHOD_NOT_ALLOWED)
2572 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n") < 0)
2575 if (code == HTTP_UNAUTHORIZED)
2577 if (auth_type == CUPSD_AUTH_NONE)
2579 if (!con->best || con->best->type <= CUPSD_AUTH_NONE)
2580 auth_type = DefaultAuthType;
2582 auth_type = con->best->type;
2587 if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
2588 strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
2589 else if (auth_type == CUPSD_AUTH_DIGEST)
2590 snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"",
2591 con->http.hostname);
2593 else if (auth_type == CUPSD_AUTH_NEGOTIATE)
2596 if (_httpAddrFamily(con->http.hostaddr) == AF_LOCAL)
2597 strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
2599 # endif /* AF_LOCAL */
2600 strlcpy(auth_str, "Negotiate", sizeof(auth_str));
2602 #endif /* HAVE_GSSAPI */
2604 if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE &&
2605 !_cups_strcasecmp(con->http.hostname, "localhost"))
2608 * Add a "trc" (try root certification) parameter for local non-Kerberos
2609 * requests when the request requires system group membership - then the
2610 * client knows the root certificate can/should be used.
2612 * Also, for Mac OS X we also look for @AUTHKEY and add an "authkey"
2613 * parameter as needed...
2616 char *name, /* Current user name */
2617 *auth_key; /* Auth key buffer */
2618 size_t auth_size; /* Size of remaining buffer */
2620 auth_key = auth_str + strlen(auth_str);
2621 auth_size = sizeof(auth_str) - (auth_key - auth_str);
2623 for (name = (char *)cupsArrayFirst(con->best->names);
2625 name = (char *)cupsArrayNext(con->best->names))
2627 #ifdef HAVE_AUTHORIZATION_H
2628 if (!_cups_strncasecmp(name, "@AUTHKEY(", 9))
2630 snprintf(auth_key, auth_size, ", authkey=\"%s\"", name + 9);
2631 /* end parenthesis is stripped in conf.c */
2635 #endif /* HAVE_AUTHORIZATION_H */
2636 if (!_cups_strcasecmp(name, "@SYSTEM"))
2638 #ifdef HAVE_AUTHORIZATION_H
2639 if (SystemGroupAuthKey)
2640 snprintf(auth_key, auth_size,
2642 SystemGroupAuthKey);
2645 strlcpy(auth_key, ", trc=\"y\"", auth_size);
2646 #endif /* HAVE_AUTHORIZATION_H */
2654 cupsdLogMessage(CUPSD_LOG_DEBUG,
2655 "cupsdSendHeader: %d WWW-Authenticate: %s", con->http.fd,
2658 if (httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0)
2663 if (con->language && strcmp(con->language->language, "C"))
2665 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
2666 con->language->language) < 0)
2672 if (!strcmp(type, "text/html"))
2674 if (httpPrintf(HTTP(con),
2675 "Content-Type: text/html; charset=utf-8\r\n") < 0)
2678 else if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
2687 * 'cupsdUpdateCGI()' - Read status messages from CGI scripts and programs.
2691 cupsdUpdateCGI(void)
2693 char *ptr, /* Pointer to end of line in buffer */
2694 message[1024]; /* Pointer to message text */
2695 int loglevel; /* Log level for message */
2698 while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel,
2699 message, sizeof(message))) != NULL)
2701 if (loglevel == CUPSD_LOG_INFO)
2702 cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
2704 if (!strchr(CGIStatusBuffer->buffer, '\n'))
2708 if (ptr == NULL && !CGIStatusBuffer->bufused)
2711 * Fatal error on pipe - should never happen!
2714 cupsdLogMessage(CUPSD_LOG_CRIT,
2715 "cupsdUpdateCGI: error reading from CGI error pipe - %s",
2722 * 'cupsdWriteClient()' - Write data to a client as needed.
2726 cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */
2728 int bytes, /* Number of bytes written */
2729 field_col; /* Current column */
2730 char *bufptr, /* Pointer into buffer */
2731 *bufend; /* Pointer to end of buffer */
2732 ipp_state_t ipp_state; /* IPP state value */
2735 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2736 "cupsdWriteClient(con=%p(%d)) response=%p(%d), file=%d "
2737 "pipe_pid=%d state=%d",
2738 con, con->http.fd, con->response,
2739 con->response ? con->response->state : -1,
2740 con->file, con->pipe_pid, con->http.state);
2742 if (con->http.state != HTTP_GET_SEND &&
2743 con->http.state != HTTP_POST_SEND)
2746 * If we get called in the wrong state, then something went wrong with the
2747 * connection and we need to shut it down...
2750 cupsdLogMessage(CUPSD_LOG_DEBUG,
2751 "cupsdWriteClient: %d Closing on unknown HTTP state %d",
2752 con->http.fd, con->http.state);
2753 cupsdCloseClient(con);
2760 * Make sure we select on the CGI output...
2763 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
2765 if (!con->file_ready)
2768 * Try again later when there is CGI output available...
2771 cupsdRemoveSelect(con->http.fd);
2775 con->file_ready = 0;
2778 if (con->response && con->response->state != IPP_DATA)
2780 ipp_state = ippWrite(HTTP(con), con->response);
2781 bytes = ipp_state != IPP_ERROR &&
2782 (con->file >= 0 || ipp_state != IPP_DATA);
2784 else if ((bytes = read(con->file, con->header + con->header_used,
2785 sizeof(con->header) - con->header_used)) > 0)
2787 con->header_used += bytes;
2789 if (con->pipe_pid && !con->got_fields)
2792 * Inspect the data for Content-Type and other fields.
2795 for (bufptr = con->header, bufend = con->header + con->header_used,
2797 !con->got_fields && bufptr < bufend;
2800 if (*bufptr == '\n')
2803 * Send line to client...
2806 if (bufptr > con->header && bufptr[-1] == '\r')
2810 cupsdLogMessage(CUPSD_LOG_DEBUG, "Script header: %s", con->header);
2812 if (!con->sent_header)
2815 * Handle redirection and CGI status codes...
2818 if (!_cups_strncasecmp(con->header, "Location:", 9))
2820 if (!cupsdSendHeader(con, HTTP_SEE_OTHER, NULL, CUPSD_AUTH_NONE))
2822 cupsdCloseClient(con);
2826 con->sent_header = 2;
2828 if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0)
2831 else if (!_cups_strncasecmp(con->header, "Status:", 7))
2833 cupsdSendError(con, (http_status_t)atoi(con->header + 7),
2835 con->sent_header = 2;
2839 if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE))
2841 cupsdCloseClient(con);
2845 con->sent_header = 1;
2847 if (con->http.version == HTTP_1_1)
2849 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
2855 if (_cups_strncasecmp(con->header, "Status:", 7))
2856 httpPrintf(HTTP(con), "%s\r\n", con->header);
2862 con->header_used -= bufptr - con->header;
2864 if (con->header_used > 0)
2865 memmove(con->header, bufptr, con->header_used);
2867 bufptr = con->header - 1;
2870 * See if the line was empty...
2875 con->got_fields = 1;
2877 if (cupsdFlushHeader(con) < 0)
2879 cupsdCloseClient(con);
2883 if (con->http.version == HTTP_1_1)
2884 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
2889 else if (*bufptr != '\r')
2893 if (!con->got_fields)
2895 con->http.activity = time(NULL);
2900 if (con->header_used > 0)
2902 if (httpWrite2(HTTP(con), con->header, con->header_used) < 0)
2904 cupsdLogMessage(CUPSD_LOG_DEBUG,
2905 "cupsdWriteClient: %d Closing for error %d (%s)",
2906 con->http.fd, con->http.error,
2907 strerror(con->http.error));
2908 cupsdCloseClient(con);
2912 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
2913 httpFlushWrite(HTTP(con));
2915 con->bytes += con->header_used;
2917 if (con->http.state == HTTP_WAITING)
2920 bytes = con->header_used;
2922 con->header_used = 0;
2927 (con->http.state != HTTP_GET_SEND && con->http.state != HTTP_POST_SEND))
2929 if (!con->sent_header && con->pipe_pid)
2930 cupsdSendError(con, HTTP_SERVER_ERROR, CUPSD_AUTH_NONE);
2933 cupsdLogRequest(con, HTTP_OK);
2935 httpFlushWrite(HTTP(con));
2937 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED && con->sent_header == 1)
2939 if (httpWrite2(HTTP(con), "", 0) < 0)
2941 cupsdLogMessage(CUPSD_LOG_DEBUG,
2942 "cupsdWriteClient: %d Closing for error %d (%s)",
2943 con->http.fd, con->http.error,
2944 strerror(con->http.error));
2945 cupsdCloseClient(con);
2951 con->http.state = HTTP_WAITING;
2953 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
2957 cupsdRemoveSelect(con->file);
2960 cupsdEndProcess(con->pipe_pid, 0);
2969 unlink(con->filename);
2970 cupsdClearString(&con->filename);
2975 ippDelete(con->request);
2976 con->request = NULL;
2981 ippDelete(con->response);
2982 con->response = NULL;
2985 cupsdClearString(&con->command);
2986 cupsdClearString(&con->options);
2987 cupsdClearString(&con->query_string);
2989 if (!con->http.keep_alive)
2991 cupsdLogMessage(CUPSD_LOG_DEBUG,
2992 "cupsdWriteClient: %d Closing because Keep-Alive disabled",
2994 cupsdCloseClient(con);
2999 cupsArrayRemove(ActiveClients, con);
3000 cupsdSetBusyState();
3004 con->http.activity = time(NULL);
3009 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
3012 static int /* O - 1 if modified since */
3014 cupsd_client_t *con, /* I - Client connection */
3015 struct stat *filestats) /* I - File information */
3017 char *ptr; /* Pointer into field */
3018 time_t date; /* Time/date value */
3019 off_t size; /* Size/length value */
3024 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
3029 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3030 "check_if_modified(con=%p(%d), "
3031 "filestats=%p(" CUPS_LLFMT ", %d)) If-Modified-Since=\"%s\"",
3032 con, con->http.fd, filestats, CUPS_LLCAST filestats->st_size,
3033 (int)filestats->st_mtime, ptr);
3035 while (*ptr != '\0')
3037 while (isspace(*ptr) || *ptr == ';')
3040 if (_cups_strncasecmp(ptr, "length=", 7) == 0)
3043 size = strtoll(ptr, NULL, 10);
3045 while (isdigit(*ptr))
3048 else if (isalpha(*ptr))
3050 date = httpGetDateTime(ptr);
3051 while (*ptr != '\0' && *ptr != ';')
3058 return ((size != filestats->st_size && size != 0) ||
3059 (date < filestats->st_mtime && date != 0) ||
3060 (size == 0 && date == 0));
3065 * 'compare_clients()' - Compare two client connections.
3068 static int /* O - Result of comparison */
3069 compare_clients(cupsd_client_t *a, /* I - First client */
3070 cupsd_client_t *b, /* I - Second client */
3071 void *data) /* I - User data (not used) */
3086 * 'copy_cdsa_certificate()' - Copy a SSL/TLS certificate from the System
3090 static CFArrayRef /* O - Array of certificates */
3091 copy_cdsa_certificate(
3092 cupsd_client_t *con) /* I - Client connection */
3094 OSStatus err; /* Error info */
3095 SecKeychainRef keychain = NULL;/* Keychain reference */
3096 SecIdentitySearchRef search = NULL; /* Search reference */
3097 SecIdentityRef identity = NULL;/* Identity */
3098 CFArrayRef certificates = NULL;
3099 /* Certificate array */
3100 # if HAVE_SECPOLICYCREATESSL
3101 SecPolicyRef policy = NULL; /* Policy ref */
3102 CFStringRef servername = NULL;
3104 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
3105 char localname[1024];/* Local hostname */
3106 # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3107 SecPolicyRef policy = NULL; /* Policy ref */
3108 SecPolicySearchRef policy_search = NULL;
3109 /* Policy search ref */
3110 CSSM_DATA options; /* Policy options */
3111 CSSM_APPLE_TP_SSL_OPTIONS
3112 ssl_options; /* SSL Option for hostname */
3113 char localname[1024];/* Local hostname */
3114 # endif /* HAVE_SECPOLICYCREATESSL */
3117 cupsdLogMessage(CUPSD_LOG_DEBUG,
3118 "copy_cdsa_certificate: Looking for certs for \"%s\"...",
3121 if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
3123 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
3124 ServerCertificate, cssmErrorString(err), (int)err);
3128 # if HAVE_SECPOLICYCREATESSL
3129 servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
3130 kCFStringEncodingUTF8);
3132 policy = SecPolicyCreateSSL(1, servername);
3135 CFRelease(servername);
3139 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
3143 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3144 &kCFTypeDictionaryKeyCallBacks,
3145 &kCFTypeDictionaryValueCallBacks)))
3147 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary");
3151 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
3152 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
3153 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
3154 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
3156 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
3158 if (err && DNSSDHostName)
3161 * Search for the connection server name failed; try the DNS-SD .local
3162 * hostname instead...
3165 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
3167 cupsdLogMessage(CUPSD_LOG_DEBUG,
3168 "copy_cdsa_certificate: Looking for certs for \"%s\"...",
3171 servername = CFStringCreateWithCString(kCFAllocatorDefault, localname,
3172 kCFStringEncodingUTF8);
3176 policy = SecPolicyCreateSSL(1, servername);
3179 CFRelease(servername);
3183 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
3187 CFDictionarySetValue(query, kSecMatchPolicy, policy);
3189 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
3194 cupsdLogMessage(CUPSD_LOG_DEBUG,
3195 "Cannot find signing key in keychain \"%s\": %s (%d)",
3196 ServerCertificate, cssmErrorString(err), (int)err);
3200 # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3202 * Use a policy to search for valid certificates whose common name matches the
3206 if (SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_SSL,
3207 NULL, &policy_search))
3209 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create a policy search reference");
3213 if (SecPolicySearchCopyNext(policy_search, &policy))
3215 cupsdLogMessage(CUPSD_LOG_ERROR,
3216 "Cannot find a policy to use for searching");
3220 memset(&ssl_options, 0, sizeof(ssl_options));
3221 ssl_options.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
3222 ssl_options.ServerName = con->servername;
3223 ssl_options.ServerNameLen = strlen(con->servername);
3225 options.Data = (uint8 *)&ssl_options;
3226 options.Length = sizeof(ssl_options);
3228 if (SecPolicySetValue(policy, &options))
3230 cupsdLogMessage(CUPSD_LOG_ERROR,
3231 "Cannot set policy value to use for searching");
3235 if ((err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
3236 keychain, FALSE, &search)))
3238 cupsdLogMessage(CUPSD_LOG_ERROR,
3239 "Cannot create identity search reference: %s (%d)",
3240 cssmErrorString(err), (int)err);
3244 err = SecIdentitySearchCopyNext(search, &identity);
3246 if (err && DNSSDHostName)
3249 * Search for the connection server name failed; try the DNS-SD .local
3250 * hostname instead...
3253 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
3255 ssl_options.ServerName = localname;
3256 ssl_options.ServerNameLen = strlen(localname);
3258 cupsdLogMessage(CUPSD_LOG_DEBUG,
3259 "copy_cdsa_certificate: Looking for certs for \"%s\"...",
3262 if (SecPolicySetValue(policy, &options))
3264 cupsdLogMessage(CUPSD_LOG_ERROR,
3265 "Cannot set policy value to use for searching");
3271 if ((err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
3272 keychain, FALSE, &search)))
3274 cupsdLogMessage(CUPSD_LOG_ERROR,
3275 "Cannot create identity search reference: %s (%d)",
3276 cssmErrorString(err), (int)err);
3280 err = SecIdentitySearchCopyNext(search, &identity);
3286 cupsdLogMessage(CUPSD_LOG_DEBUG,
3287 "Cannot find signing key in keychain \"%s\": %s (%d)",
3288 ServerCertificate, cssmErrorString(err), (int)err);
3294 * Assume there is exactly one SecIdentity in the keychain...
3297 if ((err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_SIGN, &search)))
3299 cupsdLogMessage(CUPSD_LOG_DEBUG,
3300 "Cannot create identity search reference (%d)", (int)err);
3304 if ((err = SecIdentitySearchCopyNext(search, &identity)))
3306 cupsdLogMessage(CUPSD_LOG_DEBUG,
3307 "Cannot find signing key in keychain \"%s\": %s (%d)",
3308 ServerCertificate, cssmErrorString(err), (int)err);
3311 # endif /* HAVE_SECPOLICYCREATESSL */
3313 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
3315 cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure!");
3319 if ((certificates = CFArrayCreate(NULL, (const void **)&identity,
3320 1, &kCFTypeArrayCallBacks)) == NULL)
3322 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
3329 CFRelease(keychain);
3333 CFRelease(identity);
3335 # if HAVE_SECPOLICYCREATESSL
3340 # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3344 CFRelease(policy_search);
3345 # endif /* HAVE_SECPOLICYCREATESSL */
3347 return (certificates);
3349 #endif /* HAVE_CDSASSL */
3353 * 'data_ready()' - Check whether data is available from a client.
3356 static int /* O - 1 if data is ready, 0 otherwise */
3357 data_ready(cupsd_client_t *con) /* I - Client */
3359 if (con->http.used > 0)
3362 else if (con->http.tls)
3365 if (SSL_pending((SSL *)(con->http.tls)))
3367 # elif defined(HAVE_GNUTLS)
3368 if (gnutls_record_check_pending(con->http.tls))
3370 # elif defined(HAVE_CDSASSL)
3371 size_t bytes; /* Bytes that are available */
3373 if (!SSLGetBufferedReadSize(con->http.tls, &bytes) && bytes > 0)
3375 # endif /* HAVE_LIBSSL */
3377 #endif /* HAVE_SSL */
3385 * 'encrypt_client()' - Enable encryption for the client...
3388 static int /* O - 1 on success, 0 on error */
3389 encrypt_client(cupsd_client_t *con) /* I - Client to encrypt */
3392 SSL_CTX *context; /* Context for encryption */
3393 BIO *bio; /* BIO data */
3394 unsigned long error; /* Error code */
3397 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3401 * Verify that we have a certificate...
3404 if (access(ServerKey, 0) || access(ServerCertificate, 0))
3407 * Nope, make a self-signed certificate...
3410 if (!make_certificate(con))
3415 * Create the SSL context and accept the connection...
3418 context = SSL_CTX_new(SSLv23_server_method());
3420 SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
3421 if (SSLOptions & CUPSD_SSL_NOEMPTY)
3422 SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
3423 SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
3424 SSL_CTX_use_certificate_chain_file(context, ServerCertificate);
3426 bio = BIO_new(_httpBIOMethods());
3427 BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con));
3429 con->http.tls = SSL_new(context);
3430 SSL_set_bio(con->http.tls, bio, bio);
3432 if (SSL_accept(con->http.tls) != 1)
3434 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to encrypt connection from %s.",
3435 con->http.hostname);
3437 while ((error = ERR_get_error()) != 0)
3438 cupsdLogMessage(CUPSD_LOG_ERROR, "%s", ERR_error_string(error, NULL));
3440 SSL_CTX_free(context);
3441 SSL_free(con->http.tls);
3442 con->http.tls = NULL;
3446 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3447 con->http.hostname);
3451 # elif defined(HAVE_GNUTLS)
3452 int status; /* Error code */
3453 gnutls_certificate_server_credentials *credentials;
3454 /* TLS credentials */
3457 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3461 * Verify that we have a certificate...
3464 if (access(ServerKey, 0) || access(ServerCertificate, 0))
3467 * Nope, make a self-signed certificate...
3470 if (!make_certificate(con))
3475 * Create the SSL object and perform the SSL handshake...
3478 credentials = (gnutls_certificate_server_credentials *)
3479 malloc(sizeof(gnutls_certificate_server_credentials));
3480 if (credentials == NULL)
3482 cupsdLogMessage(CUPSD_LOG_ERROR,
3483 "Unable to encrypt connection from %s - %s",
3484 con->http.hostname, strerror(errno));
3489 gnutls_certificate_allocate_credentials(credentials);
3490 gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
3491 ServerKey, GNUTLS_X509_FMT_PEM);
3493 gnutls_init(&con->http.tls, GNUTLS_SERVER);
3494 gnutls_set_default_priority(con->http.tls);
3496 gnutls_credentials_set(con->http.tls, GNUTLS_CRD_CERTIFICATE, *credentials);
3497 gnutls_transport_set_ptr(con->http.tls, (gnutls_transport_ptr)HTTP(con));
3498 gnutls_transport_set_pull_function(con->http.tls, _httpReadGNUTLS);
3499 gnutls_transport_set_push_function(con->http.tls, _httpWriteGNUTLS);
3501 while ((status = gnutls_handshake(con->http.tls)) != GNUTLS_E_SUCCESS)
3503 if (gnutls_error_is_fatal(status))
3505 cupsdLogMessage(CUPSD_LOG_ERROR,
3506 "Unable to encrypt connection from %s - %s",
3507 con->http.hostname, gnutls_strerror(status));
3509 gnutls_deinit(con->http.tls);
3510 gnutls_certificate_free_credentials(*credentials);
3511 con->http.tls = NULL;
3517 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3518 con->http.hostname);
3520 con->http.tls_credentials = credentials;
3523 # elif defined(HAVE_CDSASSL)
3524 OSStatus error = 0; /* Error code */
3525 CFArrayRef peerCerts; /* Peer certificates */
3528 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3531 con->http.tls_credentials = copy_cdsa_certificate(con);
3533 if (!con->http.tls_credentials)
3536 * No keychain (yet), make a self-signed certificate...
3539 if (make_certificate(con))
3540 con->http.tls_credentials = copy_cdsa_certificate(con);
3543 if (!con->http.tls_credentials)
3545 cupsdLogMessage(CUPSD_LOG_ERROR,
3546 "Could not find signing key in keychain \"%s\"",
3548 error = errSSLBadCert; /* errSSLBadConfiguration is a better choice, but not available on 10.2.x */
3552 error = SSLNewContext(true, &con->http.tls);
3555 error = SSLSetIOFuncs(con->http.tls, _httpReadCDSA, _httpWriteCDSA);
3558 error = SSLSetConnection(con->http.tls, HTTP(con));
3561 error = SSLSetAllowsExpiredCerts(con->http.tls, true);
3564 error = SSLSetAllowsAnyRoot(con->http.tls, true);
3567 error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
3572 * Perform SSL/TLS handshake
3575 while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
3581 cupsdLogMessage(CUPSD_LOG_ERROR,
3582 "Unable to encrypt connection from %s - %s (%d)",
3583 con->http.hostname, cssmErrorString(error), (int)error);
3585 con->http.error = error;
3586 con->http.status = HTTP_ERROR;
3590 SSLDisposeContext(con->http.tls);
3591 con->http.tls = NULL;
3594 if (con->http.tls_credentials)
3596 CFRelease(con->http.tls_credentials);
3597 con->http.tls_credentials = NULL;
3603 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3604 con->http.hostname);
3606 if (!SSLCopyPeerCertificates(con->http.tls, &peerCerts) && peerCerts)
3608 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates!",
3609 (int)CFArrayGetCount(peerCerts));
3610 CFRelease(peerCerts);
3613 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates!");
3617 # endif /* HAVE_LIBSSL */
3619 #endif /* HAVE_SSL */
3623 * 'get_file()' - Get a filename and state info.
3626 static char * /* O - Real filename */
3627 get_file(cupsd_client_t *con, /* I - Client connection */
3628 struct stat *filestats, /* O - File information */
3629 char *filename, /* IO - Filename buffer */
3630 int len) /* I - Buffer length */
3632 int status; /* Status of filesystem calls */
3633 char *ptr; /* Pointer info filename */
3634 int plen; /* Remaining length after pointer */
3635 char language[7]; /* Language subdirectory, if any */
3639 * Figure out the real filename...
3644 if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/'))
3645 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
3646 else if (!strncmp(con->uri, "/icons/", 7) && !strchr(con->uri + 7, '/'))
3648 snprintf(filename, len, "%s/%s", CacheDir, con->uri + 7);
3649 if (access(filename, F_OK) < 0)
3650 snprintf(filename, len, "%s/images/generic.png", DocumentRoot);
3652 else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/'))
3653 snprintf(filename, len, "%s/rss/%s", CacheDir, con->uri + 5);
3654 else if (!strncmp(con->uri, "/admin/conf/", 12))
3655 snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
3656 else if (!strncmp(con->uri, "/admin/log/", 11))
3658 if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/')
3659 strlcpy(filename, AccessLog, len);
3660 else if (!strncmp(con->uri + 11, "error_log", 9) && ErrorLog[0] == '/')
3661 strlcpy(filename, ErrorLog, len);
3662 else if (!strncmp(con->uri + 11, "page_log", 8) && PageLog[0] == '/')
3663 strlcpy(filename, PageLog, len);
3667 else if (con->language)
3669 snprintf(language, sizeof(language), "/%s", con->language->language);
3670 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3673 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
3675 if ((ptr = strchr(filename, '?')) != NULL)
3679 * Grab the status for this language; if there isn't a language-specific file
3680 * then fallback to the default one...
3683 if ((status = stat(filename, filestats)) != 0 && language[0] &&
3684 strncmp(con->uri, "/icons/", 7) &&
3685 strncmp(con->uri, "/ppd/", 5) &&
3686 strncmp(con->uri, "/rss/", 5) &&
3687 strncmp(con->uri, "/admin/conf/", 12) &&
3688 strncmp(con->uri, "/admin/log/", 11))
3691 * Drop the country code...
3695 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3697 if ((ptr = strchr(filename, '?')) != NULL)
3700 if ((status = stat(filename, filestats)) != 0)
3703 * Drop the language prefix and try the root directory...
3707 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
3709 if ((ptr = strchr(filename, '?')) != NULL)
3712 status = stat(filename, filestats);
3717 * If we're found a directory, get the index.html file instead...
3720 if (!status && S_ISDIR(filestats->st_mode))
3723 * Make sure the URI ends with a slash...
3726 if (con->uri[strlen(con->uri) - 1] != '/')
3727 strlcat(con->uri, "/", sizeof(con->uri));
3730 * Find the directory index file, trying every language...
3735 if (status && language[0])
3738 * Try a different language subset...
3742 language[0] = '\0'; /* Strip country code */
3744 language[0] = '\0'; /* Strip language */
3748 * Look for the index file...
3751 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3753 if ((ptr = strchr(filename, '?')) != NULL)
3756 ptr = filename + strlen(filename);
3757 plen = len - (ptr - filename);
3759 strlcpy(ptr, "index.html", plen);
3760 status = stat(filename, filestats);
3765 strlcpy(ptr, "index.class", plen);
3766 status = stat(filename, filestats);
3768 #endif /* HAVE_JAVA */
3773 strlcpy(ptr, "index.pl", plen);
3774 status = stat(filename, filestats);
3776 #endif /* HAVE_PERL */
3781 strlcpy(ptr, "index.php", plen);
3782 status = stat(filename, filestats);
3784 #endif /* HAVE_PHP */
3789 strlcpy(ptr, "index.pyc", plen);
3790 status = stat(filename, filestats);
3795 strlcpy(ptr, "index.py", plen);
3796 status = stat(filename, filestats);
3798 #endif /* HAVE_PYTHON */
3801 while (status && language[0]);
3804 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3805 "get_file(con=%p(%d), filestats=%p, filename=%p, len=%d) = "
3806 "%s", con, con->http.fd, filestats, filename, len,
3807 status ? "(null)" : filename);
3817 * 'install_conf_file()' - Install a configuration file.
3820 static http_status_t /* O - Status */
3821 install_conf_file(cupsd_client_t *con) /* I - Connection */
3823 char filename[1024]; /* Configuration filename */
3824 mode_t mode; /* Permissions */
3825 cups_file_t *in, /* Input file */
3826 *out; /* Output file */
3827 char buffer[16384]; /* Copy buffer */
3828 ssize_t bytes; /* Number of bytes */
3832 * Open the request file...
3835 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
3837 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open request file \"%s\": %s",
3838 con->filename, strerror(errno));
3839 return (HTTP_SERVER_ERROR);
3843 * Open the new config file...
3846 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri + 11);
3847 if (!strcmp(con->uri, "/admin/conf/printers.conf"))
3848 mode = ConfigFilePerm & 0600;
3850 mode = ConfigFilePerm;
3852 if ((out = cupsdCreateConfFile(filename, mode)) == NULL)
3855 return (HTTP_SERVER_ERROR);
3858 cupsdLogMessage(CUPSD_LOG_INFO, "Installing config file \"%s\"...", filename);
3861 * Copy from the request to the new config file...
3864 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
3865 if (cupsFileWrite(out, buffer, bytes) < bytes)
3867 cupsdLogMessage(CUPSD_LOG_ERROR,
3868 "Unable to copy to config file \"%s\": %s",
3869 filename, strerror(errno));
3874 snprintf(filename, sizeof(filename), "%s%s.N", ServerRoot, con->uri + 11);
3875 cupsdRemoveFile(filename);
3877 return (HTTP_SERVER_ERROR);
3881 * Close the files...
3886 if (cupsdCloseCreatedConfFile(out, filename))
3887 return (HTTP_SERVER_ERROR);
3890 * Remove the request file...
3893 cupsdRemoveFile(con->filename);
3894 cupsdClearString(&con->filename);
3897 * If the cupsd.conf file was updated, set the NeedReload flag...
3900 if (!strcmp(con->uri, "/admin/conf/cupsd.conf"))
3901 NeedReload = RELOAD_CUPSD;
3903 NeedReload = RELOAD_ALL;
3905 ReloadTime = time(NULL);
3908 * Return that the file was created successfully...
3911 return (HTTP_CREATED);
3916 * 'is_cgi()' - Is the resource a CGI script/program?
3919 static int /* O - 1 = CGI, 0 = file */
3920 is_cgi(cupsd_client_t *con, /* I - Client connection */
3921 const char *filename, /* I - Real filename */
3922 struct stat *filestats, /* I - File information */
3923 mime_type_t *type) /* I - MIME type */
3925 const char *options; /* Options on URL */
3929 * Get the options, if any...
3932 if ((options = strchr(con->uri, '?')) != NULL)
3935 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", options);
3939 * Check for known types...
3942 if (!type || _cups_strcasecmp(type->super, "application"))
3944 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3945 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
3946 "type=%s/%s) = 0", con, con->http.fd, filename, filestats,
3947 type ? type->super : "unknown",
3948 type ? type->type : "unknown");
3952 if (!_cups_strcasecmp(type->type, "x-httpd-cgi") &&
3953 (filestats->st_mode & 0111))
3956 * "application/x-httpd-cgi" is a CGI script.
3959 cupsdSetString(&con->command, filename);
3962 cupsdSetStringf(&con->options, " %s", options);
3964 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3965 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
3966 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
3967 type->super, type->type);
3971 else if (!_cups_strcasecmp(type->type, "x-httpd-java"))
3974 * "application/x-httpd-java" is a Java servlet.
3977 cupsdSetString(&con->command, CUPS_JAVA);
3980 cupsdSetStringf(&con->options, " %s %s", filename, options);
3982 cupsdSetStringf(&con->options, " %s", filename);
3984 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3985 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
3986 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
3987 type->super, type->type);
3990 #endif /* HAVE_JAVA */
3992 else if (!_cups_strcasecmp(type->type, "x-httpd-perl"))
3995 * "application/x-httpd-perl" is a Perl page.
3998 cupsdSetString(&con->command, CUPS_PERL);
4001 cupsdSetStringf(&con->options, " %s %s", filename, options);
4003 cupsdSetStringf(&con->options, " %s", filename);
4005 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4006 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4007 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4008 type->super, type->type);
4011 #endif /* HAVE_PERL */
4013 else if (!_cups_strcasecmp(type->type, "x-httpd-php"))
4016 * "application/x-httpd-php" is a PHP page.
4019 cupsdSetString(&con->command, CUPS_PHP);
4022 cupsdSetStringf(&con->options, " %s %s", filename, options);
4024 cupsdSetStringf(&con->options, " %s", filename);
4026 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4027 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4028 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4029 type->super, type->type);
4032 #endif /* HAVE_PHP */
4034 else if (!_cups_strcasecmp(type->type, "x-httpd-python"))
4037 * "application/x-httpd-python" is a Python page.
4040 cupsdSetString(&con->command, CUPS_PYTHON);
4043 cupsdSetStringf(&con->options, " %s %s", filename, options);
4045 cupsdSetStringf(&con->options, " %s", filename);
4047 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4048 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4049 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4050 type->super, type->type);
4053 #endif /* HAVE_PYTHON */
4055 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4056 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4057 "type=%s/%s) = 0", con, con->http.fd, filename, filestats,
4058 type->super, type->type);
4064 * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. "..").
4067 static int /* O - 0 if relative, 1 if absolute */
4068 is_path_absolute(const char *path) /* I - Input path */
4071 * Check for a leading slash...
4078 * Check for "/.." in the path...
4081 while ((path = strstr(path, "/..")) != NULL)
4083 if (!path[3] || path[3] == '/')
4090 * If we haven't found any relative paths, return 1 indicating an
4100 * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
4103 static int /* O - 1 on success, 0 on failure */
4104 make_certificate(cupsd_client_t *con) /* I - Client connection */
4106 #if defined(HAVE_LIBSSL) && defined(HAVE_WAITPID)
4107 int pid, /* Process ID of command */
4108 status; /* Status of command */
4109 char command[1024], /* Command */
4110 *argv[12], /* Command-line arguments */
4111 *envp[MAX_ENV + 1], /* Environment variables */
4112 infofile[1024], /* Type-in information for cert */
4113 seedfile[1024]; /* Random number seed file */
4114 int envc, /* Number of environment variables */
4115 bytes; /* Bytes written */
4116 cups_file_t *fp; /* Seed/info file */
4117 int infofd; /* Info file descriptor */
4121 * Run the "openssl" command to seed the random number generator and
4122 * generate a self-signed certificate that is good for 10 years:
4124 * openssl rand -rand seedfile 1
4126 * openssl req -new -x509 -keyout ServerKey \
4127 * -out ServerCertificate -days 3650 -nodes
4129 * The seeding step is crucial in ensuring that the openssl command
4130 * does not block on systems without sufficient entropy...
4133 if (!cupsFileFind("openssl", getenv("PATH"), 1, command, sizeof(command)))
4135 cupsdLogMessage(CUPSD_LOG_ERROR,
4136 "No SSL certificate and openssl command not found!");
4140 if (access("/dev/urandom", 0))
4143 * If the system doesn't provide /dev/urandom, then any random source
4144 * will probably be blocking-style, so generate some random data to
4145 * use as a seed for the certificate. Note that we have already
4146 * seeded the random number generator in cupsdInitCerts()...
4149 cupsdLogMessage(CUPSD_LOG_INFO,
4150 "Seeding the random number generator...");
4153 * Write the seed file...
4156 if ((fp = cupsTempFile2(seedfile, sizeof(seedfile))) == NULL)
4158 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create seed file %s - %s",
4159 seedfile, strerror(errno));
4163 for (bytes = 0; bytes < 262144; bytes ++)
4164 cupsFilePutChar(fp, random());
4169 * Run the openssl command to seed its random number generator...
4172 argv[0] = "openssl";
4179 envc = cupsdLoadEnv(envp, MAX_ENV);
4182 if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, NULL,
4189 while (waitpid(pid, &status, 0) < 0)
4196 cupsdFinishProcess(pid, command, sizeof(command), NULL);
4199 * Remove the seed file, as it is no longer needed...
4206 if (WIFEXITED(status))
4207 cupsdLogMessage(CUPSD_LOG_ERROR,
4208 "Unable to seed random number generator - "
4209 "the openssl command stopped with status %d!",
4210 WEXITSTATUS(status));
4212 cupsdLogMessage(CUPSD_LOG_ERROR,
4213 "Unable to seed random number generator - "
4214 "the openssl command crashed on signal %d!",
4222 * Create a file with the certificate information fields...
4224 * Note: This assumes that the default questions are asked by the openssl
4228 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
4230 cupsdLogMessage(CUPSD_LOG_ERROR,
4231 "Unable to create certificate information file %s - %s",
4232 infofile, strerror(errno));
4236 cupsFilePrintf(fp, ".\n.\n.\n%s\n.\n%s\n%s\n",
4237 ServerName, ServerName, ServerAdmin);
4240 cupsdLogMessage(CUPSD_LOG_INFO,
4241 "Generating SSL server key and certificate...");
4243 argv[0] = "openssl";
4247 argv[4] = "-keyout";
4248 argv[5] = ServerKey;
4250 argv[7] = ServerCertificate;
4253 argv[10] = "-nodes";
4256 cupsdLoadEnv(envp, MAX_ENV);
4258 infofd = open(infofile, O_RDONLY);
4260 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
4271 while (waitpid(pid, &status, 0) < 0)
4278 cupsdFinishProcess(pid, command, sizeof(command), NULL);
4282 if (WIFEXITED(status))
4283 cupsdLogMessage(CUPSD_LOG_ERROR,
4284 "Unable to create SSL server key and certificate - "
4285 "the openssl command stopped with status %d!",
4286 WEXITSTATUS(status));
4288 cupsdLogMessage(CUPSD_LOG_ERROR,
4289 "Unable to create SSL server key and certificate - "
4290 "the openssl command crashed on signal %d!",
4295 cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
4297 cupsdLogMessage(CUPSD_LOG_INFO,
4298 "Created SSL server certificate file \"%s\"...",
4304 #elif defined(HAVE_GNUTLS)
4305 gnutls_x509_crt crt; /* Self-signed certificate */
4306 gnutls_x509_privkey key; /* Encryption key */
4307 cups_lang_t *language; /* Default language info */
4308 cups_file_t *fp; /* Key/cert file */
4309 unsigned char buffer[8192]; /* Buffer for x509 data */
4310 size_t bytes; /* Number of bytes of data */
4311 unsigned char serial[4]; /* Serial number buffer */
4312 time_t curtime; /* Current time */
4313 int result; /* Result of GNU TLS calls */
4317 * Create the encryption key...
4320 cupsdLogMessage(CUPSD_LOG_INFO, "Generating SSL server key...");
4322 gnutls_x509_privkey_init(&key);
4323 gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
4329 bytes = sizeof(buffer);
4331 if ((result = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM,
4332 buffer, &bytes)) < 0)
4334 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to export SSL server key - %s",
4335 gnutls_strerror(result));
4336 gnutls_x509_privkey_deinit(key);
4339 else if ((fp = cupsFileOpen(ServerKey, "w")) != NULL)
4341 cupsFileWrite(fp, (char *)buffer, bytes);
4344 cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
4349 cupsdLogMessage(CUPSD_LOG_ERROR,
4350 "Unable to create SSL server key file \"%s\" - %s",
4351 ServerKey, strerror(errno));
4352 gnutls_x509_privkey_deinit(key);
4357 * Create the self-signed certificate...
4360 cupsdLogMessage(CUPSD_LOG_INFO, "Generating self-signed SSL certificate...");
4362 language = cupsLangDefault();
4363 curtime = time(NULL);
4364 serial[0] = curtime >> 24;
4365 serial[1] = curtime >> 16;
4366 serial[2] = curtime >> 8;
4367 serial[3] = curtime;
4369 gnutls_x509_crt_init(&crt);
4370 if (strlen(language->language) == 5)
4371 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
4372 language->language + 3, 2);
4374 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
4376 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0,
4377 ServerName, strlen(ServerName));
4378 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
4379 ServerName, strlen(ServerName));
4380 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
4382 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0,
4384 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0,
4386 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0,
4387 ServerAdmin, strlen(ServerAdmin));
4388 gnutls_x509_crt_set_key(crt, key);
4389 gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
4390 gnutls_x509_crt_set_activation_time(crt, curtime);
4391 gnutls_x509_crt_set_expiration_time(crt, curtime + 10 * 365 * 86400);
4392 gnutls_x509_crt_set_ca_status(crt, 0);
4393 gnutls_x509_crt_set_subject_alternative_name(crt, GNUTLS_SAN_DNSNAME,
4395 gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
4396 gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_KEY_ENCIPHERMENT);
4397 gnutls_x509_crt_set_version(crt, 3);
4399 bytes = sizeof(buffer);
4400 if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0)
4401 gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes);
4403 gnutls_x509_crt_sign(crt, crt, key);
4409 bytes = sizeof(buffer);
4410 if ((result = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM,
4411 buffer, &bytes)) < 0)
4412 cupsdLogMessage(CUPSD_LOG_ERROR,
4413 "Unable to export SSL server certificate - %s",
4414 gnutls_strerror(result));
4415 else if ((fp = cupsFileOpen(ServerCertificate, "w")) != NULL)
4417 cupsFileWrite(fp, (char *)buffer, bytes);
4420 cupsdLogMessage(CUPSD_LOG_INFO,
4421 "Created SSL server certificate file \"%s\"...",
4425 cupsdLogMessage(CUPSD_LOG_ERROR,
4426 "Unable to create SSL server certificate file \"%s\" - %s",
4427 ServerCertificate, strerror(errno));
4433 gnutls_x509_crt_deinit(crt);
4434 gnutls_x509_privkey_deinit(key);
4438 #elif defined(HAVE_CDSASSL) && defined(HAVE_WAITPID)
4439 int pid, /* Process ID of command */
4440 status; /* Status of command */
4441 char command[1024], /* Command */
4442 *argv[4], /* Command-line arguments */
4443 *envp[MAX_ENV + 1], /* Environment variables */
4444 keychain[1024], /* Keychain argument */
4445 infofile[1024], /* Type-in information for cert */
4446 localname[1024], /* Local hostname */
4447 *servername; /* Name of server in cert */
4448 cups_file_t *fp; /* Seed/info file */
4449 int infofd; /* Info file descriptor */
4452 if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
4454 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
4455 servername = localname;
4458 servername = con->servername;
4461 * Run the "certtool" command to generate a self-signed certificate...
4464 if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
4466 cupsdLogMessage(CUPSD_LOG_ERROR,
4467 "No SSL certificate and certtool command not found!");
4472 * Create a file with the certificate information fields...
4474 * Note: This assumes that the default questions are asked by the certtool
4478 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
4480 cupsdLogMessage(CUPSD_LOG_ERROR,
4481 "Unable to create certificate information file %s - %s",
4482 infofile, strerror(errno));
4487 "%s\n" /* Enter key and certificate label */
4488 "r\n" /* Generate RSA key pair */
4489 "2048\n" /* Key size in bits */
4490 "y\n" /* OK (y = yes) */
4491 "b\n" /* Usage (b=signing/encryption) */
4492 "s\n" /* Sign with SHA1 */
4493 "y\n" /* OK (y = yes) */
4494 "%s\n" /* Common name */
4495 "\n" /* Country (default) */
4496 "\n" /* Organization (default) */
4497 "\n" /* Organizational unit (default) */
4498 "\n" /* State/Province (default) */
4499 "%s\n" /* Email address */
4500 "y\n", /* OK (y = yes) */
4501 servername, servername, ServerAdmin);
4504 cupsdLogMessage(CUPSD_LOG_INFO,
4505 "Generating SSL server key and certificate...");
4507 snprintf(keychain, sizeof(keychain), "k=%s", ServerCertificate);
4509 argv[0] = "certtool";
4514 cupsdLoadEnv(envp, MAX_ENV);
4516 infofd = open(infofile, O_RDONLY);
4518 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
4529 while (waitpid(pid, &status, 0) < 0)
4536 cupsdFinishProcess(pid, command, sizeof(command), NULL);
4540 if (WIFEXITED(status))
4541 cupsdLogMessage(CUPSD_LOG_ERROR,
4542 "Unable to create SSL server key and certificate - "
4543 "the certtool command stopped with status %d!",
4544 WEXITSTATUS(status));
4546 cupsdLogMessage(CUPSD_LOG_ERROR,
4547 "Unable to create SSL server key and certificate - "
4548 "the certtool command crashed on signal %d!",
4553 cupsdLogMessage(CUPSD_LOG_INFO,
4554 "Created SSL server certificate file \"%s\"...",
4562 #endif /* HAVE_LIBSSL && HAVE_WAITPID */
4564 #endif /* HAVE_SSL */
4568 * 'pipe_command()' - Pipe the output of a command to the remote client.
4571 static int /* O - Process ID */
4572 pipe_command(cupsd_client_t *con, /* I - Client connection */
4573 int infile, /* I - Standard input for command */
4574 int *outfile, /* O - Standard output for command */
4575 char *command, /* I - Command to run */
4576 char *options, /* I - Options for command */
4577 int root) /* I - Run as root? */
4579 int i; /* Looping var */
4580 int pid; /* Process ID */
4581 char *commptr, /* Command string pointer */
4582 commch; /* Command string character */
4583 char *uriptr; /* URI string pointer */
4584 int fds[2]; /* Pipe FDs */
4585 int argc; /* Number of arguments */
4586 int envc; /* Number of environment variables */
4587 char argbuf[10240], /* Argument buffer */
4588 *argv[100], /* Argument strings */
4589 *envp[MAX_ENV + 20]; /* Environment variables */
4590 char auth_type[256], /* AUTH_TYPE environment variable */
4591 content_length[1024], /* CONTENT_LENGTH environment variable */
4592 content_type[1024], /* CONTENT_TYPE environment variable */
4593 http_cookie[32768], /* HTTP_COOKIE environment variable */
4594 http_referer[1024], /* HTTP_REFERER environment variable */
4595 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
4596 lang[1024], /* LANG environment variable */
4597 path_info[1024], /* PATH_INFO environment variable */
4598 remote_addr[1024], /* REMOTE_ADDR environment variable */
4599 remote_host[1024], /* REMOTE_HOST environment variable */
4600 remote_user[1024], /* REMOTE_USER environment variable */
4601 script_filename[1024], /* SCRIPT_FILENAME environment variable */
4602 script_name[1024], /* SCRIPT_NAME environment variable */
4603 server_name[1024], /* SERVER_NAME environment variable */
4604 server_port[1024]; /* SERVER_PORT environment variable */
4605 ipp_attribute_t *attr; /* attributes-natural-language attribute */
4606 void *ccache = NULL; /* Kerberos credentials */
4610 * Parse a copy of the options string, which is of the form:
4612 * argument+argument+argument
4613 * ?argument+argument+argument
4614 * param=value¶m=value
4615 * ?param=value¶m=value
4616 * /name?argument+argument+argument
4617 * /name?param=value¶m=value
4619 * If the string contains an "=" character after the initial name,
4620 * then we treat it as a HTTP GET form request and make a copy of
4621 * the remaining string for the environment variable.
4623 * The string is always parsed out as command-line arguments, to
4624 * be consistent with Apache...
4627 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4628 "pipe_command(con=%p(%d), infile=%d, outfile=%p, "
4629 "command=\"%s\", options=\"%s\", root=%d)",
4630 con, con->http.fd, infile, outfile, command,
4631 options ? options : "(null)", root);
4638 if (*commptr == ' ')
4640 strlcpy(argbuf, commptr, sizeof(argbuf));
4645 if (argbuf[0] == '/')
4648 * Found some trailing path information, set PATH_INFO...
4651 if ((commptr = strchr(argbuf, '?')) == NULL)
4652 commptr = argbuf + strlen(argbuf);
4656 snprintf(path_info, sizeof(path_info), "PATH_INFO=%s", argbuf);
4662 path_info[0] = '\0';
4664 if (*commptr == ' ')
4668 if (*commptr == '?' && con->operation == HTTP_GET && !con->query_string)
4671 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", commptr);
4678 argv[argc ++] = commptr;
4680 for (; *commptr && argc < 99; commptr ++)
4683 * Break arguments whenever we see a + or space...
4686 if (*commptr == ' ' || *commptr == '+')
4688 while (*commptr == ' ' || *commptr == '+')
4692 * If we don't have a blank string, save it as another argument...
4697 argv[argc] = commptr;
4703 else if (*commptr == '%' && isxdigit(commptr[1] & 255) &&
4704 isxdigit(commptr[2] & 255))
4707 * Convert the %xx notation to the individual character.
4710 if (commptr[1] >= '0' && commptr[1] <= '9')
4711 *commptr = (commptr[1] - '0') << 4;
4713 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
4715 if (commptr[2] >= '0' && commptr[2] <= '9')
4716 *commptr |= commptr[2] - '0';
4718 *commptr |= tolower(commptr[2]) - 'a' + 10;
4720 _cups_strcpy(commptr + 1, commptr + 3);
4723 * Check for a %00 and break if that is the case...
4735 * Setup the environment variables as needed...
4738 if (con->username[0])
4740 snprintf(auth_type, sizeof(auth_type), "AUTH_TYPE=%s",
4741 httpGetField(HTTP(con), HTTP_FIELD_AUTHORIZATION));
4743 if ((uriptr = strchr(auth_type + 10, ' ')) != NULL)
4747 auth_type[0] = '\0';
4750 (attr = ippFindAttribute(con->request, "attributes-natural-language",
4751 IPP_TAG_LANGUAGE)) != NULL)
4753 switch (strlen(attr->values[0].string.text))
4757 * This is an unknown or badly formatted language code; use
4758 * the POSIX locale...
4761 strcpy(lang, "LANG=C");
4766 * Just the language code (ll)...
4769 snprintf(lang, sizeof(lang), "LANG=%s.UTF8",
4770 attr->values[0].string.text);
4775 * Language and country code (ll-cc)...
4778 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF8",
4779 attr->values[0].string.text[0],
4780 attr->values[0].string.text[1],
4781 toupper(attr->values[0].string.text[3] & 255),
4782 toupper(attr->values[0].string.text[4] & 255));
4786 else if (con->language)
4787 snprintf(lang, sizeof(lang), "LANG=%s.UTF8", con->language->language);
4789 strcpy(lang, "LANG=C");
4791 strcpy(remote_addr, "REMOTE_ADDR=");
4792 httpAddrString(con->http.hostaddr, remote_addr + 12,
4793 sizeof(remote_addr) - 12);
4795 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s",
4796 con->http.hostname);
4798 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
4799 if ((uriptr = strchr(script_name, '?')) != NULL)
4802 snprintf(script_filename, sizeof(script_filename), "SCRIPT_FILENAME=%s%s",
4803 DocumentRoot, script_name + 12);
4805 sprintf(server_port, "SERVER_PORT=%d", con->serverport);
4807 if (con->http.fields[HTTP_FIELD_HOST][0])
4809 char *nameptr; /* Pointer to ":port" */
4811 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
4812 con->http.fields[HTTP_FIELD_HOST]);
4813 if ((nameptr = strrchr(server_name, ':')) != NULL && !strchr(nameptr, ']'))
4814 *nameptr = '\0'; /* Strip trailing ":port" */
4817 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
4820 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
4823 envp[envc ++] = auth_type;
4825 envp[envc ++] = lang;
4826 envp[envc ++] = "REDIRECT_STATUS=1";
4827 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
4828 envp[envc ++] = server_name;
4829 envp[envc ++] = server_port;
4830 envp[envc ++] = remote_addr;
4831 envp[envc ++] = remote_host;
4832 envp[envc ++] = script_name;
4833 envp[envc ++] = script_filename;
4836 envp[envc ++] = path_info;
4838 if (con->username[0])
4840 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
4842 envp[envc ++] = remote_user;
4845 if (con->http.version == HTTP_1_1)
4846 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
4847 else if (con->http.version == HTTP_1_0)
4848 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
4850 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
4852 if (con->http.cookie)
4854 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
4856 envp[envc ++] = http_cookie;
4859 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
4861 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
4862 con->http.fields[HTTP_FIELD_USER_AGENT]);
4863 envp[envc ++] = http_user_agent;
4866 if (con->http.fields[HTTP_FIELD_REFERER][0])
4868 snprintf(http_referer, sizeof(http_referer), "HTTP_REFERER=%s",
4869 con->http.fields[HTTP_FIELD_REFERER]);
4870 envp[envc ++] = http_referer;
4873 if (con->operation == HTTP_GET)
4875 envp[envc ++] = "REQUEST_METHOD=GET";
4877 if (con->query_string)
4880 * Add GET form variables after ?...
4883 envp[envc ++] = con->query_string;
4886 envp[envc ++] = "QUERY_STRING=";
4890 sprintf(content_length, "CONTENT_LENGTH=" CUPS_LLFMT,
4891 CUPS_LLCAST con->bytes);
4892 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
4893 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
4895 envp[envc ++] = "REQUEST_METHOD=POST";
4896 envp[envc ++] = content_length;
4897 envp[envc ++] = content_type;
4901 * Tell the CGI if we are using encryption...
4905 envp[envc ++] = "HTTPS=ON";
4908 * Terminate the environment array...
4913 if (LogLevel >= CUPSD_LOG_DEBUG)
4915 for (i = 0; i < argc; i ++)
4916 cupsdLogMessage(CUPSD_LOG_DEBUG,
4917 "[CGI] argv[%d] = \"%s\"", i, argv[i]);
4918 for (i = 0; i < envc; i ++)
4919 cupsdLogMessage(CUPSD_LOG_DEBUG,
4920 "[CGI] envp[%d] = \"%s\"", i, envp[i]);
4924 * Create a pipe for the output...
4927 if (cupsdOpenPipe(fds))
4929 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to create pipe for %s - %s",
4930 argv[0], strerror(errno));
4935 * Then execute the command...
4938 if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1],
4939 -1, -1, root, DefaultProfile, NULL, &pid) < 0)
4942 * Error - can't fork!
4945 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to start %s - %s", argv[0],
4948 cupsdClosePipe(fds);
4954 * Fork successful - return the PID...
4957 if (con->username[0])
4958 cupsdAddCert(pid, con->username, ccache);
4960 cupsdLogMessage(CUPSD_LOG_DEBUG, "[CGI] Started %s (PID %d)", command, pid);
4971 * 'valid_host()' - Is the Host: field valid?
4974 static int /* O - 1 if valid, 0 if not */
4975 valid_host(cupsd_client_t *con) /* I - Client connection */
4977 cupsd_alias_t *a; /* Current alias */
4978 cupsd_netif_t *netif; /* Current network interface */
4979 const char *host, /* Host field */
4980 *end; /* End character */
4983 host = con->http.fields[HTTP_FIELD_HOST];
4985 if (httpAddrLocalhost(con->http.hostaddr))
4988 * Only allow "localhost" or the equivalent IPv4 or IPv6 numerical
4989 * addresses when accessing CUPS via the loopback interface...
4992 return (!_cups_strcasecmp(host, "localhost") ||
4993 !_cups_strncasecmp(host, "localhost:", 10) ||
4994 !_cups_strcasecmp(host, "localhost.") ||
4995 !_cups_strncasecmp(host, "localhost.:", 11) ||
4997 !_cups_strcasecmp(host, "localhost.localdomain") ||
4998 !_cups_strncasecmp(host, "localhost.localdomain:", 22) ||
4999 #endif /* __linux */
5000 !strcmp(host, "127.0.0.1") ||
5001 !strncmp(host, "127.0.0.1:", 10) ||
5002 !strcmp(host, "[::1]") ||
5003 !strncmp(host, "[::1]:", 6));
5006 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
5008 * Check if the hostname is something.local (Bonjour); if so, allow it.
5011 if ((end = strrchr(host, '.')) != NULL &&
5012 (!_cups_strcasecmp(end, ".local") || !_cups_strncasecmp(end, ".local:", 7) ||
5013 !_cups_strcasecmp(end, ".local.") || !_cups_strncasecmp(end, ".local.:", 8)))
5015 #endif /* defined(HAVE_DNSSD) || defined(HAVE_AVAHI) */
5018 * Check if the hostname is an IP address...
5021 if (isdigit(*host & 255) || *host == '[')
5024 * Possible IPv4/IPv6 address...
5027 char temp[1024], /* Temporary string */
5028 *ptr; /* Pointer into temporary string */
5029 http_addrlist_t *addrlist; /* List of addresses */
5032 strlcpy(temp, host, sizeof(temp));
5033 if ((ptr = strrchr(temp, ':')) != NULL && !strchr(ptr, ']'))
5034 *ptr = '\0'; /* Strip :port from host value */
5036 if ((addrlist = httpAddrGetList(temp, AF_UNSPEC, NULL)) != NULL)
5039 * Good IPv4/IPv6 address...
5042 httpAddrFreeList(addrlist);
5048 * Check for (alias) name matches...
5051 for (a = (cupsd_alias_t *)cupsArrayFirst(ServerAlias);
5053 a = (cupsd_alias_t *)cupsArrayNext(ServerAlias))
5056 * "ServerAlias *" allows all host values through...
5059 if (!strcmp(a->name, "*"))
5062 if (!_cups_strncasecmp(host, a->name, a->namelen))
5065 * Prefix matches; check the character at the end - it must be ":", ".",
5069 end = host + a->namelen;
5071 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5077 for (a = (cupsd_alias_t *)cupsArrayFirst(DNSSDAlias);
5079 a = (cupsd_alias_t *)cupsArrayNext(DNSSDAlias))
5082 * "ServerAlias *" allows all host values through...
5085 if (!strcmp(a->name, "*"))
5088 if (!_cups_strncasecmp(host, a->name, a->namelen))
5091 * Prefix matches; check the character at the end - it must be ":", ".",
5095 end = host + a->namelen;
5097 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5101 #endif /* HAVE_DNSSD */
5104 * Check for interface hostname matches...
5107 for (netif = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
5109 netif = (cupsd_netif_t *)cupsArrayNext(NetIFList))
5111 if (!_cups_strncasecmp(host, netif->hostname, netif->hostlen))
5114 * Prefix matches; check the character at the end - it must be ":", ".",
5118 end = host + netif->hostlen;
5120 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5130 * 'write_file()' - Send a file via HTTP.
5133 static int /* O - 0 on failure, 1 on success */
5134 write_file(cupsd_client_t *con, /* I - Client connection */
5135 http_status_t code, /* I - HTTP status */
5136 char *filename, /* I - Filename */
5137 char *type, /* I - File type */
5138 struct stat *filestats) /* O - File information */
5140 con->file = open(filename, O_RDONLY);
5142 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5143 "write_file(con=%p(%d), code=%d, filename=\"%s\" (%d), "
5144 "type=\"%s\", filestats=%p)", con, con->http.fd,
5145 code, filename, con->file, type ? type : "(null)", filestats);
5150 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
5154 if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
5157 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
5158 httpGetDateString(filestats->st_mtime)) < 0)
5160 if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n",
5161 CUPS_LLCAST filestats->st_size) < 0)
5163 if (httpPrintf(HTTP(con), "\r\n") < 0)
5166 if (cupsdFlushHeader(con) < 0)
5169 con->http.data_encoding = HTTP_ENCODE_LENGTH;
5170 con->http.data_remaining = filestats->st_size;
5172 if (con->http.data_remaining <= INT_MAX)
5173 con->http._data_remaining = con->http.data_remaining;
5175 con->http._data_remaining = INT_MAX;
5177 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
5178 (cupsd_selfunc_t)cupsdWriteClient, con);
5185 * 'write_pipe()' - Flag that data is available on the CGI pipe.
5189 write_pipe(cupsd_client_t *con) /* I - Client connection */
5191 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5192 "write_pipe(con=%p(%d)) CGI output on fd %d",
5193 con, con->http.fd, con->file);
5195 con->file_ready = 1;
5197 cupsdRemoveSelect(con->file);
5198 cupsdAddSelect(con->http.fd, NULL, (cupsd_selfunc_t)cupsdWriteClient, con);
5203 * End of "$Id: client.c 10338 2012-03-07 06:05:39Z mike $".