/*
- * "$Id: client.c,v 1.91.2.76 2004/02/25 16:22:01 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
*
* 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);
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? */
#endif /* HAVE_GNUTLS */
- LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd);
+ LogMessage(L_DEBUG, "CloseClient: %d", con->http.fd);
partial = 0;
close(con->http.fd);
FD_CLR(con->http.fd, InputSet);
FD_CLR(con->http.fd, OutputSet);
- con->http.fd = 0;
+ con->http.fd = -1;
}
}
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);
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));
}
/*
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);
- CloseClient(con);
- return (0);
+ 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);
- CloseClient(con);
- return (0);
+ return (CloseClient(con));
}
if (major < 2)
else
{
SendError(con, HTTP_NOT_SUPPORTED);
- CloseClient(con);
- return (0);
+ return (CloseClient(con));
}
break;
}
LogMessage(L_ERROR, "Bad URI \"%s\" in request!", con->uri);
SendError(con, HTTP_METHOD_NOT_ALLOWED);
- CloseClient(con);
- return (0);
+ return (CloseClient(con));
}
/*
{
LogMessage(L_ERROR, "Bad operation \"%s\"!", operation);
SendError(con, HTTP_BAD_REQUEST);
- CloseClient(con);
- return (0);
+ 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);
- CloseClient(con);
- return (0);
+ return (CloseClient(con));
}
break;
*/
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);
- CloseClient(con);
- return (0);
+ 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;
}
*/
if (!SendError(con, HTTP_BAD_REQUEST))
- {
- CloseClient(con);
- return (0);
- }
+ return (CloseClient(con));
break;
}
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, getuid(), 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);
- CloseClient(con);
- return (0);
+ 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)
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);
* 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);
- }
-
- CloseClient(con);
- return (0);
+ SendError(con, HTTP_BAD_REQUEST);
+ return (CloseClient(con));
}
else if (ipp_state != IPP_DATA)
break;
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, getuid(), 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)
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)
{
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);
* 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);
* 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 (%s)", con->http.fd, code,
+ LogMessage(L_DEBUG, "SendError: %d code=%d (%s)", con->http.fd, code,
httpStatus(code));
/*
{
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);
}
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 ++;
+ char *ptr, /* Pointer to end of line in buffer */
+ message[1024]; /* Pointer to message text */
+ int loglevel; /* Log level for message */
- LogMessage(loglevel, "[CGI] %s", message);
- /*
- * Copy over the buffer data we've used up...
- */
-
- 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));
}
}
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);
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);
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 */
#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)
}
+/*
+ * '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 */
"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;
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)
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...
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.76 2004/02/25 16:22:01 mike Exp $".
+ * End of "$Id$".
*/