/*
- * "$Id: client.c,v 1.91.2.65 2003/07/20 12:51:43 mike Exp $"
+ * "$Id$"
*
* Client routines for the Common UNIX Printing System (CUPS) scheduler.
*
- * Copyright 1997-2003 by Easy Software Products, all rights reserved.
+ * Copyright 1997-2004 by Easy Software Products, all rights reserved.
*
* These coded instructions, statements, and computer programs are the
* property of Easy Software Products and are protected by Federal
* Attn: CUPS Licensing Information
* Easy Software Products
* 44141 Airport View Drive, Suite 204
- * Hollywood, Maryland 20636-3111 USA
+ * Hollywood, Maryland 20636 USA
*
- * Voice: (301) 373-9603
+ * Voice: (301) 373-9600
* EMail: cups-info@cups.org
* WWW: http://www.cups.org
*
* SendError() - Send an error message via HTTP.
* SendFile() - Send a file via HTTP.
* SendHeader() - Send an HTTP request.
- * ShutdownClient() - Shutdown the receiving end of a connection.
* UpdateCGI() - Read status messages from CGI scripts and programs.
* WriteClient() - Write data to a client as needed.
* check_if_modified() - Decode an "If-Modified-Since" line.
* decode_auth() - Decode an authorization string.
* get_file() - Get a filename and state info.
* install_conf_file() - Install a configuration file.
+ * is_path_absolute() - Is a path absolute and free of relative elements.
* pipe_command() - Pipe the output of a command to the remote client.
* CDSAReadFunc() - Read function for CDSA decryption code.
* CDSAWriteFunc() - Write function for CDSA encryption code.
static char *get_file(client_t *con, struct stat *filestats,
char *filename, int len);
static http_status_t install_conf_file(client_t *con);
+static int is_path_absolute(const char *path);
static int pipe_command(client_t *con, int infile, int *outfile,
char *command, char *options);
/* Time of last DoS attack */
- LogMessage(L_DEBUG2, "AcceptClient(%p) %d NumClients = %d",
+ LogMessage(L_DEBUG2, "AcceptClient(lis=%p) %d NumClients = %d",
lis, lis->fd, NumClients);
/*
memset(con, 0, sizeof(client_t));
con->http.activity = time(NULL);
+ con->file = -1;
/*
* Accept the client and get the remote address...
return;
}
-
+
/*
* Get the hostname or format the IP address as needed...
*/
* 'CloseClient()' - Close a remote client.
*/
-void
+int /* O - 1 if partial close, 0 if fully closed */
CloseClient(client_t *con) /* I - Client to close */
{
+ int partial; /* Do partial close for SSL? */
#if defined(HAVE_LIBSSL)
SSL_CTX *context; /* Context for encryption */
SSL *conn; /* Connection for encryption */
#endif /* HAVE_GNUTLS */
- LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd);
-
- if (con->http.input_set)
- free(con->http.input_set);
+ LogMessage(L_DEBUG, "CloseClient: %d", con->http.fd);
- httpClearCookie(HTTP(con));
-
- ClearString(&con->filename);
- ClearString(&con->command);
- ClearString(&con->options);
+ partial = 0;
#ifdef HAVE_SSL
/*
if (con->http.tls)
{
+ partial = 1;
+
# ifdef HAVE_LIBSSL
conn = (SSL *)(con->http.tls);
context = SSL_get_SSL_CTX(conn);
* Close the socket and clear the file from the input set for select()...
*/
- if (con->http.fd >= 0)
+ if (con->http.fd > 0)
{
- LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...",
- con->http.fd);
- close(con->http.fd);
- FD_CLR(con->http.fd, InputSet);
- FD_CLR(con->http.fd, OutputSet);
- con->http.fd = 0;
- }
+ if (partial)
+ {
+ /*
+ * Only do a partial close so that the encrypted client gets everything.
+ */
- if (con->pipe_pid != 0)
- {
- LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet...",
- con->file);
- FD_CLR(con->file, InputSet);
+ LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from OutputSet...",
+ con->http.fd);
+ shutdown(con->http.fd, 0);
+ FD_CLR(con->http.fd, OutputSet);
+ }
+ else
+ {
+ /*
+ * Shut the socket down fully...
+ */
+
+ LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...",
+ con->http.fd);
+ close(con->http.fd);
+ FD_CLR(con->http.fd, InputSet);
+ FD_CLR(con->http.fd, OutputSet);
+ con->http.fd = -1;
+ }
}
- if (con->file)
+ if (con->pipe_pid != 0)
{
/*
- * Close the open data file...
+ * Stop any CGI process...
*/
- if (con->pipe_pid)
- kill(con->pipe_pid, SIGKILL);
+ LogMessage(L_DEBUG2, "CloseClient: %d Killing process ID %d...",
+ con->http.fd, con->pipe_pid);
+ kill(con->pipe_pid, SIGKILL);
+ }
- LogMessage(L_DEBUG2, "CloseClient() %d Closing data file %d.",
- con->http.fd, con->file);
- LogMessage(L_DEBUG2, "CloseClient() %d Removing fd %d from InputSet.",
+ if (con->file >= 0)
+ {
+ if (FD_ISSET(con->file, InputSet))
+ {
+ LogMessage(L_DEBUG2, "CloseClient: %d Removing fd %d from InputSet...",
+ con->http.fd, con->file);
+ FD_CLR(con->file, InputSet);
+ }
+
+ LogMessage(L_DEBUG2, "CloseClient: %d Closing data file %d.",
con->http.fd, con->file);
- FD_CLR(con->file, InputSet);
close(con->file);
- con->file = 0;
+ con->file = -1;
}
- if (con->request)
+ if (!partial)
{
- ippDelete(con->request);
- con->request = NULL;
- }
+ /*
+ * Free memory...
+ */
- if (con->response)
- {
- ippDelete(con->response);
- con->response = NULL;
- }
+ if (con->http.input_set)
+ free(con->http.input_set);
- if (con->language)
- {
- cupsLangFree(con->language);
- con->language = NULL;
- }
+ httpClearCookie(HTTP(con));
- /*
- * Re-enable new client connections if we are going back under the
- * limit...
- */
+ ClearString(&con->filename);
+ ClearString(&con->command);
+ ClearString(&con->options);
- if (NumClients == MaxClients)
- ResumeListening();
+ if (con->request)
+ {
+ ippDelete(con->request);
+ con->request = NULL;
+ }
- /*
- * Compact the list of clients as necessary...
- */
+ if (con->response)
+ {
+ ippDelete(con->response);
+ con->response = NULL;
+ }
+
+ if (con->language)
+ {
+ cupsLangFree(con->language);
+ con->language = NULL;
+ }
+
+ /*
+ * Re-enable new client connections if we are going back under the
+ * limit...
+ */
+
+ if (NumClients == MaxClients)
+ ResumeListening();
+
+ /*
+ * Compact the list of clients as necessary...
+ */
- NumClients --;
+ NumClients --;
+
+ if (con < (Clients + NumClients))
+ memmove(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
+ }
- if (con < (Clients + NumClients))
- memmove(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
+ return (partial);
}
SSL_set_fd(conn, con->http.fd);
if (SSL_accept(conn) != 1)
{
+ LogMessage(L_ERROR, "EncryptClient: Unable to encrypt connection from %s!",
+ con->http.hostname);
+
while ((error = ERR_get_error()) != 0)
LogMessage(L_ERROR, "EncryptClient: %s", ERR_error_string(error, NULL));
return (0);
}
+ LogMessage(L_DEBUG, "EncryptClient: %d Connection from %s now encrypted.",
+ con->http.fd, con->http.hostname);
+
con->http.tls = conn;
return (1);
malloc(sizeof(gnutls_certificate_server_credentials));
if (credentials == NULL)
{
+ LogMessage(L_ERROR, "EncryptClient: Unable to encrypt connection from %s!",
+ con->http.hostname);
+ LogMessage(L_ERROR, "EncryptClient: %s", strerror(errno));
+
free(conn);
return (0);
}
if (error != GNUTLS_E_SUCCESS)
{
+ LogMessage(L_ERROR, "EncryptClient: Unable to encrypt connection from %s!",
+ con->http.hostname);
LogMessage(L_ERROR, "EncryptClient: %s", gnutls_strerror(error));
+
gnutls_deinit(conn->session);
gnutls_certificate_free_credentials(*credentials);
free(conn);
return (0);
}
- LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.",
- con->http.fd);
+ LogMessage(L_DEBUG, "EncryptClient: %d Connection from %s now encrypted.",
+ con->http.fd, con->http.hostname);
conn->credentials = credentials;
con->http.tls = conn;
if (error)
{
- LogMessage(L_ERROR, "EncryptClient: %d", error);
+ LogMessage(L_ERROR, "EncryptClient: Unable to encrypt connection from %s!",
+ con->http.hostname);
+
+ LogMessage(L_ERROR, "EncryptClient: CDSA error code is %d", error);
con->http.error = error;
con->http.status = HTTP_ERROR;
return (0);
}
- LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.",
- con->http.fd);
+ LogMessage(L_DEBUG, "EncryptClient: %d Connection from %s now encrypted.",
+ con->http.fd, con->http.hostname);
+
con->http.tls = conn;
return (1);
* Check for known types...
*/
- if (strcasecmp(type->super, "application"))
+ if (!type || strcasecmp(type->super, "application"))
{
LogMessage(L_DEBUG2, "IsCGI: Returning 0...");
return (0);
* 'ReadClient()' - Read data from a client.
*/
-int /* O - 1 on success, 0 on error */
-ReadClient(client_t *con) /* I - Client to read from */
+int /* O - 1 on success, 0 on error */
+ReadClient(client_t *con) /* I - Client to read from */
{
- char line[32768], /* Line from client... */
- operation[64], /* Operation code from socket */
- version[64]; /* HTTP version number string */
- int major, minor; /* HTTP version numbers */
- http_status_t status; /* Transfer status */
- ipp_state_t ipp_state; /* State of IPP transfer */
- int bytes; /* Number of bytes to POST */
- char *filename; /* Name of file for GET/HEAD */
- char buf[1024]; /* Buffer for real filename */
- struct stat filestats; /* File information */
- mime_type_t *type; /* MIME type of file */
- printer_t *p; /* Printer */
- location_t *best; /* Best match for authentication */
- static unsigned request_id = 0;/* Request ID for temp files */
+ char line[32768], /* Line from client... */
+ operation[64], /* Operation code from socket */
+ version[64], /* HTTP version number string */
+ locale[64], /* Locale */
+ *ptr; /* Pointer into strings */
+ int major, minor; /* HTTP version numbers */
+ http_status_t status; /* Transfer status */
+ ipp_state_t ipp_state; /* State of IPP transfer */
+ int bytes; /* Number of bytes to POST */
+ char *filename; /* Name of file for GET/HEAD */
+ char buf[1024]; /* Buffer for real filename */
+ struct stat filestats; /* File information */
+ mime_type_t *type; /* MIME type of file */
+ printer_t *p; /* Printer */
+ location_t *best; /* Best match for authentication */
+ static unsigned request_id = 0; /* Request ID for temp files */
status = HTTP_CONTINUE;
- LogMessage(L_DEBUG2, "ReadClient() %d, used=%d", con->http.fd,
- con->http.used);
+ LogMessage(L_DEBUG2, "ReadClient: %d, used=%d, file=%d", con->http.fd,
+ con->http.used, con->file);
if (con->http.error)
{
- CloseClient(con);
- return (0);
+ LogMessage(L_DEBUG2, "ReadClient: http error seen...");
+ return (CloseClient(con));
}
switch (con->http.state)
if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
{
- CloseClient(con);
- return (0);
+ LogMessage(L_DEBUG2, "ReadClient: httpGets returned EOF...");
+ return (CloseClient(con));
}
/*
con->http.data_remaining = 0;
con->operation = HTTP_WAITING;
con->bytes = 0;
- con->file = 0;
+ con->file = -1;
+ con->file_ready = 0;
con->pipe_pid = 0;
con->username[0] = '\0';
con->password[0] = '\0';
switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
{
case 1 :
- LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
+ LogMessage(L_ERROR, "Bad request line \"%s\" from %s!", line,
+ con->http.hostname);
SendError(con, HTTP_BAD_REQUEST);
- ShutdownClient(con);
- return (1);
+ return (CloseClient(con));
case 2 :
con->http.version = HTTP_0_9;
break;
case 3 :
if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
{
- LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
+ LogMessage(L_ERROR, "Bad request line \"%s\" from %s!", line,
+ con->http.hostname);
SendError(con, HTTP_BAD_REQUEST);
- ShutdownClient(con);
- return (1);
+ return (CloseClient(con));
}
if (major < 2)
else
{
SendError(con, HTTP_NOT_SUPPORTED);
- ShutdownClient(con);
- return (1);
+ return (CloseClient(con));
}
break;
}
LogMessage(L_ERROR, "Bad URI \"%s\" in request!", con->uri);
SendError(con, HTTP_METHOD_NOT_ALLOWED);
- ShutdownClient(con);
- return (1);
+ return (CloseClient(con));
}
/*
{
LogMessage(L_ERROR, "Bad operation \"%s\"!", operation);
SendError(con, HTTP_BAD_REQUEST);
- ShutdownClient(con);
- return (1);
+ return (CloseClient(con));
}
con->start = time(NULL);
con->operation = con->http.state;
- LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
+ LogMessage(L_DEBUG, "ReadClient: %d %s %s HTTP/%d.%d", con->http.fd,
operation, con->uri,
con->http.version / 100, con->http.version % 100);
if (status != HTTP_OK && status != HTTP_CONTINUE)
{
SendError(con, HTTP_BAD_REQUEST);
- ShutdownClient(con);
- return (1);
+ return (CloseClient(con));
}
break;
if (status == HTTP_OK)
{
if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
- con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
+ {
+ /*
+ * Figure out the locale from the Accept-Language and Content-Type
+ * fields...
+ */
+
+ if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ',')) != NULL)
+ *ptr = '\0';
+
+ if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ';')) != NULL)
+ *ptr = '\0';
+
+ if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "charset=")) != NULL)
+ {
+ /*
+ * Combine language and charset, and trim any extra params in the
+ * content-type.
+ */
+
+ snprintf(locale, sizeof(locale), "%s.%s",
+ con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8);
+
+ if ((ptr = strchr(locale, ',')) != NULL)
+ *ptr = '\0';
+ }
+ else
+ snprintf(locale, sizeof(locale), "%s.%s",
+ con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], DefaultCharset);
+
+ con->language = cupsLangGet(locale);
+ }
else
- con->language = cupsLangGet(DefaultLanguage);
+ con->language = cupsLangGet(DefaultLocale);
decode_auth(con);
*/
if (!SendError(con, HTTP_BAD_REQUEST))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
else if (con->operation == HTTP_OPTIONS)
{
best->type != AUTH_NONE)
{
if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
*/
if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
EncryptClient(con);
#else
if (!SendError(con, HTTP_NOT_IMPLEMENTED))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
#endif /* HAVE_SSL */
}
}
if (!SendHeader(con, HTTP_OK, NULL))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
httpPrintf(HTTP(con), "Content-Length: 0\r\n");
httpPrintf(HTTP(con), "\r\n");
}
- else if (strstr(con->uri, "..") != NULL)
+ else if (!is_path_absolute(con->uri))
{
/*
* Protect against malicious users!
*/
if (!SendError(con, HTTP_FORBIDDEN))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
else
{
*/
if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
httpPrintf(HTTP(con), "\r\n");
EncryptClient(con);
-
- status = IsAuthorized(con);
#else
if (!SendError(con, HTTP_NOT_IMPLEMENTED))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
#endif /* HAVE_SSL */
}
- if (status != HTTP_OK)
+ if ((status = IsAuthorized(con)) != HTTP_OK)
{
LogMessage(L_DEBUG2, "ReadClient: Unauthorized request for %s...\n",
con->uri);
SendError(con, status);
- ShutdownClient(con);
- return (1);
+ return (CloseClient(con));
}
if (con->http.expect)
else
{
if (!SendError(con, HTTP_NOT_FOUND))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
break;
}
if (strncmp(con->uri, "/admin", 6) == 0)
{
SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
- SetString(&con->options, con->uri + 6);
+
+ if ((ptr = strchr(con->uri + 6, '?')) != NULL)
+ SetStringf(&con->options, "admin%s", ptr);
+ else
+ SetString(&con->options, "admin");
}
else if (strncmp(con->uri, "/printers", 9) == 0)
{
else
{
SetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin);
- SetString(&con->options, con->uri + 5);
+ SetString(&con->options, con->uri + 5);
}
- if (con->options[0] == '/')
- cups_strcpy(con->options, con->options + 1);
+ if (con->options[0] == '/')
+ cups_strcpy(con->options, con->options + 1);
if (!SendCommand(con, con->command, con->options))
{
if (!SendError(con, HTTP_NOT_FOUND))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
else
LogRequest(con, HTTP_OK);
*/
if (!SendError(con, HTTP_FORBIDDEN))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
break;
}
sizeof(buf))) == NULL)
{
if (!SendError(con, HTTP_NOT_FOUND))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
break;
}
if (IsCGI(con, filename, &filestats, type))
{
+ /*
+ * Note: con->command and con->options were set by
+ * IsCGI()...
+ */
+
if (!SendCommand(con, con->command, con->options))
{
if (!SendError(con, HTTP_NOT_FOUND))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
else
LogRequest(con, HTTP_OK);
if (!check_if_modified(con, &filestats))
{
if (!SendError(con, HTTP_NOT_MODIFIED))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
else
{
snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
if (!SendFile(con, HTTP_OK, filename, line, &filestats))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
}
break;
*/
if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
break;
}
*/
if (!SendError(con, HTTP_BAD_REQUEST))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
break;
}
if (strncmp(con->uri, "/admin", 6) == 0)
{
SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
- SetString(&con->options, con->uri + 6);
+
+ if ((ptr = strchr(con->uri + 6, '?')) != NULL)
+ SetStringf(&con->options, "admin%s", ptr);
+ else
+ SetString(&con->options, "admin");
}
else if (strncmp(con->uri, "/printers", 9) == 0)
{
if (con->options[0] == '/')
cups_strcpy(con->options, con->options + 1);
- LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"",
+ LogMessage(L_DEBUG2, "ReadClient: %d command=\"%s\", options = \"%s\"",
con->http.fd, con->command, con->options);
if (con->http.version <= HTTP_1_0)
sizeof(buf))) == NULL)
{
if (!SendError(con, HTTP_NOT_FOUND))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
break;
}
*/
if (!SendError(con, HTTP_UNAUTHORIZED))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
}
break;
*/
if (!SendError(con, HTTP_FORBIDDEN))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
break;
}
*/
if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
break;
}
+ else if (atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) < 0)
+ {
+ /*
+ * Negative content lengths are invalid!
+ */
+
+ if (!SendError(con, HTTP_BAD_REQUEST))
+ return (CloseClient(con));
+
+ break;
+ }
/*
* Open a temporary file to hold the request...
SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
- fchmod(con->file, 0640);
- fchown(con->file, User, Group);
- LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
+ LogMessage(L_DEBUG2, "ReadClient: %d REQUEST %s=%d", con->http.fd,
con->filename, con->file);
if (con->file < 0)
{
if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
+
+ fchmod(con->file, 0640);
+ fchown(con->file, RunUser, Group);
+ fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
break;
case HTTP_DELETE :
case HTTP_TRACE :
SendError(con, HTTP_NOT_IMPLEMENTED);
- ShutdownClient(con);
- return (1);
+ return (CloseClient(con));
case HTTP_HEAD :
if (strncmp(con->uri, "/printers/", 10) == 0 &&
else
{
if (!SendError(con, HTTP_NOT_FOUND))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
break;
}
*/
if (!SendHeader(con, HTTP_OK, "text/html"))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
if (httpPrintf(HTTP(con), "\r\n") < 0)
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
LogRequest(con, HTTP_OK);
}
*/
if (!SendError(con, HTTP_FORBIDDEN))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
break;
}
sizeof(buf))) == NULL)
{
if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
LogRequest(con, HTTP_NOT_FOUND);
}
else if (!check_if_modified(con, &filestats))
{
if (!SendError(con, HTTP_NOT_MODIFIED))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
LogRequest(con, HTTP_NOT_MODIFIED);
}
snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
if (!SendHeader(con, HTTP_OK, line))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
httpGetDateString(filestats.st_mtime)) < 0)
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
(unsigned long)filestats.st_size) < 0)
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
LogRequest(con, HTTP_OK);
}
if (httpPrintf(HTTP(con), "\r\n") < 0)
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
con->http.state = HTTP_WAITING;
break;
switch (con->http.state)
{
case HTTP_PUT_RECV :
- LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
+ LogMessage(L_DEBUG2, "ReadClient: %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
con->http.fd,
con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
con->http.data_remaining, con->file);
if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
else if (bytes > 0)
{
con->bytes += bytes;
- LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
+ LogMessage(L_DEBUG2, "ReadClient: %d writing %d bytes to %d",
con->http.fd, bytes, con->file);
if (write(con->file, line, bytes) < bytes)
con->file);
close(con->file);
- con->file = 0;
+ con->file = -1;
unlink(con->filename);
ClearString(&con->filename);
if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
}
fstat(con->file, &filestats);
- LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
+ LogMessage(L_DEBUG2, "ReadClient: %d Closing data file %d, size = %d.",
con->http.fd, con->file, (int)filestats.st_size);
close(con->file);
- con->file = 0;
+ con->file = -1;
if (filestats.st_size > MaxRequestSize &&
MaxRequestSize > 0)
* Request is too big; remove it and send an error...
*/
- LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
+ LogMessage(L_DEBUG2, "ReadClient: %d Removing temp file %s",
con->http.fd, con->filename);
unlink(con->filename);
ClearString(&con->filename);
if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
/*
*/
if (!SendError(con, status))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
break;
case HTTP_POST_RECV :
- LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
+ LogMessage(L_DEBUG2, "ReadClient: %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
con->http.fd,
con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
con->http.data_remaining, con->file);
if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
{
- LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!",
+ LogMessage(L_ERROR, "ReadClient: %d IPP Read Error!",
con->http.fd);
- if (!SendError(con, HTTP_BAD_REQUEST))
- {
- CloseClient(con);
- return (0);
- }
-
- ShutdownClient(con);
- return (1);
+ SendError(con, HTTP_BAD_REQUEST);
+ return (CloseClient(con));
}
else if (ipp_state != IPP_DATA)
break;
con->bytes += ippLength(con->request);
}
- if (con->file == 0 && con->http.state != HTTP_POST_SEND)
+ if (con->file < 0 && con->http.state != HTTP_POST_SEND)
{
/*
* Create a file as needed for the request data...
SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
- fchmod(con->file, 0640);
- fchown(con->file, User, Group);
- LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
+ LogMessage(L_DEBUG2, "ReadClient: %d REQUEST %s=%d", con->http.fd,
con->filename, con->file);
if (con->file < 0)
{
if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
+
+ fchmod(con->file, 0640);
+ fchown(con->file, RunUser, Group);
+ fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
}
if (con->http.state != HTTP_POST_SEND)
{
if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
else if (bytes > 0)
{
con->bytes += bytes;
- LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
+ LogMessage(L_DEBUG2, "ReadClient: %d writing %d bytes to %d",
con->http.fd, bytes, con->file);
if (write(con->file, line, bytes) < bytes)
con->file);
close(con->file);
- con->file = 0;
+ con->file = -1;
unlink(con->filename);
ClearString(&con->filename);
if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
}
else if (con->http.state == HTTP_POST_RECV)
- {
- return (0);
- }
+ return (1); /* ??? */
else if (con->http.state != HTTP_POST_SEND)
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
if (con->http.state == HTTP_POST_SEND)
{
- if (con->file)
+ if (con->file >= 0)
{
fstat(con->file, &filestats);
- LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
+ LogMessage(L_DEBUG2, "ReadClient: %d Closing data file %d, size = %d.",
con->http.fd, con->file, (int)filestats.st_size);
close(con->file);
- con->file = 0;
+ con->file = -1;
if (filestats.st_size > MaxRequestSize &&
MaxRequestSize > 0)
* Request is too big; remove it and send an error...
*/
- LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
+ LogMessage(L_DEBUG2, "ReadClient: %d Removing temp file %s",
con->http.fd, con->filename);
unlink(con->filename);
ClearString(&con->filename);
}
if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
if (con->command)
if (!SendCommand(con, con->command, con->options))
{
if (!SendError(con, HTTP_NOT_FOUND))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
}
else
LogRequest(con, HTTP_OK);
}
if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
else
return (1);
}
else
fd = open("/dev/null", O_RDONLY);
+ if (fd < 0)
+ {
+ LogMessage(L_ERROR, "SendCommand: %d Unable to open \"%s\" for reading: %s",
+ con->http.fd, con->filename ? con->filename : "/dev/null",
+ strerror(errno));
+ return (0);
+ }
+
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
+
con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
close(fd);
LogMessage(L_INFO, "Started \"%s\" (pid=%d)", command, con->pipe_pid);
- LogMessage(L_DEBUG, "SendCommand() %d file=%d", con->http.fd, con->file);
+ LogMessage(L_DEBUG, "SendCommand: %d file=%d", con->http.fd, con->file);
if (con->pipe_pid == 0)
return (0);
return (0);
}
+ con->file_ready = 0;
con->got_fields = 0;
con->field_col = 0;
* Put the request in the access_log file...
*/
- if (con->operation > HTTP_WAITING)
- LogRequest(con, code);
+ LogRequest(con, code);
- LogMessage(L_DEBUG, "SendError() %d code=%d", con->http.fd, code);
+ LogMessage(L_DEBUG, "SendError: %d code=%d (%s)", con->http.fd, code,
+ httpStatus(code));
/*
* To work around bugs in some proxies, don't use Keep-Alive for some
{
con->file = open(filename, O_RDONLY);
- LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
+ LogMessage(L_DEBUG, "SendFile: %d file=%d", con->http.fd, con->file);
if (con->file < 0)
return (0);
return (0);
if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
return (0);
- if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
- return (0);
+ if (ServerHeader)
+ if (httpPrintf(HTTP(con), "Server: %s\r\n", ServerHeader) < 0)
+ return (0);
if (con->http.keep_alive && con->http.version >= HTTP_1_0)
{
if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
}
else
{
- if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" "
+ if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\", "
"nonce=\"%s\"\r\n", con->http.hostname) < 0)
return (0);
}
}
-/*
- * 'ShutdownClient()' - Shutdown the receiving end of a connection.
- */
-
-void
-ShutdownClient(client_t *con) /* I - Client connection */
-{
- /*
- * Shutdown the receiving end of the socket, since the client
- * still needs to read the error message...
- */
-
- shutdown(con->http.fd, 0);
- con->http.used = 0;
-
- /*
- * Update the activity time so that we timeout after 30 seconds rather
- * then the current Timeout setting (300 by default). This prevents
- * some DoS situations...
- */
-
- con->http.activity = time(NULL) - Timeout + 30;
-
- LogMessage(L_DEBUG2, "ShutdownClient: Removing fd %d from InputSet...",
- con->http.fd);
-
- FD_CLR(con->http.fd, InputSet);
-}
-
-
/*
* 'UpdateCGI()' - Read status messages from CGI scripts and programs.
*/
void
UpdateCGI(void)
{
- int bytes; /* Number of bytes read */
- char *lineptr, /* Pointer to end of line in buffer */
- *message; /* Pointer to message text */
- int loglevel; /* Log level for message */
- static int bufused = 0; /* Number of bytes used in buffer */
- static char buffer[1024]; /* Status buffer */
-
-
- if ((bytes = read(CGIPipes[0], buffer + bufused,
- sizeof(buffer) - bufused - 1)) > 0)
- {
- bufused += bytes;
- buffer[bufused] = '\0';
- lineptr = strchr(buffer, '\n');
- }
- else if (bytes < 0 && errno == EINTR)
- return;
- else
- {
- lineptr = buffer + bufused;
- lineptr[1] = 0;
- }
-
- if (bytes == 0 && bufused == 0)
- lineptr = NULL;
-
- while (lineptr != NULL)
- {
- /*
- * Terminate each line and process it...
- */
-
- *lineptr++ = '\0';
-
- /*
- * Figure out the logging level...
- */
-
- if (strncmp(buffer, "EMERG:", 6) == 0)
- {
- loglevel = L_EMERG;
- message = buffer + 6;
- }
- else if (strncmp(buffer, "ALERT:", 6) == 0)
- {
- loglevel = L_ALERT;
- message = buffer + 6;
- }
- else if (strncmp(buffer, "CRIT:", 5) == 0)
- {
- loglevel = L_CRIT;
- message = buffer + 5;
- }
- else if (strncmp(buffer, "ERROR:", 6) == 0)
- {
- loglevel = L_ERROR;
- message = buffer + 6;
- }
- else if (strncmp(buffer, "WARNING:", 8) == 0)
- {
- loglevel = L_WARN;
- message = buffer + 8;
- }
- else if (strncmp(buffer, "NOTICE:", 6) == 0)
- {
- loglevel = L_NOTICE;
- message = buffer + 6;
- }
- else if (strncmp(buffer, "INFO:", 5) == 0)
- {
- loglevel = L_INFO;
- message = buffer + 5;
- }
- else if (strncmp(buffer, "DEBUG:", 6) == 0)
- {
- loglevel = L_DEBUG;
- message = buffer + 6;
- }
- else if (strncmp(buffer, "DEBUG2:", 7) == 0)
- {
- loglevel = L_DEBUG2;
- message = buffer + 7;
- }
- else if (strncmp(buffer, "PAGE:", 5) == 0)
- {
- loglevel = L_PAGE;
- message = buffer + 5;
- }
- else
- {
- loglevel = L_DEBUG;
- message = buffer;
- }
-
- /*
- * Skip leading whitespace in the message...
- */
-
- while (isspace(*message))
- message ++;
-
- LogMessage(loglevel, "[CGI] %s", message);
-
- /*
- * Copy over the buffer data we've used up...
- */
+ char *ptr, /* Pointer to end of line in buffer */
+ message[1024]; /* Pointer to message text */
+ int loglevel; /* Log level for message */
- strcpy(buffer, lineptr);
- bufused -= lineptr - buffer;
- if (bufused < 0)
- bufused = 0;
-
- lineptr = strchr(buffer, '\n');
- }
+ while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel,
+ message, sizeof(message))) != NULL)
+ if (!strchr(CGIStatusBuffer->buffer, '\n'))
+ break;
- if (bytes <= 0)
+ if (ptr == NULL)
{
/*
* Fatal error on pipe - should never happen!
*/
- LogMessage(L_ERROR, "UpdateCGI: error reading from CGI error pipe - %s",
+ LogMessage(L_CRIT, "UpdateCGI: error reading from CGI error pipe - %s",
strerror(errno));
}
}
ipp_state_t ipp_state; /* IPP state value */
+#ifdef DEBUG
+ LogMessage(L_DEBUG2, "WriteClient(con=%p) %d response=%p, file=%d pipe_pid=%d",
+ con, con->http.fd, con->response, con->file, con->pipe_pid);
+#endif /* DEBUG */
+
if (con->http.state != HTTP_GET_SEND &&
con->http.state != HTTP_POST_SEND)
return (1);
}
else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
{
+#ifdef DEBUG
+ LogMessage(L_DEBUG2, "WriteClient: Read %d bytes from file %d...",
+ bytes, con->file);
+#endif /* DEBUG */
+
if (con->pipe_pid && !con->got_fields)
{
/*
*bufptr++ = '\0';
httpPrintf(HTTP(con), "%s\r\n", buf);
- LogMessage(L_DEBUG2, "WriteClient() %d %s", con->http.fd, buf);
+ LogMessage(L_DEBUG2, "WriteClient: %d %s", con->http.fd, buf);
/*
* Update buffer...
*/
bytes -= (bufptr - buf);
- memcpy(buf, bufptr, bytes + 1);
+ memmove(buf, bufptr, bytes + 1);
bufptr = buf - 1;
/*
con->http.state = HTTP_WAITING;
- LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from OutputSet...",
+ LogMessage(L_DEBUG2, "WriteClient: Removing fd %d from OutputSet...",
con->http.fd);
FD_CLR(con->http.fd, OutputSet);
- if (con->file)
+ if (con->file >= 0)
{
- LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from InputSet...",
- con->file);
- FD_CLR(con->file, InputSet);
+ if (FD_ISSET(con->file, InputSet))
+ {
+ LogMessage(L_DEBUG2, "WriteClient: Removing fd %d from InputSet...",
+ con->file);
+ FD_CLR(con->file, InputSet);
+ }
if (con->pipe_pid)
kill(con->pipe_pid, SIGTERM);
- LogMessage(L_DEBUG2, "WriteClient() %d Closing data file %d.",
+ LogMessage(L_DEBUG2, "WriteClient: %d Closing data file %d.",
con->http.fd, con->file);
close(con->file);
- con->file = 0;
+ con->file = -1;
con->pipe_pid = 0;
}
if (con->filename)
{
- LogMessage(L_DEBUG2, "WriteClient() %d Removing temp file %s",
+ LogMessage(L_DEBUG2, "WriteClient: %d Removing temp file %s",
con->http.fd, con->filename);
unlink(con->filename);
ClearString(&con->filename);
return (0);
}
}
+ else
+ {
+ con->file_ready = 0;
+
+ if (con->pipe_pid && !FD_ISSET(con->file, InputSet))
+ {
+ LogMessage(L_DEBUG2, "WriteClient: Adding fd %d to InputSet...", con->file);
+ FD_SET(con->file, InputSet);
+ }
+ }
if (bytes >= 1024)
- LogMessage(L_DEBUG2, "WriteClient() %d %d bytes", con->http.fd, bytes);
+ LogMessage(L_DEBUG2, "WriteClient: %d %d bytes", con->http.fd, bytes);
con->http.activity = time(NULL);
if (*ptr == '\0')
return (1);
- LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"",
+ LogMessage(L_DEBUG2, "check_if_modified: %d If-Modified-Since=\"%s\"",
con->http.fd, ptr);
while (*ptr != '\0')
}
}
- LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
+ LogMessage(L_DEBUG2, "check_if_modified: %d sizes=%d,%d dates=%d,%d",
con->http.fd, size, (int)filestats->st_size, (int)date,
(int)filestats->st_mtime);
if ((s = strchr(value, ':')) == NULL)
{
- LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
+ LogMessage(L_DEBUG, "decode_auth: %d no colon in auth string \"%s\"",
con->http.fd, value);
return;
}
* Get the username and password from the Digest attributes...
*/
- if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username",
+ if (httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "username",
value))
strlcpy(con->username, value, sizeof(con->username));
- if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response",
+ if (httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "response",
value))
- strlcpy(con->password, value, sizeof(con->password) - 1);
+ strlcpy(con->password, value, sizeof(con->password));
}
- LogMessage(L_DEBUG2, "decode_auth() %d username=\"%s\"",
+ LogMessage(L_DEBUG2, "decode_auth: %d username=\"%s\"",
con->http.fd, con->username);
}
* 'get_file()' - Get a filename and state info.
*/
-static char * /* O - Real filename */
-get_file(client_t *con, /* I - Client connection */
- struct stat *filestats,/* O - File information */
- char *filename, /* IO - Filename buffer */
- int len) /* I - Buffer length */
+static char * /* O - Real filename */
+get_file(client_t *con, /* I - Client connection */
+ struct stat *filestats, /* O - File information */
+ char *filename, /* IO - Filename buffer */
+ int len) /* I - Buffer length */
{
- int status; /* Status of filesystem calls */
- char *params; /* Pointer to parameters in URI */
+ int status; /* Status of filesystem calls */
+ char *ptr; /* Pointer info filename */
+ int plen; /* Remaining length after pointer */
/*
else
snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
- if ((params = strchr(filename, '?')) != NULL)
- *params = '\0';
+ if ((ptr = strchr(filename, '?')) != NULL)
+ *ptr = '\0';
/*
* Grab the status for this language; if there isn't a language-specific file
{
snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
+ if ((ptr = strchr(filename, '?')) != NULL)
+ *ptr = '\0';
+
status = stat(filename, filestats);
}
}
if (!status && S_ISDIR(filestats->st_mode))
{
- if (filename[strlen(filename) - 1] == '/')
- strlcat(filename, "index.html", len);
- else
- strlcat(filename, "/index.html", len);
+ if (filename[strlen(filename) - 1] != '/')
+ strlcat(filename, "/", len);
+
+ ptr = filename + strlen(filename);
+ plen = len - (ptr - filename);
+ strlcpy(ptr, "index.html", plen);
status = stat(filename, filestats);
+
+#ifdef HAVE_JAVA
+ if (status)
+ {
+ strlcpy(ptr, "index.class", plen);
+ status = stat(filename, filestats);
+ }
+#endif /* HAVE_JAVA */
+
+#ifdef HAVE_PERL
+ if (status)
+ {
+ strlcpy(ptr, "index.pl", plen);
+ status = stat(filename, filestats);
+ }
+#endif /* HAVE_PERL */
+
+#ifdef HAVE_PHP
+ if (status)
+ {
+ strlcpy(ptr, "index.php", plen);
+ status = stat(filename, filestats);
+ }
+#endif /* HAVE_PHP */
+
+#ifdef HAVE_PYTHON
+ if (status)
+ {
+ strlcpy(ptr, "index.pyc", plen);
+ status = stat(filename, filestats);
+ }
+
+ if (status)
+ {
+ strlcpy(ptr, "index.py", plen);
+ status = stat(filename, filestats);
+ }
+#endif /* HAVE_PYTHON */
}
- LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d",
+ LogMessage(L_DEBUG2, "get_file: %d filename=%s size=%d",
con->http.fd, filename, status ? -1 : (int)filestats->st_size);
if (status)
else
NeedReload = RELOAD_ALL;
+ ReloadTime = time(NULL);
+
/*
* Return that the file was created successfully...
*/
}
+/*
+ * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. "..").
+ */
+
+static int /* O - 0 if relative, 1 if absolute */
+is_path_absolute(const char *path) /* I - Input path */
+{
+ /*
+ * Check for a leading slash...
+ */
+
+ if (path[0] != '/')
+ return (0);
+
+ /*
+ * Check for "/.." in the path...
+ */
+
+ while ((path = strstr(path, "/..")) != NULL)
+ {
+ if (!path[3] || path[3] == '/')
+ return (0);
+
+ path ++;
+ }
+
+ /*
+ * If we haven't found any relative paths, return 1 indicating an
+ * absolute path...
+ */
+
+ return (1);
+}
+
+
/*
* 'pipe_command()' - Pipe the output of a command to the remote client.
*/
int pid; /* Process ID */
char *commptr; /* Command string pointer */
char *uriptr; /* URI string pointer */
- int fd; /* Looping var */
int fds[2]; /* Pipe FDs */
int argc; /* Number of arguments */
int envc; /* Number of environment variables */
dyld_library_path[1024],/* DYLD_LIBRARY_PATH environment variable */
shlib_path[1024], /* SHLIB_PATH environment variable */
nlspath[1024], /* NLSPATH environment variable */
- query_string[10240], /* QUERY_STRING env variable */
+ *query_string, /* QUERY_STRING env variable */
remote_addr[1024], /* REMOTE_ADDR environment variable */
remote_host[1024], /* REMOTE_HOST environment variable */
remote_user[1024], /* REMOTE_USER environment variable */
server_name[1024], /* SERVER_NAME environment variable */
server_port[1024], /* SERVER_PORT environment variable */
tmpdir[1024], /* TMPDIR environment variable */
- vg_args[1024]; /* VG_ARGS environment variable */
+ vg_args[1024], /* VG_ARGS environment variable */
+ ld_assume_kernel[1024]; /* LD_ASSUME_KERNEL environment variable */
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
struct sigaction action; /* POSIX signal handler */
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+ static const char * const locale_encodings[] =
+ { /* Locale charset names */
+ "ASCII", "ISO8859-1", "ISO8859-2", "ISO8859-3",
+ "ISO8859-4", "ISO8859-5", "ISO8859-6", "ISO8859-7",
+ "ISO8859-8", "ISO8859-9", "ISO8859-10", "UTF-8",
+ "ISO8859-13", "ISO8859-14", "ISO8859-15", "CP874",
+ "CP1250", "CP1251", "CP1252", "CP1253",
+ "CP1254", "CP1255", "CP1256", "CP1257",
+ "CP1258", "KOI8R", "KOI8U", "ISO8859-11",
+ "ISO8859-16", "", "", "",
+
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+
+ "CP932", "CP936", "CP949", "CP950",
+ "CP1361", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+ "", "", "", "",
+
+ "EUC-CN", "EUC-JP", "EUC-KR", "EUC-TW"
+ };
+ static const char * const encryptions[] =
+ {
+ "CUPS_ENCRYPTION=IfRequested",
+ "CUPS_ENCRYPTION=Never",
+ "CUPS_ENCRYPTION=Required",
+ "CUPS_ENCRYPTION=Always"
+ };
/*
- * Copy the command string...
+ * Parse a copy of the options string, which is of the form:
+ *
+ * name argument+argument+argument
+ * name?argument+argument+argument
+ * name param=value¶m=value
+ * name?param=value¶m=value
+ *
+ * If the string contains an "=" character after the initial name,
+ * then we treat it as a HTTP GET form request and make a copy of
+ * the remaining string for the environment variable.
+ *
+ * The string is always parsed out as command-line arguments, to
+ * be consistent with Apache...
*/
- strlcpy(argbuf, options, sizeof(argbuf));
+ LogMessage(L_DEBUG2, "pipe_command: command=\"%s\", options=\"%s\"",
+ command, options);
- /*
- * Parse the string; arguments can be separated by + and are terminated
- * by ?...
- */
+ strlcpy(argbuf, options, sizeof(argbuf));
- argv[0] = argbuf;
+ argv[0] = argbuf;
+ query_string = NULL;
for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
- if (*commptr == ' ' || *commptr == '+')
+ {
+ /*
+ * Break arguments whenever we see a + or space...
+ */
+
+ if (*commptr == ' ' || *commptr == '+' || (*commptr == '?' && argc == 1))
{
+ /*
+ * Terminate the current string and skip trailing whitespace...
+ */
+
*commptr++ = '\0';
while (*commptr == ' ')
commptr ++;
- if (*commptr != '\0')
+ /*
+ * If we don't have a blank string, save it as another argument...
+ */
+
+ if (*commptr)
{
argv[argc] = commptr;
argc ++;
}
+ else
+ break;
+
+ /*
+ * If we see an "=" in the remaining string, make a copy of it since
+ * it will be query data...
+ */
+
+ if (argc == 2 && strchr(commptr, '=') && con->operation == HTTP_GET)
+ SetStringf(&query_string, "QUERY_STRING=%s", commptr);
+
+ /*
+ * Don't skip the first non-blank character...
+ */
commptr --;
}
- else if (*commptr == '%')
+ else if (*commptr == '%' && isxdigit(commptr[1] & 255) &&
+ isxdigit(commptr[2] & 255))
{
+ /*
+ * Convert the %xx notation to the individual character.
+ */
+
if (commptr[1] >= '0' && commptr[1] <= '9')
*commptr = (commptr[1] - '0') << 4;
else
*commptr |= tolower(commptr[2]) - 'a' + 10;
cups_strcpy(commptr + 1, commptr + 3);
+
+ /*
+ * Check for a %00 and break if that is the case...
+ */
+
+ if (!*commptr)
+ break;
}
- else if (*commptr == '?')
- break;
+ }
argv[argc] = NULL;
* Setup the environment variables as needed...
*/
- snprintf(lang, sizeof(lang), "LANG=%s",
- con->language ? con->language->language : "C");
+ if (con->language)
+ snprintf(lang, sizeof(lang), "LANG=%s.%s", con->language->language,
+ locale_encodings[con->language->encoding]);
+ else
+ strcpy(lang, "LANG=C");
+
sprintf(ipp_port, "IPP_PORT=%d", LocalPort);
#ifdef AF_INET6
if (con->http.hostaddr.addr.sa_family == AF_INET6)
envp[envc ++] = vg_args;
}
+ if (getenv("LD_ASSUME_KERNEL") != NULL)
+ {
+ snprintf(ld_assume_kernel, sizeof(ld_assume_kernel), "LD_ASSUME_KERNEL=%s",
+ getenv("LD_ASSUME_KERNEL"));
+ envp[envc ++] = ld_assume_kernel;
+ }
+
if (getenv("LD_LIBRARY_PATH") != NULL)
{
snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s",
LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
envp[envc ++] = "REQUEST_METHOD=GET";
- if (*commptr)
+ if (query_string)
{
/*
* Add GET form variables after ?...
*/
- *commptr++ = '\0';
-
- snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
envp[envc ++] = query_string;
}
}
*/
if (con->http.encryption == HTTP_ENCRYPT_ALWAYS)
- {
envp[envc ++] = "HTTPS=ON";
- envp[envc ++] = "CUPS_ENCRYPTION=Always";
- }
+
+ envp[envc ++] = (char *)encryptions[LocalEncryption];
+
+ /*
+ * Terminate the environment array...
+ */
envp[envc] = NULL;
if (LogLevel == L_DEBUG2)
{
for (i = 0; i < argc; i ++)
- LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
+ LogMessage(L_DEBUG2, "pipe_command: argv[%d] = \"%s\"", i, argv[i]);
for (i = 0; i < envc; i ++)
- LogMessage(L_DEBUG2, "envp[%d] = \"%s\"", i, envp[i]);
+ LogMessage(L_DEBUG2, "pipe_command: envp[%d] = \"%s\"", i, envp[i]);
}
/*
* Create a pipe for the output...
*/
- if (pipe(fds))
+ if (cupsdOpenPipe(fds))
{
+ ClearString(&query_string);
+
LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
argv[0], strerror(errno));
return (0);
* Child comes here... Close stdin if necessary and dup the pipe to stdout.
*/
- if (getuid() == 0)
+ if (!RunUser)
{
/*
* Running as root, so change to a non-priviledged user...
if (setgid(Group))
exit(errno);
- if (setgroups(0, NULL))
+ if (setgroups(1, &Group))
exit(errno);
if (setuid(User))
* Reset group membership to just the main one we belong to.
*/
- setgroups(0, NULL);
+ setgroups(1, &Group);
}
/*
close(2);
dup(CGIPipes[1]);
- /*
- * Close extra file descriptors...
- */
-
- for (fd = 3; fd < MaxFDs; fd ++)
- close(fd);
-
/*
* Change umask to restrict permissions on created files...
*/
LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
strerror(errno));
- close(fds[0]);
- close(fds[1]);
+ cupsdClosePipe(fds);
pid = 0;
}
else
ReleaseSignals();
+ ClearString(&query_string);
+
return (pid);
}
/*
- * End of "$Id: client.c,v 1.91.2.65 2003/07/20 12:51:43 mike Exp $".
+ * End of "$Id$".
*/