/*
- * "$Id: client.c,v 1.91.2.49 2003/03/13 04:19:41 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
*
* CloseAllClients() - Close all remote clients immediately.
* CloseClient() - Close a remote client.
* EncryptClient() - Enable encryption for the client...
+ * IsCGI() - Is the resource a CGI script/program?
* ReadClient() - Read data from a client.
* SendCommand() - Send output from a command via HTTP.
* 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.
* Include necessary headers...
*/
+#include <cups/http-private.h>
#include "cupsd.h"
#include <grp.h>
-#include <cups/http-private.h>
/*
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 status; /* Exit status of pipe command */
+ 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);
- 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)
+ LogMessage(L_DEBUG2, "CloseClient: %d Killing process ID %d...",
+ con->http.fd, con->pipe_pid);
+ kill(con->pipe_pid, SIGKILL);
+ }
+
+ if (con->file >= 0)
+ {
+ if (FD_ISSET(con->file, InputSet))
{
- kill(con->pipe_pid, SIGKILL);
- waitpid(con->pipe_pid, &status, WNOHANG);
+ 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);
- LogMessage(L_DEBUG2, "CloseClient() %d Removing fd %d from 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))
- memcpy(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);
}
+/*
+ * 'IsCGI()' - Is the resource a CGI script/program?
+ */
+
+int /* O - 1 = CGI, 0 = file */
+IsCGI(client_t *con, /* I - Client connection */
+ const char *filename, /* I - Real filename */
+ struct stat *filestats, /* I - File information */
+ mime_type_t *type) /* I - MIME type */
+{
+ const char *options; /* Options on URL */
+
+
+ LogMessage(L_DEBUG2, "IsCGI(con=%p, filename=\"%s\", filestats=%p, type=%s/%s)\n",
+ con, filename, filestats, type ? type->super : "unknown",
+ type ? type->type : "unknown");
+
+ /*
+ * Get the options, if any...
+ */
+
+ if ((options = strchr(con->uri, '?')) != NULL)
+ options ++;
+
+ /*
+ * Check for known types...
+ */
+
+ if (!type || strcasecmp(type->super, "application"))
+ {
+ LogMessage(L_DEBUG2, "IsCGI: Returning 0...");
+ return (0);
+ }
+
+ if (!strcasecmp(type->type, "x-httpd-cgi") &&
+ (filestats->st_mode & 0111))
+ {
+ /*
+ * "application/x-httpd-cgi" is a CGI script.
+ */
+
+ SetString(&con->command, filename);
+
+ filename = strrchr(filename, '/') + 1; /* Filename always absolute */
+
+ if (options)
+ SetStringf(&con->options, "%s %s", filename, options);
+ else
+ SetStringf(&con->options, "%s", filename);
+
+ LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
+ con->command, con->options);
+
+ return (1);
+ }
+#ifdef HAVE_JAVA
+ else if (!strcasecmp(type->type, "x-httpd-java"))
+ {
+ /*
+ * "application/x-httpd-java" is a Java servlet.
+ */
+
+ SetString(&con->command, CUPS_JAVA);
+
+ if (options)
+ SetStringf(&con->options, "java %s %s", filename, options);
+ else
+ SetStringf(&con->options, "java %s", filename);
+
+ LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
+ con->command, con->options);
+
+ return (1);
+ }
+#endif /* HAVE_JAVA */
+#ifdef HAVE_PERL
+ else if (!strcasecmp(type->type, "x-httpd-perl"))
+ {
+ /*
+ * "application/x-httpd-perl" is a Perl page.
+ */
+
+ SetString(&con->command, CUPS_PERL);
+
+ if (options)
+ SetStringf(&con->options, "perl %s %s", filename, options);
+ else
+ SetStringf(&con->options, "perl %s", filename);
+
+ LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
+ con->command, con->options);
+
+ return (1);
+ }
+#endif /* HAVE_PERL */
+#ifdef HAVE_PHP
+ else if (!strcasecmp(type->type, "x-httpd-php"))
+ {
+ /*
+ * "application/x-httpd-php" is a PHP page.
+ */
+
+ SetString(&con->command, CUPS_PHP);
+
+ if (options)
+ SetStringf(&con->options, "php %s %s", filename, options);
+ else
+ SetStringf(&con->options, "php %s", filename);
+
+ LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
+ con->command, con->options);
+
+ return (1);
+ }
+#endif /* HAVE_PHP */
+#ifdef HAVE_PYTHON
+ else if (!strcasecmp(type->type, "x-httpd-python"))
+ {
+ /*
+ * "application/x-httpd-python" is a Python page.
+ */
+
+ SetString(&con->command, CUPS_PYTHON);
+
+ if (options)
+ SetStringf(&con->options, "python %s %s", filename, options);
+ else
+ SetStringf(&con->options, "python %s", filename);
+
+ LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
+ con->command, con->options);
+
+ return (1);
+ }
+#endif /* HAVE_PYTHON */
+
+ 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)
{
- con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
+ if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
+ {
+ /*
+ * Figure out the locale from the Accept-Language and Content-Type
+ * fields...
+ */
+
+ if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ',')) != 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(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] == '/')
- 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;
}
- type = mimeFileType(MimeDatabase, filename);
+ type = mimeFileType(MimeDatabase, filename, NULL);
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] == '/')
- strcpy(con->options, con->options + 1);
+ 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;
}
- type = mimeFileType(MimeDatabase, filename);
+ type = mimeFileType(MimeDatabase, filename, NULL);
if (!IsCGI(con, filename, &filestats, type))
{
*/
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);
}
* Serve a file...
*/
- type = mimeFileType(MimeDatabase, filename);
+ type = mimeFileType(MimeDatabase, filename, NULL);
if (type == NULL)
strcpy(line, "text/plain");
else
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 (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.
+ * 'UpdateCGI()' - Read status messages from CGI scripts and programs.
*/
void
-ShutdownClient(client_t *con) /* I - Client connection */
+UpdateCGI(void)
{
- /*
- * Shutdown the receiving end of the socket, since the client
- * still needs to read the error message...
- */
+ char *ptr, /* Pointer to end of line in buffer */
+ message[1024]; /* Pointer to message text */
+ int loglevel; /* Log level for 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;
+ while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel,
+ message, sizeof(message))) != NULL)
+ if (!strchr(CGIStatusBuffer->buffer, '\n'))
+ break;
- LogMessage(L_DEBUG2, "ShutdownClient: Removing fd %d from InputSet...",
- con->http.fd);
+ if (ptr == NULL)
+ {
+ /*
+ * Fatal error on pipe - should never happen!
+ */
- FD_CLR(con->http.fd, InputSet);
+ 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)
static http_status_t /* O - Status */
install_conf_file(client_t *con) /* I - Connection */
{
- FILE *in, /* Input file */
+ cups_file_t *in, /* Input file */
*out; /* Output file */
char buffer[1024]; /* Copy buffer */
int bytes; /* Number of bytes */
{
confinfo.st_uid = User;
confinfo.st_gid = Group;
- confinfo.st_mode = 0640;
+ confinfo.st_mode = ConfigFilePerm;
}
/*
* Open the request file and new config file...
*/
- if ((in = fopen(con->filename, "rb")) == NULL)
+ if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
{
LogMessage(L_ERROR, "Unable to open request file \"%s\" - %s",
con->filename, strerror(errno));
return (HTTP_SERVER_ERROR);
}
- if ((out = fopen(newfile, "wb")) == NULL)
+ if ((out = cupsFileOpen(newfile, "wb")) == NULL)
{
- fclose(in);
+ cupsFileClose(in);
LogMessage(L_ERROR, "Unable to open config file \"%s\" - %s",
newfile, strerror(errno));
return (HTTP_SERVER_ERROR);
}
- fchmod(fileno(out), confinfo.st_mode);
- fchown(fileno(out), confinfo.st_uid, confinfo.st_gid);
+ fchmod(cupsFileNumber(out), confinfo.st_mode);
+ fchown(cupsFileNumber(out), confinfo.st_uid, confinfo.st_gid);
/*
* Copy from the request to the new config file...
*/
- while ((bytes = fread(buffer, 1, sizeof(buffer), in)) > 0)
- if (fwrite(buffer, 1, bytes, out) < bytes)
+ while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
+ if (cupsFileWrite(out, buffer, bytes) < bytes)
{
LogMessage(L_ERROR, "Unable to copy to config file \"%s\" - %s",
newfile, strerror(errno));
- fclose(in);
- fclose(out);
+ cupsFileClose(in);
+ cupsFileClose(out);
unlink(newfile);
return (HTTP_SERVER_ERROR);
* Close the files...
*/
- fclose(in);
- if (fclose(out))
+ cupsFileClose(in);
+ if (cupsFileClose(out))
{
LogMessage(L_ERROR, "Error file closing config file \"%s\" - %s",
newfile, strerror(errno));
*/
if (strcmp(con->uri, "/admin/conf/cupsd.conf") == 0)
- NeedReload = TRUE;
+ NeedReload = RELOAD_CUPSD;
+ 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.
*/
char *command, /* I - Command to run */
char *options) /* I - Options for command */
{
+ int i; /* Looping var */
int pid; /* Process ID */
char *commptr; /* Command string pointer */
- int fd; /* Looping var */
+ char *uriptr; /* URI string pointer */
int fds[2]; /* Pipe FDs */
int argc; /* Number of arguments */
int envc; /* Number of environment variables */
char argbuf[10240], /* Argument buffer */
*argv[100], /* Argument strings */
*envp[100]; /* Environment variables */
- char lang[1024], /* LANG env variable */
- content_length[1024], /* CONTENT_LENGTH env variable */
- content_type[1024], /* CONTENT_TYPE env variable */
- ipp_port[1024], /* Default listen port */
- server_port[1024], /* Default server port */
- server_name[1024], /* Default listen hostname */
- remote_host[1024], /* REMOTE_HOST env variable */
- remote_user[1024], /* REMOTE_USER env variable */
- tmpdir[1024], /* TMPDIR environment variable */
- ldpath[1024], /* LD_LIBRARY_PATH environment variable */
+ char content_length[1024], /* CONTENT_LENGTH environment variable */
+ content_type[1024], /* CONTENT_TYPE environment variable */
+ cups_datadir[1024], /* CUPS_DATADIR environment variable */
+ cups_serverroot[1024], /* CUPS_SERVERROOT environment variable */
+ http_cookie[1024], /* HTTP_COOKIE environment variable */
+ http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
+ ipp_port[1024], /* IPP_PORT environment variable */
+ lang[1024], /* LANG environment variable */
+ ld_library_path[1024], /* LD_LIBRARY_PATH environment variable */
+ ld_preload[1024], /* LD_PRELOAD environment variable */
+ dyld_library_path[1024],/* DYLD_LIBRARY_PATH environment variable */
+ shlib_path[1024], /* SHLIB_PATH environment variable */
nlspath[1024], /* NLSPATH environment variable */
- datadir[1024], /* CUPS_DATADIR environment variable */
- root[1024], /* CUPS_SERVERROOT 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 */
+ script_name[1024], /* SCRIPT_NAME 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 */
+ 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
else
*commptr |= tolower(commptr[2]) - 'a' + 10;
- strcpy(commptr + 1, commptr + 3);
+ 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)
- {
- sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.ipv6.sin6_port));
sprintf(server_port, "SERVER_PORT=%d",
ntohs(con->http.hostaddr.ipv6.sin6_port));
- }
else
#endif /* AF_INET6 */
- {
- sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.ipv4.sin_port));
sprintf(server_port, "SERVER_PORT=%d",
ntohs(con->http.hostaddr.ipv4.sin_port));
- }
if (strcmp(con->http.hostname, "localhost") == 0)
strlcpy(server_name, "SERVER_NAME=localhost", sizeof(server_name));
else
snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName);
snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname);
+ strcpy(remote_addr, "REMOTE_ADDR=");
+ httpAddrString(&(con->http.hostaddr), remote_addr + 12,
+ sizeof(remote_addr) - 12);
snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
- snprintf(datadir, sizeof(datadir), "CUPS_DATADIR=%s", DataDir);
- snprintf(root, sizeof(root), "CUPS_SERVERROOT=%s", ServerRoot);
+ snprintf(cups_datadir, sizeof(cups_datadir), "CUPS_DATADIR=%s", DataDir);
+ snprintf(cups_serverroot, sizeof(cups_serverroot), "CUPS_SERVERROOT=%s", ServerRoot);
+
+ envc = 0;
+
+ envp[envc ++] = "PATH=/bin:/usr/bin";
+ envp[envc ++] = "SERVER_SOFTWARE=CUPS/1.1";
+ envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
+ if (con->http.version == HTTP_1_1)
+ envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
+ else if (con->http.version == HTTP_1_0)
+ envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
+ else
+ envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
+ envp[envc ++] = "REDIRECT_STATUS=1";
+ envp[envc ++] = "CUPS_SERVER=localhost";
+ envp[envc ++] = ipp_port;
+ envp[envc ++] = server_name;
+ envp[envc ++] = server_port;
+ envp[envc ++] = remote_addr;
+ envp[envc ++] = remote_host;
+ envp[envc ++] = remote_user;
+ envp[envc ++] = lang;
+ envp[envc ++] = TZ;
+ envp[envc ++] = tmpdir;
+ envp[envc ++] = cups_datadir;
+ envp[envc ++] = cups_serverroot;
+
+ if (getenv("VG_ARGS") != NULL)
+ {
+ snprintf(vg_args, sizeof(vg_args), "VG_ARGS=%s", getenv("VG_ARGS"));
+ 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(ldpath, sizeof(ldpath), "LD_LIBRARY_PATH=%s",
+ {
+ snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s",
getenv("LD_LIBRARY_PATH"));
- else if (getenv("DYLD_LIBRARY_PATH") != NULL)
- snprintf(ldpath, sizeof(ldpath), "DYLD_LIBRARY_PATH=%s",
+ envp[envc ++] = ld_library_path;
+ }
+
+ if (getenv("LD_PRELOAD") != NULL)
+ {
+ snprintf(ld_preload, sizeof(ld_preload), "LD_PRELOAD=%s",
+ getenv("LD_PRELOAD"));
+ envp[envc ++] = ld_preload;
+ }
+
+ if (getenv("DYLD_LIBRARY_PATH") != NULL)
+ {
+ snprintf(dyld_library_path, sizeof(dyld_library_path), "DYLD_LIBRARY_PATH=%s",
getenv("DYLD_LIBRARY_PATH"));
- else if (getenv("SHLIB_PATH") != NULL)
- snprintf(ldpath, sizeof(ldpath), "SHLIB_PATH=%s",
+ envp[envc ++] = dyld_library_path;
+ }
+
+ if (getenv("SHLIB_PATH") != NULL)
+ {
+ snprintf(shlib_path, sizeof(shlib_path), "SHLIB_PATH=%s",
getenv("SHLIB_PATH"));
- else
- ldpath[0] = '\0';
+ envp[envc ++] = shlib_path;
+ }
if (getenv("NLSPATH") != NULL)
+ {
snprintf(nlspath, sizeof(nlspath), "NLSPATH=%s", getenv("NLSPATH"));
- else
- nlspath[0] = '\0';
-
- envp[0] = "PATH=/bin:/usr/bin";
- envp[1] = "SERVER_SOFTWARE=CUPS/1.2";
- envp[2] = "GATEWAY_INTERFACE=CGI/1.1";
- envp[3] = "SERVER_PROTOCOL=HTTP/1.1";
- envp[4] = ipp_port;
- envp[5] = server_name;
- envp[6] = server_port;
- envp[7] = remote_host;
- envp[8] = remote_user;
- envp[9] = lang;
- envp[10] = TZ;
- envp[11] = tmpdir;
- envp[12] = datadir;
- envp[13] = root;
-
- envc = 14;
-
- if (ldpath[0])
- envp[envc ++] = ldpath;
-
- if (nlspath[0])
envp[envc ++] = nlspath;
+ }
+
+ if (con->http.cookie)
+ {
+ snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
+ con->http.cookie);
+ envp[envc ++] = http_cookie;
+ }
+
+ if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
+ {
+ snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
+ con->http.fields[HTTP_FIELD_USER_AGENT]);
+ envp[envc ++] = http_user_agent;
+ }
+
+ snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
+ if ((uriptr = strchr(script_name, '?')) != NULL)
+ *uriptr = '\0';
+ envp[envc ++] = script_name;
if (con->operation == HTTP_GET)
{
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, "pipe_command: argv[%d] = \"%s\"", i, argv[i]);
+ for (i = 0; i < envc; 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(1, &Group))
+ exit(errno);
+
if (setuid(User))
exit(errno);
}
+ else
+ {
+ /*
+ * Reset group membership to just the main one we belong to.
+ */
- /*
- * Reset group membership to just the main one we belong to.
- */
-
- setgroups(0, NULL);
+ setgroups(1, &Group);
+ }
/*
* Update stdin/stdout/stderr...
exit(errno);
close(2);
- open("/dev/null", O_WRONLY);
-
- /*
- * Close extra file descriptors...
- */
-
- for (fd = 3; fd < MaxFDs; fd ++)
- close(fd);
+ dup(CGIPipes[1]);
/*
* 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.49 2003/03/13 04:19:41 mike Exp $".
+ * End of "$Id$".
*/