]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/client.c
Mirror 1.1.x changes.
[thirdparty/cups.git] / scheduler / client.c
CommitLineData
a74b005d 1/*
0f71c41e 2 * "$Id: client.c,v 1.91.2.64 2003/07/20 03:13:08 mike Exp $"
a74b005d 3 *
4 * Client routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
1d9595ab 6 * Copyright 1997-2003 by Easy Software Products, all rights reserved.
a74b005d 7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
8784b6a6 17 * 44141 Airport View Drive, Suite 204
a74b005d 18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
93894a43 26 * AcceptClient() - Accept a new client.
27 * CloseAllClients() - Close all remote clients immediately.
28 * CloseClient() - Close a remote client.
a75c006a 29 * EncryptClient() - Enable encryption for the client...
3721af07 30 * IsCGI() - Is the resource a CGI script/program?
93894a43 31 * ReadClient() - Read data from a client.
32 * SendCommand() - Send output from a command via HTTP.
33 * SendError() - Send an error message via HTTP.
34 * SendFile() - Send a file via HTTP.
35 * SendHeader() - Send an HTTP request.
c3c5af5e 36 * ShutdownClient() - Shutdown the receiving end of a connection.
0a968cfb 37 * UpdateCGI() - Read status messages from CGI scripts and programs.
93894a43 38 * WriteClient() - Write data to a client as needed.
39 * check_if_modified() - Decode an "If-Modified-Since" line.
d2122fde 40 * decode_auth() - Decode an authorization string.
93894a43 41 * get_file() - Get a filename and state info.
753453e4 42 * install_conf_file() - Install a configuration file.
93894a43 43 * pipe_command() - Pipe the output of a command to the remote client.
dcfcaeac 44 * CDSAReadFunc() - Read function for CDSA decryption code.
45 * CDSAWriteFunc() - Write function for CDSA encryption code.
a74b005d 46 */
47
48/*
49 * Include necessary headers...
50 */
51
3b602493 52#include <cups/http-private.h>
a74b005d 53#include "cupsd.h"
bd917997 54#include <grp.h>
55
a74b005d 56
57/*
58 * Local functions...
59 */
60
753453e4 61static int check_if_modified(client_t *con,
62 struct stat *filestats);
63static void decode_auth(client_t *con);
6db7190f 64static char *get_file(client_t *con, struct stat *filestats,
65 char *filename, int len);
753453e4 66static http_status_t install_conf_file(client_t *con);
67static int pipe_command(client_t *con, int infile, int *outfile,
68 char *command, char *options);
a74b005d 69
dcfcaeac 70#ifdef HAVE_CDSASSL
df28ec49 71static OSStatus CDSAReadFunc(SSLConnectionRef connection, void *data,
dcfcaeac 72 size_t *dataLength);
df28ec49 73static OSStatus CDSAWriteFunc(SSLConnectionRef connection,
dcfcaeac 74 const void *data, size_t *dataLength);
75#endif /* HAVE_CDSASSL */
76
a74b005d 77
78/*
79 * 'AcceptClient()' - Accept a new client.
80 */
81
82void
83AcceptClient(listener_t *lis) /* I - Listener socket */
84{
27eba2dd 85 int i; /* Looping var */
1060c9b6 86 int count; /* Count of connections on a host */
a74b005d 87 int val; /* Parameter value */
88 client_t *con; /* New client pointer */
99de6da0 89 const struct hostent *host; /* Host entry for address */
18fe941f 90 char *hostname;/* Hostname for address */
99de6da0 91 http_addr_t temp; /* Temporary address variable */
f0ad8cdc 92 static time_t last_dos = 0;
93 /* Time of last DoS attack */
a74b005d 94
95
aa7e125a 96 LogMessage(L_DEBUG2, "AcceptClient(%p) %d NumClients = %d",
27eba2dd 97 lis, lis->fd, NumClients);
bfa1abf0 98
12aa0c1f 99 /*
100 * Make sure we don't have a full set of clients already...
101 */
102
103 if (NumClients == MaxClients)
104 return;
105
a74b005d 106 /*
107 * Get a pointer to the next available client...
108 */
109
110 con = Clients + NumClients;
111
112 memset(con, 0, sizeof(client_t));
113 con->http.activity = time(NULL);
114
115 /*
116 * Accept the client and get the remote address...
117 */
118
119 val = sizeof(struct sockaddr_in);
120
2f6d083c 121 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr),
122 &val)) < 0)
a74b005d 123 {
27eba2dd 124 LogMessage(L_ERROR, "Unable to accept client connection - %s.",
125 strerror(errno));
a74b005d 126 return;
127 }
128
99de6da0 129#ifdef AF_INET6
130 if (lis->address.addr.sa_family == AF_INET6)
131 con->http.hostaddr.ipv6.sin6_port = lis->address.ipv6.sin6_port;
132 else
133#endif /* AF_INET6 */
134 con->http.hostaddr.ipv4.sin_port = lis->address.ipv4.sin_port;
a74b005d 135
1060c9b6 136 /*
137 * Check the number of clients on the same address...
138 */
139
140 for (i = 0, count = 0; i < NumClients; i ++)
141 if (memcmp(&(Clients[i].http.hostaddr), &(con->http.hostaddr),
142 sizeof(con->http.hostaddr)) == 0)
143 {
144 count ++;
145 if (count >= MaxClientsPerHost)
146 break;
147 }
148
149 if (count >= MaxClientsPerHost)
150 {
f0ad8cdc 151 if ((time(NULL) - last_dos) >= 60)
152 {
153 last_dos = time(NULL);
154 LogMessage(L_WARN, "Possible DoS attack - more than %d clients connecting from %s!",
155 MaxClientsPerHost, Clients[i].http.hostname);
156 }
1060c9b6 157
158#ifdef WIN32
159 closesocket(con->http.fd);
160#else
161 close(con->http.fd);
162#endif /* WIN32 */
163
164 return;
165 }
166
167 /*
168 * Get the hostname or format the IP address as needed...
169 */
170
a74b005d 171 if (HostNameLookups)
18fe941f 172 hostname = httpAddrLookup(&(con->http.hostaddr), con->http.hostname,
173 sizeof(con->http.hostname));
a74b005d 174 else
99de6da0 175 {
18fe941f 176 hostname = NULL;
99de6da0 177 httpAddrString(&(con->http.hostaddr), con->http.hostname,
178 sizeof(con->http.hostname));
179 }
a74b005d 180
06415a96 181 if (httpAddrLocalhost(&(con->http.hostaddr)))
182 {
183 /*
184 * Map accesses from the loopback interface to "localhost"...
185 */
186
187 strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname));
188 }
189 else if (httpAddrEqual(&(con->http.hostaddr), &ServerAddr))
2bdd1992 190 {
191 /*
192 * Map accesses from the same host to the server name.
193 */
194
def978d5 195 strlcpy(con->http.hostname, ServerName, sizeof(con->http.hostname));
2bdd1992 196 }
27eba2dd 197
18fe941f 198 if (hostname == NULL && HostNameLookups == 2)
99de6da0 199 {
200 /*
201 * Can't have an unresolved IP address with double-lookups enabled...
202 */
27eba2dd 203
6cd03542 204 LogMessage(L_DEBUG2, "AcceptClient: Closing connection %d...",
205 con->http.fd);
206
27eba2dd 207#ifdef WIN32
99de6da0 208 closesocket(con->http.fd);
27eba2dd 209#else
99de6da0 210 close(con->http.fd);
27eba2dd 211#endif /* WIN32 */
212
99de6da0 213 LogMessage(L_WARN, "Name lookup failed - connection from %s closed!",
214 con->http.hostname);
215 return;
27eba2dd 216 }
a74b005d 217
27eba2dd 218 if (HostNameLookups == 2)
219 {
220 /*
221 * Do double lookups as needed...
222 */
223
753453e4 224 if ((host = httpGetHostByName(con->http.hostname)) != NULL)
27eba2dd 225 {
226 /*
99de6da0 227 * See if the hostname maps to the same IP address...
27eba2dd 228 */
229
99de6da0 230 if (host->h_addrtype != con->http.hostaddr.addr.sa_family)
27eba2dd 231 {
232 /*
99de6da0 233 * Not the right type of address...
27eba2dd 234 */
235
46490d9d 236 host = NULL;
27eba2dd 237 }
238 else
239 {
240 /*
241 * Compare all of the addresses against this one...
242 */
243
46490d9d 244 for (i = 0; host->h_addr_list[i]; i ++)
99de6da0 245 {
246 httpAddrLoad(host, 0, i, &temp);
247
248 if (httpAddrEqual(&(con->http.hostaddr), &temp))
27eba2dd 249 break;
99de6da0 250 }
27eba2dd 251
46490d9d 252 if (!host->h_addr_list[i])
253 host = NULL;
27eba2dd 254 }
255 }
256
46490d9d 257 if (host == NULL)
27eba2dd 258 {
259 /*
260 * Can't have a hostname that doesn't resolve to the same IP address
261 * with double-lookups enabled...
262 */
263
6cd03542 264 LogMessage(L_DEBUG2, "AcceptClient: Closing connection %d...",
265 con->http.fd);
266
27eba2dd 267#ifdef WIN32
268 closesocket(con->http.fd);
269#else
270 close(con->http.fd);
271#endif /* WIN32 */
272
273 LogMessage(L_WARN, "IP lookup failed - connection from %s closed!",
274 con->http.hostname);
275 return;
276 }
277 }
278
99de6da0 279#ifdef AF_INET6
280 if (con->http.hostaddr.addr.sa_family == AF_INET6)
18fe941f 281 LogMessage(L_DEBUG, "AcceptClient: %d from %s:%d.", con->http.fd,
99de6da0 282 con->http.hostname, ntohs(con->http.hostaddr.ipv6.sin6_port));
283 else
284#endif /* AF_INET6 */
18fe941f 285 LogMessage(L_DEBUG, "AcceptClient: %d from %s:%d.", con->http.fd,
99de6da0 286 con->http.hostname, ntohs(con->http.hostaddr.ipv4.sin_port));
a74b005d 287
9ae34eb7 288 /*
289 * Using TCP_NODELAY improves responsiveness, especially on systems
290 * with a slow loopback interface... Since we write large buffers
291 * when sending print files and requests, there shouldn't be any
292 * performance penalty for this...
293 */
294
295 val = 1;
296 setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
297
a74b005d 298 /*
2e32fcdb 299 * Close this file on all execs...
a74b005d 300 */
301
302 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
303
2e32fcdb 304 /*
305 * Add the socket to the select() input mask.
306 */
307
18fe941f 308 LogMessage(L_DEBUG2, "AcceptClient: Adding fd %d to InputSet...",
27eba2dd 309 con->http.fd);
f3bc1068 310 FD_SET(con->http.fd, InputSet);
a74b005d 311
312 NumClients ++;
313
314 /*
315 * Temporarily suspend accept()'s until we lose a client...
316 */
317
997edb40 318 if (NumClients == MaxClients)
d236cb49 319 PauseListening();
2a0ef17a 320
bcf61448 321#ifdef HAVE_SSL
2a0ef17a 322 /*
0b50ddbc 323 * See if we are connecting on a secure port...
2a0ef17a 324 */
325
0b50ddbc 326 if (lis->encryption == HTTP_ENCRYPT_ALWAYS)
2a0ef17a 327 {
328 /*
329 * https connection; go secure...
330 */
331
b92c5a4f 332 con->http.encryption = HTTP_ENCRYPT_ALWAYS;
333
2a0ef17a 334 EncryptClient(con);
335 }
bcf61448 336#endif /* HAVE_SSL */
a74b005d 337}
338
339
340/*
341 * 'CloseAllClients()' - Close all remote clients immediately.
342 */
343
344void
345CloseAllClients(void)
346{
347 while (NumClients > 0)
348 CloseClient(Clients);
349}
350
351
352/*
353 * 'CloseClient()' - Close a remote client.
354 */
355
356void
357CloseClient(client_t *con) /* I - Client to close */
358{
dcfcaeac 359#if defined(HAVE_LIBSSL)
2a0ef17a 360 SSL_CTX *context; /* Context for encryption */
361 SSL *conn; /* Connection for encryption */
c3c5af5e 362 unsigned long error; /* Error code */
dcfcaeac 363#elif defined(HAVE_GNUTLS)
364 http_tls_t *conn; /* TLS connection information */
bcf61448 365 int error; /* Error code */
dcfcaeac 366 gnutls_certificate_server_credentials *credentials;
367 /* TLS credentials */
bcf61448 368#endif /* HAVE_GNUTLS */
a74b005d 369
370
5ea8888e 371 LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd);
a74b005d 372
2e32fcdb 373 if (con->http.input_set)
374 free(con->http.input_set);
375
2ae14c39 376 httpClearCookie(HTTP(con));
377
fc757c63 378 ClearString(&con->filename);
379 ClearString(&con->command);
380 ClearString(&con->options);
381
bcf61448 382#ifdef HAVE_SSL
2a0ef17a 383 /*
384 * Shutdown encryption as needed...
385 */
386
387 if (con->http.tls)
388 {
dcfcaeac 389# ifdef HAVE_LIBSSL
2a0ef17a 390 conn = (SSL *)(con->http.tls);
391 context = SSL_get_SSL_CTX(conn);
392
c3c5af5e 393 switch (SSL_shutdown(conn))
394 {
395 case 1 :
396 LogMessage(L_INFO, "CloseClient: SSL shutdown successful!");
397 break;
398 case -1 :
399 LogMessage(L_ERROR, "CloseClient: Fatal error during SSL shutdown!");
400 default :
401 while ((error = ERR_get_error()) != 0)
402 LogMessage(L_ERROR, "CloseClient: %s", ERR_error_string(error, NULL));
403 break;
404 }
405
2a0ef17a 406 SSL_CTX_free(context);
407 SSL_free(conn);
dcfcaeac 408
409# elif defined(HAVE_GNUTLS)
bcf61448 410 conn = (http_tls_t *)(con->http.tls);
411 credentials = (gnutls_certificate_server_credentials *)(conn->credentials);
412
413 error = gnutls_bye(conn->session, GNUTLS_SHUT_WR);
414 switch (error)
415 {
416 case GNUTLS_E_SUCCESS:
417 LogMessage(L_INFO, "CloseClient: SSL shutdown successful!");
418 break;
419 default:
420 LogMessage(L_ERROR, "CloseClient: %s", gnutls_strerror(error));
421 break;
422 }
423
424 gnutls_deinit(conn->session);
425 gnutls_certificate_free_credentials(*credentials);
426 free(credentials);
427 free(conn);
dcfcaeac 428
429# elif defined(HAVE_CDSASSL)
430 status = SSLClose((SSLContextRef)con->http.tls);
431 SSLDisposeContext((SSLContextRef)con->http.tls);
432# endif /* HAVE_LIBSSL */
2a0ef17a 433
434 con->http.tls = NULL;
435 }
dcfcaeac 436#endif /* HAVE_SSL */
2a0ef17a 437
a74b005d 438 /*
439 * Close the socket and clear the file from the input set for select()...
440 */
441
6cd03542 442 if (con->http.fd >= 0)
882031b3 443 {
e668e627 444 LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...",
27eba2dd 445 con->http.fd);
882031b3 446 close(con->http.fd);
f3bc1068 447 FD_CLR(con->http.fd, InputSet);
448 FD_CLR(con->http.fd, OutputSet);
882031b3 449 con->http.fd = 0;
450 }
a74b005d 451
a74b005d 452 if (con->pipe_pid != 0)
bfa1abf0 453 {
27eba2dd 454 LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet...",
455 con->file);
f3bc1068 456 FD_CLR(con->file, InputSet);
bfa1abf0 457 }
458
bfa1abf0 459 if (con->file)
a74b005d 460 {
26bb6ac6 461 /*
462 * Close the open data file...
463 */
464
a74b005d 465 if (con->pipe_pid)
a74b005d 466 kill(con->pipe_pid, SIGKILL);
a74b005d 467
bd917997 468 LogMessage(L_DEBUG2, "CloseClient() %d Closing data file %d.",
469 con->http.fd, con->file);
e668e627 470 LogMessage(L_DEBUG2, "CloseClient() %d Removing fd %d from InputSet.",
471 con->http.fd, con->file);
bd917997 472
f3bc1068 473 FD_CLR(con->file, InputSet);
a74b005d 474 close(con->file);
bfa1abf0 475 con->file = 0;
a74b005d 476 }
477
25135af7 478 if (con->request)
479 {
480 ippDelete(con->request);
481 con->request = NULL;
482 }
483
484 if (con->response)
485 {
486 ippDelete(con->response);
487 con->response = NULL;
488 }
489
490 if (con->language)
491 {
492 cupsLangFree(con->language);
493 con->language = NULL;
494 }
495
26bb6ac6 496 /*
497 * Re-enable new client connections if we are going back under the
498 * limit...
499 */
500
501 if (NumClients == MaxClients)
d236cb49 502 ResumeListening();
26bb6ac6 503
a74b005d 504 /*
505 * Compact the list of clients as necessary...
506 */
507
508 NumClients --;
509
510 if (con < (Clients + NumClients))
511 memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
512}
513
514
a75c006a 515/*
516 * 'EncryptClient()' - Enable encryption for the client...
517 */
518
519int /* O - 1 on success, 0 on error */
520EncryptClient(client_t *con) /* I - Client to encrypt */
521{
bcf61448 522#if defined HAVE_LIBSSL
a75c006a 523 SSL_CTX *context; /* Context for encryption */
524 SSL *conn; /* Connection for encryption */
753453e4 525 unsigned long error; /* Error code */
1c6682dd 526
527
528 /*
529 * Create the SSL context and accept the connection...
530 */
531
bcf61448 532 context = SSL_CTX_new(SSLv23_server_method());
533
534 SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
535 SSL_CTX_use_certificate_file(context, ServerCertificate, SSL_FILETYPE_PEM);
a75c006a 536
bcf61448 537 conn = SSL_new(context);
a75c006a 538
539 SSL_set_fd(conn, con->http.fd);
540 if (SSL_accept(conn) != 1)
541 {
1c6682dd 542 while ((error = ERR_get_error()) != 0)
543 LogMessage(L_ERROR, "EncryptClient: %s", ERR_error_string(error, NULL));
544
a75c006a 545 SSL_CTX_free(context);
546 SSL_free(conn);
547 return (0);
548 }
549
bcf61448 550 con->http.tls = conn;
551 return (1);
dcfcaeac 552
553#elif defined(HAVE_GNUTLS)
bcf61448 554 http_tls_t *conn; /* TLS session object */
555 int error; /* Error code */
dcfcaeac 556 gnutls_certificate_server_credentials *credentials;
557 /* TLS credentials */
bcf61448 558
559 /*
560 * Create the SSL object and perform the SSL handshake...
561 */
562
563 conn = (http_tls_t *)malloc(sizeof(gnutls_session));
dcfcaeac 564
bcf61448 565 if (conn == NULL)
566 return (0);
dcfcaeac 567
bcf61448 568 credentials = (gnutls_certificate_server_credentials *)
dcfcaeac 569 malloc(sizeof(gnutls_certificate_server_credentials));
bcf61448 570 if (credentials == NULL)
571 {
572 free(conn);
573 return (0);
574 }
575
576 gnutls_certificate_allocate_credentials(credentials);
577 gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
578 ServerKey, GNUTLS_X509_FMT_PEM);
579
580 gnutls_init(&(conn->session), GNUTLS_SERVER);
581 gnutls_set_default_priority(conn->session);
582 gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials);
583 gnutls_transport_set_ptr(conn->session, con->http.fd);
584
585 error = gnutls_handshake(conn->session);
586
587 if (error != GNUTLS_E_SUCCESS)
588 {
589 LogMessage(L_ERROR, "EncryptClient: %s", gnutls_strerror(error));
590 gnutls_deinit(conn->session);
591 gnutls_certificate_free_credentials(*credentials);
592 free(conn);
593 free(credentials);
594 return (0);
595 }
596
597 LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.",
598 con->http.fd);
599
600 conn->credentials = credentials;
a75c006a 601 con->http.tls = conn;
602 return (1);
dcfcaeac 603
604#elif defined(HAVE_CDSASSL)
605 OSStatus error; /* Error info */
606 SSLContextRef conn; /* New connection */
607 SSLProtocol tryVersion; /* Protocol version */
608 const char *hostName; /* Local hostname */
609 int allowExpired; /* Allow expired certificates? */
610 int allowAnyRoot; /* Allow any root certificate? */
611 SSLProtocol *negVersion; /* Negotiated protocol version */
612 SSLCipherSuite *negCipher; /* Negotiated cypher */
613 CFArrayRef *peerCerts; /* Certificates */
614
615
616 conn = NULL;
617 error = SSLNewContext(true, &conn);
618 allowExpired = 1;
619 allowAnyRoot = 1;
620
621 if (!error)
622 error = SSLSetIOFuncs(conn, CDSAReadFunc, CDSAWriteFunc);
623
624 if (!error)
625 error = SSLSetProtocolVersion(conn, kSSLProtocol3);
626
627 if (!error)
628 error = SSLSetConnection(conn, (SSLConnectionRef)con->http.fd);
629
630 if (!error)
631 {
632 hostName = ServerName; /* MRS: ??? */
633 error = SSLSetPeerDomainName(conn, hostName, strlen(hostName) + 1);
634 }
635
636 /* have to do these options befor setting server certs */
637 if (!error && allowExpired)
638 error = SSLSetAllowsExpiredCerts(conn, true);
639
640 if (!error && allowAnyRoot)
641 error = SSLSetAllowsAnyRoot(conn, true);
642
643 if (!error && ServerCertificatesArray != NULL)
644 error = SSLSetCertificate(conn, ServerCertificatesArray);
645
646 /*
647 * Perform SSL/TLS handshake
648 */
649
650 do
651 {
652 error = SSLHandshake(conn);
653 }
654 while (error == errSSLWouldBlock);
655
656 if (error)
657 {
658 LogMessage(L_ERROR, "EncryptClient: %d", error);
659
660 con->http.error = error;
661 con->http.status = HTTP_ERROR;
662
663 if (conn != NULL)
664 SSLDisposeContext(conn);
665
666 return (0);
667 }
668
669 LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.",
670 con->http.fd);
671 con->http.tls = conn;
672 return (1);
673
a75c006a 674#else
675 return (0);
bcf61448 676#endif /* HAVE_GNUTLS */
a75c006a 677}
678
679
3721af07 680/*
681 * 'IsCGI()' - Is the resource a CGI script/program?
682 */
683
684int /* O - 1 = CGI, 0 = file */
685IsCGI(client_t *con, /* I - Client connection */
686 const char *filename, /* I - Real filename */
687 struct stat *filestats, /* I - File information */
688 mime_type_t *type) /* I - MIME type */
689{
690 const char *options; /* Options on URL */
691
692
693 LogMessage(L_DEBUG2, "IsCGI(con=%p, filename=\"%s\", filestats=%p, type=%s/%s)\n",
694 con, filename, filestats, type ? type->super : "unknown",
695 type ? type->type : "unknown");
696
697 /*
698 * Get the options, if any...
699 */
700
701 if ((options = strchr(con->uri, '?')) != NULL)
702 options ++;
703
704 /*
705 * Check for known types...
706 */
707
708 if (strcasecmp(type->super, "application"))
709 {
710 LogMessage(L_DEBUG2, "IsCGI: Returning 0...");
711 return (0);
712 }
713
714 if (!strcasecmp(type->type, "x-httpd-cgi") &&
715 (filestats->st_mode & 0111))
716 {
717 /*
718 * "application/x-httpd-cgi" is a CGI script.
719 */
720
721 SetString(&con->command, filename);
722
723 filename = strrchr(filename, '/') + 1; /* Filename always absolute */
724
725 if (options)
726 SetStringf(&con->options, "%s %s", filename, options);
727 else
728 SetStringf(&con->options, "%s", filename);
729
730 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
731 con->command, con->options);
732
733 return (1);
734 }
735#ifdef HAVE_JAVA
736 else if (!strcasecmp(type->type, "x-httpd-java"))
737 {
738 /*
739 * "application/x-httpd-java" is a Java servlet.
740 */
741
742 SetString(&con->command, CUPS_JAVA);
743
744 if (options)
745 SetStringf(&con->options, "java %s %s", filename, options);
746 else
747 SetStringf(&con->options, "java %s", filename);
748
749 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
750 con->command, con->options);
751
752 return (1);
753 }
754#endif /* HAVE_JAVA */
755#ifdef HAVE_PERL
756 else if (!strcasecmp(type->type, "x-httpd-perl"))
757 {
758 /*
759 * "application/x-httpd-perl" is a Perl page.
760 */
761
762 SetString(&con->command, CUPS_PERL);
763
764 if (options)
765 SetStringf(&con->options, "perl %s %s", filename, options);
766 else
767 SetStringf(&con->options, "perl %s", filename);
768
769 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
770 con->command, con->options);
771
772 return (1);
773 }
774#endif /* HAVE_PERL */
775#ifdef HAVE_PHP
776 else if (!strcasecmp(type->type, "x-httpd-php"))
777 {
778 /*
779 * "application/x-httpd-php" is a PHP page.
780 */
781
782 SetString(&con->command, CUPS_PHP);
783
784 if (options)
785 SetStringf(&con->options, "php %s %s", filename, options);
786 else
787 SetStringf(&con->options, "php %s", filename);
788
789 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
790 con->command, con->options);
791
792 return (1);
793 }
794#endif /* HAVE_PHP */
795#ifdef HAVE_PYTHON
796 else if (!strcasecmp(type->type, "x-httpd-python"))
797 {
798 /*
799 * "application/x-httpd-python" is a Python page.
800 */
801
802 SetString(&con->command, CUPS_PYTHON);
803
804 if (options)
805 SetStringf(&con->options, "python %s %s", filename, options);
806 else
807 SetStringf(&con->options, "python %s", filename);
808
809 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
810 con->command, con->options);
811
812 return (1);
813 }
814#endif /* HAVE_PYTHON */
815
816 LogMessage(L_DEBUG2, "IsCGI: Returning 0...");
817
818 return (0);
819}
820
821
a74b005d 822/*
823 * 'ReadClient()' - Read data from a client.
824 */
825
826int /* O - 1 on success, 0 on error */
827ReadClient(client_t *con) /* I - Client to read from */
828{
753453e4 829 char line[32768], /* Line from client... */
a74b005d 830 operation[64], /* Operation code from socket */
831 version[64]; /* HTTP version number string */
832 int major, minor; /* HTTP version numbers */
833 http_status_t status; /* Transfer status */
bd176c9c 834 ipp_state_t ipp_state; /* State of IPP transfer */
a74b005d 835 int bytes; /* Number of bytes to POST */
e31bfb6e 836 char *filename; /* Name of file for GET/HEAD */
6db7190f 837 char buf[1024]; /* Buffer for real filename */
a74b005d 838 struct stat filestats; /* File information */
e31bfb6e 839 mime_type_t *type; /* MIME type of file */
2a8fc30c 840 printer_t *p; /* Printer */
8f85ff77 841 location_t *best; /* Best match for authentication */
5d99df62 842 static unsigned request_id = 0;/* Request ID for temp files */
a74b005d 843
bfa1abf0 844
a74b005d 845 status = HTTP_CONTINUE;
846
2d5e06dd 847 LogMessage(L_DEBUG2, "ReadClient() %d, used=%d", con->http.fd,
848 con->http.used);
55809c6f 849
901b295d 850 if (con->http.error)
851 {
852 CloseClient(con);
853 return (0);
854 }
855
a74b005d 856 switch (con->http.state)
857 {
858 case HTTP_WAITING :
859 /*
860 * See if we've received a request line...
861 */
862
863 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
93894a43 864 {
865 CloseClient(con);
866 return (0);
867 }
a74b005d 868
869 /*
870 * Ignore blank request lines...
871 */
872
873 if (line[0] == '\0')
874 break;
875
876 /*
877 * Clear other state variables...
878 */
879
880 httpClearFields(HTTP(con));
881
882 con->http.activity = time(NULL);
883 con->http.version = HTTP_1_0;
884 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
885 con->http.data_encoding = HTTP_ENCODE_LENGTH;
886 con->http.data_remaining = 0;
6a0c519d 887 con->operation = HTTP_WAITING;
888 con->bytes = 0;
a74b005d 889 con->file = 0;
890 con->pipe_pid = 0;
891 con->username[0] = '\0';
892 con->password[0] = '\0';
893 con->uri[0] = '\0';
894
fc757c63 895 ClearString(&con->command);
896 ClearString(&con->options);
897
a74b005d 898 if (con->language != NULL)
899 {
900 cupsLangFree(con->language);
901 con->language = NULL;
902 }
903
904 /*
905 * Grab the request line...
906 */
907
970017a4 908 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
a74b005d 909 {
910 case 1 :
434ddc80 911 LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
a74b005d 912 SendError(con, HTTP_BAD_REQUEST);
c3c5af5e 913 ShutdownClient(con);
2d5e06dd 914 return (1);
a74b005d 915 case 2 :
916 con->http.version = HTTP_0_9;
917 break;
918 case 3 :
919 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
920 {
434ddc80 921 LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
a74b005d 922 SendError(con, HTTP_BAD_REQUEST);
c3c5af5e 923 ShutdownClient(con);
2d5e06dd 924 return (1);
a74b005d 925 }
926
927 if (major < 2)
928 {
929 con->http.version = (http_version_t)(major * 100 + minor);
753453e4 930 if (con->http.version == HTTP_1_1 && KeepAlive)
a74b005d 931 con->http.keep_alive = HTTP_KEEPALIVE_ON;
932 else
933 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
934 }
935 else
936 {
937 SendError(con, HTTP_NOT_SUPPORTED);
c3c5af5e 938 ShutdownClient(con);
2d5e06dd 939 return (1);
a74b005d 940 }
941 break;
942 }
943
f726e5b9 944 /*
945 * Handle full URLs in the request line...
946 */
947
38e6ed71 948 if (con->uri[0] != '/' && strcmp(con->uri, "*"))
f726e5b9 949 {
950 char method[HTTP_MAX_URI], /* Method/scheme */
951 userpass[HTTP_MAX_URI], /* Username:password */
952 hostname[HTTP_MAX_URI], /* Hostname */
953 resource[HTTP_MAX_URI]; /* Resource path */
954 int port; /* Port number */
955
956
957 /*
958 * Separate the URI into its components...
959 */
960
961 httpSeparate(con->uri, method, userpass, hostname, &port, resource);
962
963 /*
964 * Only allow URIs with the servername, localhost, or an IP
965 * address...
966 */
967
968 if (strcasecmp(hostname, ServerName) &&
969 strcasecmp(hostname, "localhost") &&
970 !isdigit(hostname[0]))
971 {
972 /*
973 * Nope, we don't do proxies...
974 */
975
976 LogMessage(L_ERROR, "Bad URI \"%s\" in request!", con->uri);
977 SendError(con, HTTP_METHOD_NOT_ALLOWED);
978 ShutdownClient(con);
979 return (1);
980 }
981
982 /*
983 * Copy the resource portion back into the URI; both resource and
984 * con->uri are HTTP_MAX_URI bytes in size...
985 */
986
987 strcpy(con->uri, resource);
988 }
989
a74b005d 990 /*
991 * Process the request...
992 */
993
994 if (strcmp(operation, "GET") == 0)
995 con->http.state = HTTP_GET;
996 else if (strcmp(operation, "PUT") == 0)
997 con->http.state = HTTP_PUT;
998 else if (strcmp(operation, "POST") == 0)
999 con->http.state = HTTP_POST;
1000 else if (strcmp(operation, "DELETE") == 0)
1001 con->http.state = HTTP_DELETE;
1002 else if (strcmp(operation, "TRACE") == 0)
1003 con->http.state = HTTP_TRACE;
a74b005d 1004 else if (strcmp(operation, "OPTIONS") == 0)
1005 con->http.state = HTTP_OPTIONS;
1006 else if (strcmp(operation, "HEAD") == 0)
1007 con->http.state = HTTP_HEAD;
1008 else
1009 {
434ddc80 1010 LogMessage(L_ERROR, "Bad operation \"%s\"!", operation);
a74b005d 1011 SendError(con, HTTP_BAD_REQUEST);
c3c5af5e 1012 ShutdownClient(con);
2d5e06dd 1013 return (1);
a74b005d 1014 }
1015
6a0c519d 1016 con->start = time(NULL);
1017 con->operation = con->http.state;
1018
5ea8888e 1019 LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
4a64fdb7 1020 operation, con->uri,
1021 con->http.version / 100, con->http.version % 100);
a74b005d 1022
1023 con->http.status = HTTP_OK;
a74b005d 1024
8f85ff77 1025 case HTTP_OPTIONS :
a74b005d 1026 case HTTP_DELETE :
1027 case HTTP_GET :
1028 case HTTP_HEAD :
1029 case HTTP_POST :
1030 case HTTP_PUT :
1031 case HTTP_TRACE :
1032 /*
1033 * Parse incoming parameters until the status changes...
1034 */
1035
1036 status = httpUpdate(HTTP(con));
1037
1038 if (status != HTTP_OK && status != HTTP_CONTINUE)
1039 {
1040 SendError(con, HTTP_BAD_REQUEST);
c3c5af5e 1041 ShutdownClient(con);
2d5e06dd 1042 return (1);
a74b005d 1043 }
1044 break;
d21a7597 1045
1046 default :
1047 break; /* Anti-compiler-warning-code */
a74b005d 1048 }
1049
1050 /*
1051 * Handle new transfers...
1052 */
1053
1054 if (status == HTTP_OK)
1055 {
5a617005 1056 if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
1057 con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
1058 else
1059 con->language = cupsLangGet(DefaultLanguage);
a74b005d 1060
d2122fde 1061 decode_auth(con);
a74b005d 1062
753453e4 1063 if (strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) == 0 &&
1064 KeepAlive)
b1ac1113 1065 con->http.keep_alive = HTTP_KEEPALIVE_ON;
1066
a74b005d 1067 if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
d558743f 1068 con->http.version >= HTTP_1_1)
a74b005d 1069 {
d558743f 1070 /*
1071 * HTTP/1.1 and higher require the "Host:" field...
1072 */
1073
a74b005d 1074 if (!SendError(con, HTTP_BAD_REQUEST))
1075 {
1076 CloseClient(con);
1077 return (0);
1078 }
1079 }
8f85ff77 1080 else if (con->operation == HTTP_OPTIONS)
1081 {
1082 /*
1083 * Do OPTIONS command...
1084 */
1085
753453e4 1086 if ((best = FindBest(con->uri, con->http.state)) != NULL &&
8f85ff77 1087 best->type != AUTH_NONE)
1088 {
1089 if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL))
1090 {
1091 CloseClient(con);
1092 return (0);
1093 }
1094 }
a75c006a 1095
b5cb0608 1096 if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
a75c006a 1097 con->http.tls == NULL)
8f85ff77 1098 {
bcf61448 1099#ifdef HAVE_SSL
a75c006a 1100 /*
1101 * Do encryption stuff...
1102 */
1103
b38fb7fc 1104 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
8f85ff77 1105 {
1106 CloseClient(con);
1107 return (0);
1108 }
a75c006a 1109
1110 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
b5cb0608 1111 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1c6682dd 1112 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
a75c006a 1113 httpPrintf(HTTP(con), "\r\n");
1114
1115 EncryptClient(con);
1116#else
1117 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
1118 {
1119 CloseClient(con);
1120 return (0);
1121 }
bcf61448 1122#endif /* HAVE_SSL */
a75c006a 1123 }
1124
65c098da 1125 if (con->http.expect)
1126 {
1127 /**** TODO: send expected header ****/
1128 }
1129
a75c006a 1130 if (!SendHeader(con, HTTP_OK, NULL))
1131 {
1132 CloseClient(con);
1133 return (0);
8f85ff77 1134 }
1135
753453e4 1136 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
1c6682dd 1137 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
8f85ff77 1138 httpPrintf(HTTP(con), "\r\n");
1139 }
bd84e0d1 1140 else if (strstr(con->uri, "..") != NULL)
a74b005d 1141 {
1142 /*
1143 * Protect against malicious users!
1144 */
1145
1146 if (!SendError(con, HTTP_FORBIDDEN))
1147 {
1148 CloseClient(con);
1149 return (0);
1150 }
1151 }
b38fb7fc 1152 else
a74b005d 1153 {
b5cb0608 1154 if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
1155 con->http.tls == NULL)
b38fb7fc 1156 {
bcf61448 1157#ifdef HAVE_SSL
b38fb7fc 1158 /*
1159 * Do encryption stuff...
1160 */
f3d580b9 1161
b38fb7fc 1162 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
1163 {
1164 CloseClient(con);
1165 return (0);
1166 }
f969a074 1167
b38fb7fc 1168 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
b5cb0608 1169 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1c6682dd 1170 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
b38fb7fc 1171 httpPrintf(HTTP(con), "\r\n");
1172
1173 EncryptClient(con);
77425121 1174
1175 status = IsAuthorized(con);
b38fb7fc 1176#else
1177 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
1178 {
1179 CloseClient(con);
1180 return (0);
1181 }
bcf61448 1182#endif /* HAVE_SSL */
b38fb7fc 1183 }
b5cb0608 1184
77425121 1185 if (status != HTTP_OK)
b38fb7fc 1186 {
2d5e06dd 1187 LogMessage(L_DEBUG2, "ReadClient: Unauthorized request for %s...\n",
1188 con->uri);
b38fb7fc 1189 SendError(con, status);
c3c5af5e 1190 ShutdownClient(con);
2d5e06dd 1191 return (1);
b38fb7fc 1192 }
1193
65c098da 1194 if (con->http.expect)
1195 {
1196 /**** TODO: send expected header ****/
1197 }
1198
b38fb7fc 1199 switch (con->http.state)
1200 {
1201 case HTTP_GET_SEND :
1202 if (strncmp(con->uri, "/printers/", 10) == 0 &&
1203 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
2a8fc30c 1204 {
b38fb7fc 1205 /*
1206 * Send PPD file - get the real printer name since printer
1207 * names are not case sensitive but filenames can be...
1208 */
2a8fc30c 1209
b38fb7fc 1210 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
f3d580b9 1211
b38fb7fc 1212 if ((p = FindPrinter(con->uri + 10)) != NULL)
1213 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1214 else
1215 {
1216 if (!SendError(con, HTTP_NOT_FOUND))
1217 {
1218 CloseClient(con);
1219 return (0);
1220 }
a74b005d 1221
b38fb7fc 1222 break;
1223 }
b14d90ba 1224 }
b38fb7fc 1225
753453e4 1226 if ((strncmp(con->uri, "/admin", 6) == 0 &&
1227 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
b38fb7fc 1228 strncmp(con->uri, "/printers", 9) == 0 ||
1229 strncmp(con->uri, "/classes", 8) == 0 ||
1230 strncmp(con->uri, "/jobs", 5) == 0)
b14d90ba 1231 {
b38fb7fc 1232 /*
1233 * Send CGI output...
1234 */
1235
1236 if (strncmp(con->uri, "/admin", 6) == 0)
1237 {
fc757c63 1238 SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
1239 SetString(&con->options, con->uri + 6);
b38fb7fc 1240 }
1241 else if (strncmp(con->uri, "/printers", 9) == 0)
1242 {
fc757c63 1243 SetStringf(&con->command, "%s/cgi-bin/printers.cgi", ServerBin);
1244 SetString(&con->options, con->uri + 9);
b38fb7fc 1245 }
1246 else if (strncmp(con->uri, "/classes", 8) == 0)
1247 {
fc757c63 1248 SetStringf(&con->command, "%s/cgi-bin/classes.cgi", ServerBin);
1249 SetString(&con->options, con->uri + 8);
b38fb7fc 1250 }
1251 else
1252 {
fc757c63 1253 SetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin);
1254 SetString(&con->options, con->uri + 5);
b38fb7fc 1255 }
1256
1257 if (con->options[0] == '/')
fc757c63 1258 strcpy(con->options, con->options + 1);
b38fb7fc 1259
1260 if (!SendCommand(con, con->command, con->options))
1261 {
1262 if (!SendError(con, HTTP_NOT_FOUND))
1263 {
1264 CloseClient(con);
1265 return (0);
1266 }
1267 }
1268 else
1269 LogRequest(con, HTTP_OK);
1270
1271 if (con->http.version <= HTTP_1_0)
1272 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
b14d90ba 1273 }
753453e4 1274 else if (strncmp(con->uri, "/admin/conf/", 12) == 0 &&
1275 (strchr(con->uri + 12, '/') != NULL ||
1276 strlen(con->uri) == 12))
1277 {
1278 /*
1279 * GET can only be done to configuration files under
1280 * /admin/conf...
1281 */
1282
1283 if (!SendError(con, HTTP_FORBIDDEN))
1284 {
1285 CloseClient(con);
1286 return (0);
1287 }
1288
1289 break;
1290 }
b14d90ba 1291 else
1292 {
b38fb7fc 1293 /*
1294 * Serve a file...
1295 */
1296
6db7190f 1297 if ((filename = get_file(con, &filestats, buf,
1298 sizeof(buf))) == NULL)
b38fb7fc 1299 {
1300 if (!SendError(con, HTTP_NOT_FOUND))
1301 {
1302 CloseClient(con);
1303 return (0);
1304 }
e6221033 1305
1306 break;
1307 }
1308
d59a189c 1309 type = mimeFileType(MimeDatabase, filename, NULL);
e6221033 1310
1311 if (IsCGI(con, filename, &filestats, type))
1312 {
1313 if (!SendCommand(con, con->command, con->options))
1314 {
1315 if (!SendError(con, HTTP_NOT_FOUND))
1316 {
1317 CloseClient(con);
1318 return (0);
1319 }
1320 }
1321 else
1322 LogRequest(con, HTTP_OK);
1323
1324 if (con->http.version <= HTTP_1_0)
1325 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1326 break;
b38fb7fc 1327 }
e6221033 1328
1329 if (!check_if_modified(con, &filestats))
b38fb7fc 1330 {
1331 if (!SendError(con, HTTP_NOT_MODIFIED))
1332 {
1333 CloseClient(con);
1334 return (0);
1335 }
1336 }
1337 else
1338 {
b38fb7fc 1339 if (type == NULL)
1340 strcpy(line, "text/plain");
1341 else
1342 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1343
1344 if (!SendFile(con, HTTP_OK, filename, line, &filestats))
1345 {
1346 CloseClient(con);
1347 return (0);
1348 }
1349 }
b14d90ba 1350 }
b38fb7fc 1351 break;
1352
1353 case HTTP_POST_RECV :
1354 /*
1355 * See if the POST request includes a Content-Length field, and if
1356 * so check the length against any limits that are set...
1357 */
b14d90ba 1358
b38fb7fc 1359 LogMessage(L_DEBUG2, "POST %s", con->uri);
1360 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
b14d90ba 1361
b38fb7fc 1362 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1363 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
1364 MaxRequestSize > 0)
a74b005d 1365 {
b38fb7fc 1366 /*
1367 * Request too large...
1368 */
1369
1370 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
a74b005d 1371 {
b38fb7fc 1372 CloseClient(con);
a74b005d 1373 return (0);
1374 }
b38fb7fc 1375
1376 break;
a74b005d 1377 }
901b295d 1378 else if (atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) < 0)
1379 {
1380 /*
1381 * Negative content lengths are invalid!
1382 */
1383
1384 if (!SendError(con, HTTP_BAD_REQUEST))
1385 {
1386 CloseClient(con);
1387 return (0);
1388 }
1389
1390 break;
1391 }
a74b005d 1392
b38fb7fc 1393 /*
1394 * See what kind of POST request this is; for IPP requests the
1395 * content-type field will be "application/ipp"...
a74b005d 1396 */
1397
b38fb7fc 1398 if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
1399 con->request = ippNew();
753453e4 1400 else if ((strncmp(con->uri, "/admin", 6) == 0 &&
1401 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
b38fb7fc 1402 strncmp(con->uri, "/printers", 9) == 0 ||
1403 strncmp(con->uri, "/classes", 8) == 0 ||
1404 strncmp(con->uri, "/jobs", 5) == 0)
a74b005d 1405 {
b38fb7fc 1406 /*
1407 * CGI request...
1408 */
1409
1410 if (strncmp(con->uri, "/admin", 6) == 0)
a74b005d 1411 {
fc757c63 1412 SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
1413 SetString(&con->options, con->uri + 6);
a74b005d 1414 }
b38fb7fc 1415 else if (strncmp(con->uri, "/printers", 9) == 0)
a74b005d 1416 {
fc757c63 1417 SetStringf(&con->command, "%s/cgi-bin/printers.cgi", ServerBin);
1418 SetString(&con->options, con->uri + 9);
b38fb7fc 1419 }
1420 else if (strncmp(con->uri, "/classes", 8) == 0)
1421 {
fc757c63 1422 SetStringf(&con->command, "%s/cgi-bin/classes.cgi", ServerBin);
1423 SetString(&con->options, con->uri + 8);
a74b005d 1424 }
e31bfb6e 1425 else
a74b005d 1426 {
fc757c63 1427 SetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin);
1428 SetString(&con->options, con->uri + 5);
a74b005d 1429 }
8b43895a 1430
b38fb7fc 1431 if (con->options[0] == '/')
fc757c63 1432 strcpy(con->options, con->options + 1);
f63a2256 1433
b38fb7fc 1434 LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"",
1435 con->http.fd, con->command, con->options);
8b43895a 1436
b38fb7fc 1437 if (con->http.version <= HTTP_1_0)
1438 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1439 }
e6221033 1440 else
8b43895a 1441 {
e6221033 1442 /*
1443 * POST to a file...
1444 */
1445
1446 if ((filename = get_file(con, &filestats, buf,
1447 sizeof(buf))) == NULL)
1448 {
1449 if (!SendError(con, HTTP_NOT_FOUND))
1450 {
1451 CloseClient(con);
1452 return (0);
1453 }
1454
1455 break;
1456 }
1457
d59a189c 1458 type = mimeFileType(MimeDatabase, filename, NULL);
e6221033 1459
1460 if (!IsCGI(con, filename, &filestats, type))
1461 {
1462 /*
1463 * Only POST to CGI's...
1464 */
1465
1466 if (!SendError(con, HTTP_UNAUTHORIZED))
1467 {
1468 CloseClient(con);
1469 return (0);
1470 }
1471 }
8b43895a 1472 }
8b43895a 1473 break;
b14d90ba 1474
b38fb7fc 1475 case HTTP_PUT_RECV :
753453e4 1476 /*
1477 * Validate the resource name...
1478 */
1479
1480 if (strncmp(con->uri, "/admin/conf/", 12) != 0 ||
1481 strchr(con->uri + 12, '/') != NULL ||
1482 strlen(con->uri) == 12)
1483 {
1484 /*
1485 * PUT can only be done to configuration files under
1486 * /admin/conf...
1487 */
1488
1489 if (!SendError(con, HTTP_FORBIDDEN))
1490 {
1491 CloseClient(con);
1492 return (0);
1493 }
1494
1495 break;
1496 }
1497
1498 /*
1499 * See if the PUT request includes a Content-Length field, and if
1500 * so check the length against any limits that are set...
1501 */
1502
1503 LogMessage(L_DEBUG2, "PUT %s", con->uri);
1504 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1505
1506 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1507 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
1508 MaxRequestSize > 0)
1509 {
1510 /*
1511 * Request too large...
1512 */
1513
1514 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1515 {
1516 CloseClient(con);
1517 return (0);
1518 }
1519
1520 break;
1521 }
1522
1523 /*
1524 * Open a temporary file to hold the request...
1525 */
1526
fc757c63 1527 SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
753453e4 1528 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1529 fchmod(con->file, 0640);
1530 fchown(con->file, User, Group);
1531
1532 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
1533 con->filename, con->file);
1534
1535 if (con->file < 0)
1536 {
1537 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1538 {
1539 CloseClient(con);
1540 return (0);
1541 }
1542 }
1543 break;
1544
b38fb7fc 1545 case HTTP_DELETE :
1546 case HTTP_TRACE :
1547 SendError(con, HTTP_NOT_IMPLEMENTED);
c3c5af5e 1548 ShutdownClient(con);
2d5e06dd 1549 return (1);
b14d90ba 1550
b38fb7fc 1551 case HTTP_HEAD :
d588a92e 1552 if (strncmp(con->uri, "/printers/", 10) == 0 &&
b38fb7fc 1553 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
bd84e0d1 1554 {
b38fb7fc 1555 /*
d588a92e 1556 * Send PPD file - get the real printer name since printer
1557 * names are not case sensitive but filenames can be...
b38fb7fc 1558 */
a74b005d 1559
d588a92e 1560 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1561
1562 if ((p = FindPrinter(con->uri + 10)) != NULL)
1563 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1564 else
1565 {
1566 if (!SendError(con, HTTP_NOT_FOUND))
1567 {
1568 CloseClient(con);
1569 return (0);
1570 }
1571
1572 break;
1573 }
b38fb7fc 1574 }
a74b005d 1575
753453e4 1576 if ((strncmp(con->uri, "/admin/", 7) == 0 &&
1577 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
b38fb7fc 1578 strncmp(con->uri, "/printers/", 10) == 0 ||
1579 strncmp(con->uri, "/classes/", 9) == 0 ||
1580 strncmp(con->uri, "/jobs/", 6) == 0)
1581 {
1582 /*
1583 * CGI output...
1584 */
f3d580b9 1585
b38fb7fc 1586 if (!SendHeader(con, HTTP_OK, "text/html"))
1587 {
1588 CloseClient(con);
1589 return (0);
1590 }
f3d580b9 1591
b38fb7fc 1592 if (httpPrintf(HTTP(con), "\r\n") < 0)
1593 {
1594 CloseClient(con);
1595 return (0);
1596 }
a74b005d 1597
b38fb7fc 1598 LogRequest(con, HTTP_OK);
a74b005d 1599 }
753453e4 1600 else if (strncmp(con->uri, "/admin/conf/", 12) == 0 &&
1601 (strchr(con->uri + 12, '/') != NULL ||
1602 strlen(con->uri) == 12))
1603 {
1604 /*
1605 * HEAD can only be done to configuration files under
1606 * /admin/conf...
1607 */
1608
1609 if (!SendError(con, HTTP_FORBIDDEN))
1610 {
1611 CloseClient(con);
1612 return (0);
1613 }
1614
1615 break;
1616 }
6db7190f 1617 else if ((filename = get_file(con, &filestats, buf,
1618 sizeof(buf))) == NULL)
a74b005d 1619 {
b38fb7fc 1620 if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
1621 {
1622 CloseClient(con);
1623 return (0);
1624 }
6a0c519d 1625
b38fb7fc 1626 LogRequest(con, HTTP_NOT_FOUND);
a74b005d 1627 }
b38fb7fc 1628 else if (!check_if_modified(con, &filestats))
1629 {
1630 if (!SendError(con, HTTP_NOT_MODIFIED))
1631 {
1632 CloseClient(con);
1633 return (0);
1634 }
6a0c519d 1635
b38fb7fc 1636 LogRequest(con, HTTP_NOT_MODIFIED);
a74b005d 1637 }
b38fb7fc 1638 else
1639 {
1640 /*
1641 * Serve a file...
1642 */
6a0c519d 1643
d59a189c 1644 type = mimeFileType(MimeDatabase, filename, NULL);
b38fb7fc 1645 if (type == NULL)
1646 strcpy(line, "text/plain");
1647 else
1648 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
a74b005d 1649
b38fb7fc 1650 if (!SendHeader(con, HTTP_OK, line))
1651 {
1652 CloseClient(con);
1653 return (0);
1654 }
a74b005d 1655
b38fb7fc 1656 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1657 httpGetDateString(filestats.st_mtime)) < 0)
1658 {
1659 CloseClient(con);
1660 return (0);
1661 }
a74b005d 1662
753453e4 1663 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1664 (unsigned long)filestats.st_size) < 0)
b38fb7fc 1665 {
1666 CloseClient(con);
1667 return (0);
1668 }
1669
1670 LogRequest(con, HTTP_OK);
a74b005d 1671 }
1672
b38fb7fc 1673 if (httpPrintf(HTTP(con), "\r\n") < 0)
a74b005d 1674 {
1675 CloseClient(con);
1676 return (0);
1677 }
6a0c519d 1678
b38fb7fc 1679 con->http.state = HTTP_WAITING;
1680 break;
a74b005d 1681
b38fb7fc 1682 default :
1683 break; /* Anti-compiler-warning-code */
1684 }
a74b005d 1685 }
1686 }
1687
1688 /*
1689 * Handle any incoming data...
1690 */
1691
1692 switch (con->http.state)
1693 {
1694 case HTTP_PUT_RECV :
753453e4 1695 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
1696 con->http.fd,
1697 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
1698 con->http.data_remaining, con->file);
1699
1700 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
1701 {
1702 CloseClient(con);
1703 return (0);
1704 }
1705 else if (bytes > 0)
1706 {
1707 con->bytes += bytes;
1708
1709 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
1710 con->http.fd, bytes, con->file);
1711
1712 if (write(con->file, line, bytes) < bytes)
1713 {
1714 LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s",
1715 bytes, con->filename, strerror(errno));
1716
6cd03542 1717 LogMessage(L_DEBUG2, "ReadClient: Closing data file %d...",
1718 con->file);
1719
753453e4 1720 close(con->file);
1721 con->file = 0;
1722 unlink(con->filename);
fc757c63 1723 ClearString(&con->filename);
753453e4 1724
1725 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1726 {
1727 CloseClient(con);
1728 return (0);
1729 }
1730 }
1731 }
1732
1733 if (con->http.state == HTTP_WAITING)
1734 {
1735 /*
1736 * End of file, see how big it is...
1737 */
1738
1739 fstat(con->file, &filestats);
1740
1741 LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
aa7e125a 1742 con->http.fd, con->file, (int)filestats.st_size);
753453e4 1743
1744 close(con->file);
1745 con->file = 0;
1746
1747 if (filestats.st_size > MaxRequestSize &&
1748 MaxRequestSize > 0)
1749 {
1750 /*
1751 * Request is too big; remove it and send an error...
1752 */
1753
1754 LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
1755 con->http.fd, con->filename);
1756 unlink(con->filename);
fc757c63 1757 ClearString(&con->filename);
753453e4 1758
1759 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1760 {
1761 CloseClient(con);
1762 return (0);
1763 }
1764 }
1765
1766 /*
1767 * Install the configuration file...
1768 */
1769
1770 status = install_conf_file(con);
1771
1772 /*
1773 * Return the status to the client...
1774 */
1775
1776 if (!SendError(con, status))
1777 {
1778 CloseClient(con);
1779 return (0);
1780 }
1781 }
a74b005d 1782 break;
1783
1784 case HTTP_POST_RECV :
58e8cf34 1785 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
93894a43 1786 con->http.fd,
a74b005d 1787 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
f63a2256 1788 con->http.data_remaining, con->file);
a74b005d 1789
b14d90ba 1790 if (con->request != NULL)
1791 {
1792 /*
1793 * Grab any request data from the connection...
1794 */
1795
bd176c9c 1796 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
1797 {
5ea8888e 1798 LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!",
bd176c9c 1799 con->http.fd);
3955a26e 1800
1801 if (!SendError(con, HTTP_BAD_REQUEST))
1802 {
1803 CloseClient(con);
1804 return (0);
1805 }
1806
1807 ShutdownClient(con);
1808 return (1);
bd176c9c 1809 }
1810 else if (ipp_state != IPP_DATA)
b14d90ba 1811 break;
68ff4f01 1812 else
1813 con->bytes += ippLength(con->request);
f63a2256 1814 }
b14d90ba 1815
f63a2256 1816 if (con->file == 0 && con->http.state != HTTP_POST_SEND)
1817 {
1818 /*
1819 * Create a file as needed for the request data...
1820 */
1d2c70a6 1821
fc757c63 1822 SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
f63a2256 1823 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1824 fchmod(con->file, 0640);
1825 fchown(con->file, User, Group);
b14d90ba 1826
a61a2be6 1827 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
1828 con->filename, con->file);
b14d90ba 1829
f63a2256 1830 if (con->file < 0)
1831 {
1832 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
b14d90ba 1833 {
f63a2256 1834 CloseClient(con);
1835 return (0);
b14d90ba 1836 }
1837 }
f63a2256 1838 }
b14d90ba 1839
bfa1abf0 1840 if (con->http.state != HTTP_POST_SEND)
a74b005d 1841 {
bfa1abf0 1842 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
a74b005d 1843 {
bfa1abf0 1844 CloseClient(con);
1845 return (0);
1846 }
1847 else if (bytes > 0)
1848 {
1849 con->bytes += bytes;
a74b005d 1850
a61a2be6 1851 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
1852 con->http.fd, bytes, con->file);
bfa1abf0 1853
1854 if (write(con->file, line, bytes) < bytes)
a74b005d 1855 {
a61a2be6 1856 LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s",
1857 bytes, con->filename, strerror(errno));
1858
6cd03542 1859 LogMessage(L_DEBUG2, "ReadClient: Closing file %d...",
1860 con->file);
1861
bfa1abf0 1862 close(con->file);
1863 con->file = 0;
1864 unlink(con->filename);
fc757c63 1865 ClearString(&con->filename);
bfa1abf0 1866
1867 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1868 {
1869 CloseClient(con);
1870 return (0);
1871 }
a74b005d 1872 }
1873 }
6faac3f3 1874 else if (con->http.state == HTTP_POST_RECV)
1875 {
1876 return (0);
1877 }
bd176c9c 1878 else if (con->http.state != HTTP_POST_SEND)
1879 {
1880 CloseClient(con);
1881 return (0);
1882 }
a74b005d 1883 }
93894a43 1884
1885 if (con->http.state == HTTP_POST_SEND)
1886 {
bfa1abf0 1887 if (con->file)
1888 {
8b43895a 1889 fstat(con->file, &filestats);
bd917997 1890
1891 LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
aa7e125a 1892 con->http.fd, con->file, (int)filestats.st_size);
bd917997 1893
bfa1abf0 1894 close(con->file);
1895 con->file = 0;
8b43895a 1896
1897 if (filestats.st_size > MaxRequestSize &&
1898 MaxRequestSize > 0)
1899 {
1900 /*
1901 * Request is too big; remove it and send an error...
1902 */
1903
a61a2be6 1904 LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
1905 con->http.fd, con->filename);
8b43895a 1906 unlink(con->filename);
fc757c63 1907 ClearString(&con->filename);
8b43895a 1908
1909 if (con->request)
1910 {
1911 /*
1912 * Delete any IPP request data...
1913 */
1914
1915 ippDelete(con->request);
1916 con->request = NULL;
1917 }
1918
1919 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1920 {
1921 CloseClient(con);
1922 return (0);
1923 }
1924 }
f63a2256 1925
fc757c63 1926 if (con->command)
f63a2256 1927 {
1928 if (!SendCommand(con, con->command, con->options))
1929 {
1930 if (!SendError(con, HTTP_NOT_FOUND))
1931 {
1932 CloseClient(con);
1933 return (0);
1934 }
1935 }
1936 else
1937 LogRequest(con, HTTP_OK);
1938 }
bfa1abf0 1939 }
93894a43 1940
1941 if (con->request)
6e3d4276 1942 return (ProcessIPPRequest(con));
93894a43 1943 }
a74b005d 1944 break;
d21a7597 1945
1946 default :
1947 break; /* Anti-compiler-warning-code */
a74b005d 1948 }
1949
1950 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
1951 {
1952 CloseClient(con);
1953 return (0);
1954 }
1955 else
1956 return (1);
1957}
1958
1959
a74b005d 1960/*
1961 * 'SendCommand()' - Send output from a command via HTTP.
1962 */
1963
1964int
1965SendCommand(client_t *con,
a74b005d 1966 char *command,
b14d90ba 1967 char *options)
a74b005d 1968{
f63a2256 1969 int fd;
1970
1971
fc757c63 1972 if (con->filename)
f63a2256 1973 fd = open(con->filename, O_RDONLY);
1974 else
1975 fd = open("/dev/null", O_RDONLY);
1976
1977 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
1978
1979 close(fd);
a74b005d 1980
b5cb0608 1981 LogMessage(L_INFO, "Started \"%s\" (pid=%d)", command, con->pipe_pid);
1982
1983 LogMessage(L_DEBUG, "SendCommand() %d file=%d", con->http.fd, con->file);
a74b005d 1984
1985 if (con->pipe_pid == 0)
1986 return (0);
1987
1988 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1989
27eba2dd 1990 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to InputSet...", con->file);
e668e627 1991 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to OutputSet...",
1992 con->http.fd);
1993
f3bc1068 1994 FD_SET(con->file, InputSet);
1995 FD_SET(con->http.fd, OutputSet);
a74b005d 1996
96df88bb 1997 if (!SendHeader(con, HTTP_OK, NULL))
a74b005d 1998 return (0);
1999
2000 if (con->http.version == HTTP_1_1)
2001 {
2002 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
2003
2004 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
2005 return (0);
2006 }
2007
13486cb7 2008 con->got_fields = 0;
2009 con->field_col = 0;
2010
a74b005d 2011 return (1);
2012}
2013
2014
2015/*
2016 * 'SendError()' - Send an error message via HTTP.
2017 */
2018
2019int /* O - 1 if successful, 0 otherwise */
2020SendError(client_t *con, /* I - Connection */
2021 http_status_t code) /* I - Error code */
2022{
83e740a5 2023 char message[1024]; /* Message for user */
a74b005d 2024
2025
6a0c519d 2026 /*
2027 * Put the request in the access_log file...
2028 */
2029
993e15da 2030 if (con->operation > HTTP_WAITING)
2031 LogRequest(con, code);
6a0c519d 2032
753453e4 2033 LogMessage(L_DEBUG, "SendError() %d code=%d", con->http.fd, code);
a61a2be6 2034
a74b005d 2035 /*
2036 * To work around bugs in some proxies, don't use Keep-Alive for some
2037 * error messages...
2038 */
2039
2040 if (code >= HTTP_BAD_REQUEST)
2041 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
2042
2043 /*
2044 * Send an error message back to the client. If the error code is a
2045 * 400 or 500 series, make sure the message contains some text, too!
2046 */
2047
2048 if (!SendHeader(con, code, NULL))
2049 return (0);
2050
bcf61448 2051#ifdef HAVE_SSL
a75c006a 2052 if (code == HTTP_UPGRADE_REQUIRED)
a75c006a 2053 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
2054 return (0);
2055
b5cb0608 2056 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0)
2057 return (0);
bcf61448 2058#endif /* HAVE_SSL */
a75c006a 2059
55809c6f 2060 if ((con->http.version >= HTTP_1_1 && !con->http.keep_alive) ||
2061 (code >= HTTP_BAD_REQUEST && code != HTTP_UPGRADE_REQUIRED))
a74b005d 2062 {
2063 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
2064 return (0);
2065 }
2066
2067 if (code >= HTTP_BAD_REQUEST)
2068 {
2069 /*
2070 * Send a human-readable error message.
2071 */
2072
04de52f8 2073 snprintf(message, sizeof(message),
970017a4 2074 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
2075 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
2076 code, httpStatus(code), httpStatus(code),
2077 con->language ? con->language->messages[code] :
2078 httpStatus(code));
a74b005d 2079
2080 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
2081 return (0);
e0ba31cd 2082 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
2083 (int)strlen(message)) < 0)
a74b005d 2084 return (0);
2085 if (httpPrintf(HTTP(con), "\r\n") < 0)
2086 return (0);
97d73ddb 2087 if (httpPrintf(HTTP(con), "%s", message) < 0)
a74b005d 2088 return (0);
2089 }
2090 else if (httpPrintf(HTTP(con), "\r\n") < 0)
2091 return (0);
2092
2093 con->http.state = HTTP_WAITING;
2094
2095 return (1);
2096}
2097
2098
2099/*
2100 * 'SendFile()' - Send a file via HTTP.
2101 */
2102
2103int
2104SendFile(client_t *con,
2105 http_status_t code,
2106 char *filename,
2107 char *type,
2108 struct stat *filestats)
2109{
2110 con->file = open(filename, O_RDONLY);
2111
5ea8888e 2112 LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
a74b005d 2113
2114 if (con->file < 0)
2115 return (0);
2116
2117 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2118
2119 con->pipe_pid = 0;
2120
2121 if (!SendHeader(con, code, type))
2122 return (0);
2123
2124 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
2125 return (0);
753453e4 2126 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
2127 (unsigned long)filestats->st_size) < 0)
a74b005d 2128 return (0);
2129 if (httpPrintf(HTTP(con), "\r\n") < 0)
2130 return (0);
2131
e668e627 2132 LogMessage(L_DEBUG2, "SendFile: Adding fd %d to OutputSet...", con->http.fd);
2133
f3bc1068 2134 FD_SET(con->http.fd, OutputSet);
a74b005d 2135
2136 return (1);
2137}
2138
2139
2140/*
93894a43 2141 * 'SendHeader()' - Send an HTTP request.
a74b005d 2142 */
2143
2144int /* O - 1 on success, 0 on failure */
2145SendHeader(client_t *con, /* I - Client to send to */
2146 http_status_t code, /* I - HTTP status code */
2147 char *type) /* I - MIME type of document */
2148{
83e740a5 2149 location_t *loc; /* Authentication location */
2150
2151
a74b005d 2152 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
ffe90a99 2153 con->http.version % 100, code, httpStatus(code)) < 0)
a74b005d 2154 return (0);
2155 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
2156 return (0);
bd84e0d1 2157 if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
a74b005d 2158 return (0);
2159 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
2160 {
2161 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
2162 return (0);
2163 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
2164 return (0);
2165 }
8f85ff77 2166 if (code == HTTP_METHOD_NOT_ALLOWED)
9bfcbffc 2167 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
8f85ff77 2168 return (0);
2169
5ea8888e 2170 if (code == HTTP_UNAUTHORIZED)
83e740a5 2171 {
753453e4 2172 /*
2173 * This already succeeded in IsAuthorized...
2174 */
2175
2176 loc = FindBest(con->uri, con->http.state);
83e740a5 2177
753453e4 2178 if (loc->type != AUTH_DIGEST)
83e740a5 2179 {
2180 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
2181 return (0);
2182 }
2183 else
2184 {
2185 if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" "
2186 "nonce=\"%s\"\r\n", con->http.hostname) < 0)
2187 return (0);
2188 }
2189 }
a74b005d 2190 if (con->language != NULL)
2191 {
2192 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
2193 con->language->language) < 0)
2194 return (0);
2195
2196 if (type != NULL)
2197 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
2198 cupsLangEncoding(con->language)) < 0)
2199 return (0);
2200 }
2201 else if (type != NULL)
2202 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
2203 return (0);
2204
2205 return (1);
2206}
2207
2208
c3c5af5e 2209/*
2210 * 'ShutdownClient()' - Shutdown the receiving end of a connection.
2211 */
2212
2213void
2214ShutdownClient(client_t *con) /* I - Client connection */
2215{
2216 /*
2217 * Shutdown the receiving end of the socket, since the client
2218 * still needs to read the error message...
2219 */
2220
2221 shutdown(con->http.fd, 0);
549d1888 2222 con->http.used = 0;
c3c5af5e 2223
de218675 2224 /*
2225 * Update the activity time so that we timeout after 30 seconds rather
2226 * then the current Timeout setting (300 by default). This prevents
2227 * some DoS situations...
2228 */
2229
2230 con->http.activity = time(NULL) - Timeout + 30;
2231
c3c5af5e 2232 LogMessage(L_DEBUG2, "ShutdownClient: Removing fd %d from InputSet...",
2233 con->http.fd);
2234
f3bc1068 2235 FD_CLR(con->http.fd, InputSet);
c3c5af5e 2236}
2237
2238
0a968cfb 2239/*
2240 * 'UpdateCGI()' - Read status messages from CGI scripts and programs.
2241 */
2242
2243void
2244UpdateCGI(void)
2245{
2246 int bytes; /* Number of bytes read */
2247 char *lineptr, /* Pointer to end of line in buffer */
2248 *message; /* Pointer to message text */
2249 int loglevel; /* Log level for message */
2250 static int bufused = 0; /* Number of bytes used in buffer */
2251 static char buffer[1024]; /* Status buffer */
2252
2253
2254 if ((bytes = read(CGIPipes[0], buffer + bufused,
2255 sizeof(buffer) - bufused - 1)) > 0)
2256 {
2257 bufused += bytes;
2258 buffer[bufused] = '\0';
2259 lineptr = strchr(buffer, '\n');
2260 }
2261 else if (bytes < 0 && errno == EINTR)
2262 return;
2263 else
2264 {
2265 lineptr = buffer + bufused;
2266 lineptr[1] = 0;
2267 }
2268
2269 if (bytes == 0 && bufused == 0)
2270 lineptr = NULL;
2271
2272 while (lineptr != NULL)
2273 {
2274 /*
2275 * Terminate each line and process it...
2276 */
2277
2278 *lineptr++ = '\0';
2279
2280 /*
2281 * Figure out the logging level...
2282 */
2283
2284 if (strncmp(buffer, "EMERG:", 6) == 0)
2285 {
2286 loglevel = L_EMERG;
2287 message = buffer + 6;
2288 }
2289 else if (strncmp(buffer, "ALERT:", 6) == 0)
2290 {
2291 loglevel = L_ALERT;
2292 message = buffer + 6;
2293 }
2294 else if (strncmp(buffer, "CRIT:", 5) == 0)
2295 {
2296 loglevel = L_CRIT;
2297 message = buffer + 5;
2298 }
2299 else if (strncmp(buffer, "ERROR:", 6) == 0)
2300 {
2301 loglevel = L_ERROR;
2302 message = buffer + 6;
2303 }
2304 else if (strncmp(buffer, "WARNING:", 8) == 0)
2305 {
2306 loglevel = L_WARN;
2307 message = buffer + 8;
2308 }
2309 else if (strncmp(buffer, "NOTICE:", 6) == 0)
2310 {
2311 loglevel = L_NOTICE;
2312 message = buffer + 6;
2313 }
2314 else if (strncmp(buffer, "INFO:", 5) == 0)
2315 {
2316 loglevel = L_INFO;
2317 message = buffer + 5;
2318 }
2319 else if (strncmp(buffer, "DEBUG:", 6) == 0)
2320 {
2321 loglevel = L_DEBUG;
2322 message = buffer + 6;
2323 }
2324 else if (strncmp(buffer, "DEBUG2:", 7) == 0)
2325 {
2326 loglevel = L_DEBUG2;
2327 message = buffer + 7;
2328 }
2329 else if (strncmp(buffer, "PAGE:", 5) == 0)
2330 {
2331 loglevel = L_PAGE;
2332 message = buffer + 5;
2333 }
2334 else
2335 {
2336 loglevel = L_DEBUG;
2337 message = buffer;
2338 }
2339
2340 /*
2341 * Skip leading whitespace in the message...
2342 */
2343
2344 while (isspace(*message))
2345 message ++;
2346
2347 LogMessage(loglevel, "[CGI] %s", message);
2348
2349 /*
2350 * Copy over the buffer data we've used up...
2351 */
2352
2353 strcpy(buffer, lineptr);
2354 bufused -= lineptr - buffer;
2355
2356 if (bufused < 0)
2357 bufused = 0;
2358
2359 lineptr = strchr(buffer, '\n');
2360 }
2361
2362 if (bytes <= 0)
2363 {
2364 /*
2365 * Fatal error on pipe - should never happen!
2366 */
2367
2368 LogMessage(L_ERROR, "UpdateCGI: error reading from CGI error pipe - %s",
2369 strerror(errno));
2370 }
2371}
2372
2373
a74b005d 2374/*
2375 * 'WriteClient()' - Write data to a client as needed.
2376 */
2377
ff49100f 2378int /* O - 1 if success, 0 if fail */
2379WriteClient(client_t *con) /* I - Client connection */
a74b005d 2380{
ff49100f 2381 int bytes; /* Number of bytes written */
13486cb7 2382 char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */
2383 char *bufptr; /* Pointer into buffer */
ff49100f 2384 ipp_state_t ipp_state; /* IPP state value */
a74b005d 2385
2386
2387 if (con->http.state != HTTP_GET_SEND &&
2388 con->http.state != HTTP_POST_SEND)
2389 return (1);
2390
ff49100f 2391 if (con->response != NULL)
bfa1abf0 2392 {
ff49100f 2393 ipp_state = ippWrite(&(con->http), con->response);
2394 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
bfa1abf0 2395 }
13486cb7 2396 else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
a74b005d 2397 {
13486cb7 2398 if (con->pipe_pid && !con->got_fields)
2399 {
2400 /*
2401 * Inspect the data for Content-Type and other fields.
2402 */
2403
2404 buf[bytes] = '\0';
2405
2406 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
2407 if (*bufptr == '\n')
2408 {
2409 /*
2410 * Send line to client...
2411 */
2412
2413 if (bufptr > buf && bufptr[-1] == '\r')
2414 bufptr[-1] = '\0';
2415 *bufptr++ = '\0';
2416
2417 httpPrintf(HTTP(con), "%s\r\n", buf);
58e8cf34 2418 LogMessage(L_DEBUG2, "WriteClient() %d %s", con->http.fd, buf);
13486cb7 2419
2420 /*
2421 * Update buffer...
2422 */
2423
2424 bytes -= (bufptr - buf);
2425 memcpy(buf, bufptr, bytes + 1);
2426 bufptr = buf - 1;
2427
2428 /*
2429 * See if the line was empty...
2430 */
2431
2432 if (con->field_col == 0)
2433 con->got_fields = 1;
2434 else
2435 con->field_col = 0;
2436 }
2437 else if (*bufptr != '\r')
2438 con->field_col ++;
2439
2440 if (bytes > 0 && !con->got_fields)
2441 {
2442 /*
2443 * Remaining text needs to go out...
2444 */
2445
2446 httpPrintf(HTTP(con), "%s", buf);
2447
2448 con->http.activity = time(NULL);
2449 return (1);
2450 }
2451 else if (bytes == 0)
2452 {
2453 con->http.activity = time(NULL);
2454 return (1);
2455 }
2456 }
2457
a74b005d 2458 if (httpWrite(HTTP(con), buf, bytes) < 0)
2459 {
2460 CloseClient(con);
2461 return (0);
2462 }
6a0c519d 2463
2464 con->bytes += bytes;
a74b005d 2465 }
b14d90ba 2466
2467 if (bytes <= 0)
a74b005d 2468 {
6a0c519d 2469 LogRequest(con, HTTP_OK);
2470
a74b005d 2471 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
2472 {
2473 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
2474 {
2475 CloseClient(con);
2476 return (0);
2477 }
2478 }
2479
96df88bb 2480 con->http.state = HTTP_WAITING;
2481
e668e627 2482 LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from OutputSet...",
2483 con->http.fd);
2484
f3bc1068 2485 FD_CLR(con->http.fd, OutputSet);
a74b005d 2486
bfa1abf0 2487 if (con->file)
a74b005d 2488 {
27eba2dd 2489 LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from InputSet...",
2490 con->file);
f3bc1068 2491 FD_CLR(con->file, InputSet);
a74b005d 2492
bfa1abf0 2493 if (con->pipe_pid)
96df88bb 2494 kill(con->pipe_pid, SIGTERM);
bfa1abf0 2495
bd917997 2496 LogMessage(L_DEBUG2, "WriteClient() %d Closing data file %d.",
2497 con->http.fd, con->file);
2498
bfa1abf0 2499 close(con->file);
96df88bb 2500 con->file = 0;
2501 con->pipe_pid = 0;
bfa1abf0 2502 }
a74b005d 2503
fc757c63 2504 if (con->filename)
a61a2be6 2505 {
2506 LogMessage(L_DEBUG2, "WriteClient() %d Removing temp file %s",
bd917997 2507 con->http.fd, con->filename);
d4c438d4 2508 unlink(con->filename);
fc757c63 2509 ClearString(&con->filename);
a61a2be6 2510 }
d4c438d4 2511
b14d90ba 2512 if (con->request != NULL)
2513 {
2514 ippDelete(con->request);
2515 con->request = NULL;
2516 }
2517
2518 if (con->response != NULL)
2519 {
2520 ippDelete(con->response);
2521 con->response = NULL;
2522 }
96df88bb 2523
fc757c63 2524 ClearString(&con->command);
2525 ClearString(&con->options);
2526
96df88bb 2527 if (!con->http.keep_alive)
2528 {
2529 CloseClient(con);
2530 return (0);
2531 }
a74b005d 2532 }
2533
997edb40 2534 if (bytes >= 1024)
58e8cf34 2535 LogMessage(L_DEBUG2, "WriteClient() %d %d bytes", con->http.fd, bytes);
a74b005d 2536
2537 con->http.activity = time(NULL);
2538
2539 return (1);
2540}
2541
2542
a74b005d 2543/*
2544 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
2545 */
2546
2547static int /* O - 1 if modified since */
2548check_if_modified(client_t *con, /* I - Client connection */
2549 struct stat *filestats) /* I - File information */
2550{
2551 char *ptr; /* Pointer into field */
2552 time_t date; /* Time/date value */
2553 int size; /* Size/length value */
2554
2555
2556 size = 0;
2557 date = 0;
2558 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
2559
2560 if (*ptr == '\0')
2561 return (1);
2562
58e8cf34 2563 LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"",
4a64fdb7 2564 con->http.fd, ptr);
2565
a74b005d 2566 while (*ptr != '\0')
2567 {
4a64fdb7 2568 while (isspace(*ptr) || *ptr == ';')
a74b005d 2569 ptr ++;
2570
2571 if (strncasecmp(ptr, "length=", 7) == 0)
2572 {
2573 ptr += 7;
2574 size = atoi(ptr);
2575
2576 while (isdigit(*ptr))
2577 ptr ++;
2578 }
4a64fdb7 2579 else if (isalpha(*ptr))
a74b005d 2580 {
2581 date = httpGetDateTime(ptr);
4a64fdb7 2582 while (*ptr != '\0' && *ptr != ';')
a74b005d 2583 ptr ++;
2584 }
2585 }
2586
58e8cf34 2587 LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
aa7e125a 2588 con->http.fd, size, (int)filestats->st_size, (int)date,
2589 (int)filestats->st_mtime);
4a64fdb7 2590
a74b005d 2591 return ((size != filestats->st_size && size != 0) ||
4a64fdb7 2592 (date < filestats->st_mtime && date != 0) ||
2593 (size == 0 && date == 0));
a74b005d 2594}
2595
2596
2597/*
d2122fde 2598 * 'decode_auth()' - Decode an authorization string.
a74b005d 2599 */
2600
93894a43 2601static void
d2122fde 2602decode_auth(client_t *con) /* I - Client to decode to */
a74b005d 2603{
d2122fde 2604 char *s, /* Authorization string */
2605 value[1024]; /* Value string */
2606 const char *username; /* Certificate username */
a74b005d 2607
a74b005d 2608
2609 /*
b0d58d07 2610 * Decode the string...
a74b005d 2611 */
2612
f3d580b9 2613 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
f3d580b9 2614
aa7e125a 2615 LogMessage(L_DEBUG2, "decode_auth(%p): Authorization string = \"%s\"",
27eba2dd 2616 con, s);
69f14ebb 2617
d2122fde 2618 if (strncmp(s, "Basic", 5) == 0)
2619 {
2620 s += 5;
2621 while (isspace(*s))
2622 s ++;
f3d580b9 2623
d2122fde 2624 httpDecode64(value, s);
a74b005d 2625
d2122fde 2626 /*
2627 * Pull the username and password out...
2628 */
b0d58d07 2629
d2122fde 2630 if ((s = strchr(value, ':')) == NULL)
2631 {
5ea8888e 2632 LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
d2122fde 2633 con->http.fd, value);
2634 return;
2635 }
2636
2637 *s++ = '\0';
b0d58d07 2638
def978d5 2639 strlcpy(con->username, value, sizeof(con->username));
2640 strlcpy(con->password, s, sizeof(con->password));
d2122fde 2641 }
2642 else if (strncmp(s, "Local", 5) == 0)
2643 {
2644 s += 5;
2645 while (isspace(*s))
2646 s ++;
b0d58d07 2647
d2122fde 2648 if ((username = FindCert(s)) != NULL)
def978d5 2649 strlcpy(con->username, username, sizeof(con->username));
d2122fde 2650 }
83e740a5 2651 else if (strncmp(s, "Digest", 5) == 0)
2652 {
2653 /*
2654 * Get the username and password from the Digest attributes...
2655 */
2656
2657 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username",
2658 value))
def978d5 2659 strlcpy(con->username, value, sizeof(con->username));
83e740a5 2660
2661 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response",
2662 value))
def978d5 2663 strlcpy(con->password, value, sizeof(con->password) - 1);
83e740a5 2664 }
41637133 2665
27eba2dd 2666 LogMessage(L_DEBUG2, "decode_auth() %d username=\"%s\"",
41637133 2667 con->http.fd, con->username);
a74b005d 2668}
2669
2670
e31bfb6e 2671/*
2672 * 'get_file()' - Get a filename and state info.
2673 */
2674
6db7190f 2675static char * /* O - Real filename */
2676get_file(client_t *con, /* I - Client connection */
2677 struct stat *filestats,/* O - File information */
2678 char *filename, /* IO - Filename buffer */
2679 int len) /* I - Buffer length */
e31bfb6e 2680{
2681 int status; /* Status of filesystem calls */
2682 char *params; /* Pointer to parameters in URI */
e31bfb6e 2683
2684
2685 /*
2686 * Need to add DocumentRoot global...
2687 */
2688
f3d580b9 2689 if (strncmp(con->uri, "/ppd/", 5) == 0)
6db7190f 2690 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
753453e4 2691 else if (strncmp(con->uri, "/admin/conf/", 12) == 0)
6db7190f 2692 snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
f3d580b9 2693 else if (con->language != NULL)
6db7190f 2694 snprintf(filename, len, "%s/%s%s", DocumentRoot, con->language->language,
e31bfb6e 2695 con->uri);
2696 else
6db7190f 2697 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
e31bfb6e 2698
2699 if ((params = strchr(filename, '?')) != NULL)
2700 *params = '\0';
2701
2702 /*
2703 * Grab the status for this language; if there isn't a language-specific file
2704 * then fallback to the default one...
2705 */
2706
2707 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
2708 {
2709 /*
2710 * Drop the language prefix and try the current directory...
2711 */
2712
753453e4 2713 if (strncmp(con->uri, "/ppd/", 5) != 0 &&
2714 strncmp(con->uri, "/admin/conf/", 12) != 0)
f3d580b9 2715 {
6db7190f 2716 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
e31bfb6e 2717
f3d580b9 2718 status = stat(filename, filestats);
2719 }
e31bfb6e 2720 }
2721
2722 /*
2723 * If we're found a directory, get the index.html file instead...
2724 */
2725
2726 if (!status && S_ISDIR(filestats->st_mode))
2727 {
2728 if (filename[strlen(filename) - 1] == '/')
6db7190f 2729 strlcat(filename, "index.html", len);
e31bfb6e 2730 else
6db7190f 2731 strlcat(filename, "/index.html", len);
e31bfb6e 2732
2733 status = stat(filename, filestats);
2734 }
2735
58e8cf34 2736 LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d",
aa7e125a 2737 con->http.fd, filename, status ? -1 : (int)filestats->st_size);
e31bfb6e 2738
2739 if (status)
2740 return (NULL);
2741 else
2742 return (filename);
2743}
2744
2745
753453e4 2746/*
2747 * 'install_conf_file()' - Install a configuration file.
2748 */
2749
2750static http_status_t /* O - Status */
2751install_conf_file(client_t *con) /* I - Connection */
2752{
7b0fde61 2753 cups_file_t *in, /* Input file */
753453e4 2754 *out; /* Output file */
2755 char buffer[1024]; /* Copy buffer */
2756 int bytes; /* Number of bytes */
2757 char conffile[1024], /* Configuration filename */
2758 newfile[1024], /* New config filename */
2759 oldfile[1024]; /* Old config filename */
2760 struct stat confinfo; /* Config file info */
2761
2762
2763 /*
2764 * First construct the filenames...
2765 */
2766
2767 snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11);
2768 snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11);
2769 snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11);
2770
2771 LogMessage(L_INFO, "Installing config file \"%s\"...", conffile);
2772
2773 /*
2774 * Get the owner, group, and permissions of the configuration file.
2775 * If it doesn't exist, assign it to the User and Group in the
2776 * cupsd.conf file with mode 0640 permissions.
2777 */
2778
2779 if (stat(conffile, &confinfo))
2780 {
2781 confinfo.st_uid = User;
2782 confinfo.st_gid = Group;
d59a189c 2783 confinfo.st_mode = ConfigFilePerm;
753453e4 2784 }
2785
2786 /*
2787 * Open the request file and new config file...
2788 */
2789
7b0fde61 2790 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
753453e4 2791 {
2792 LogMessage(L_ERROR, "Unable to open request file \"%s\" - %s",
2793 con->filename, strerror(errno));
2794 return (HTTP_SERVER_ERROR);
2795 }
2796
7b0fde61 2797 if ((out = cupsFileOpen(newfile, "wb")) == NULL)
753453e4 2798 {
7b0fde61 2799 cupsFileClose(in);
753453e4 2800 LogMessage(L_ERROR, "Unable to open config file \"%s\" - %s",
2801 newfile, strerror(errno));
2802 return (HTTP_SERVER_ERROR);
2803 }
2804
7b0fde61 2805 fchmod(cupsFileNumber(out), confinfo.st_mode);
2806 fchown(cupsFileNumber(out), confinfo.st_uid, confinfo.st_gid);
753453e4 2807
2808 /*
2809 * Copy from the request to the new config file...
2810 */
2811
7b0fde61 2812 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
2813 if (cupsFileWrite(out, buffer, bytes) < bytes)
753453e4 2814 {
2815 LogMessage(L_ERROR, "Unable to copy to config file \"%s\" - %s",
2816 newfile, strerror(errno));
2817
7b0fde61 2818 cupsFileClose(in);
2819 cupsFileClose(out);
753453e4 2820 unlink(newfile);
2821
2822 return (HTTP_SERVER_ERROR);
2823 }
2824
2825 /*
2826 * Close the files...
2827 */
2828
7b0fde61 2829 cupsFileClose(in);
2830 if (cupsFileClose(out))
753453e4 2831 {
2832 LogMessage(L_ERROR, "Error file closing config file \"%s\" - %s",
2833 newfile, strerror(errno));
2834
2835 unlink(newfile);
2836
2837 return (HTTP_SERVER_ERROR);
2838 }
2839
2840 /*
2841 * Remove the request file...
2842 */
2843
2844 unlink(con->filename);
fc757c63 2845 ClearString(&con->filename);
753453e4 2846
2847 /*
2848 * Unlink the old backup, rename the current config file to the backup
2849 * filename, and rename the new config file to the config file name...
2850 */
2851
2852 if (unlink(oldfile))
2853 if (errno != ENOENT)
2854 {
2855 LogMessage(L_ERROR, "Unable to remove backup config file \"%s\" - %s",
2856 oldfile, strerror(errno));
2857
2858 unlink(newfile);
2859
2860 return (HTTP_SERVER_ERROR);
2861 }
2862
2863 if (rename(conffile, oldfile))
2864 if (errno != ENOENT)
2865 {
2866 LogMessage(L_ERROR, "Unable to rename old config file \"%s\" - %s",
2867 conffile, strerror(errno));
2868
2869 unlink(newfile);
2870
2871 return (HTTP_SERVER_ERROR);
2872 }
2873
2874 if (rename(newfile, conffile))
2875 {
2876 LogMessage(L_ERROR, "Unable to rename new config file \"%s\" - %s",
2877 newfile, strerror(errno));
2878
2879 rename(oldfile, conffile);
2880 unlink(newfile);
2881
2882 return (HTTP_SERVER_ERROR);
2883 }
2884
2885 /*
2886 * If the cupsd.conf file was updated, set the NeedReload flag...
2887 */
2888
2889 if (strcmp(con->uri, "/admin/conf/cupsd.conf") == 0)
53510eae 2890 NeedReload = RELOAD_CUPSD;
2891 else
2892 NeedReload = RELOAD_ALL;
753453e4 2893
2894 /*
2895 * Return that the file was created successfully...
2896 */
2897
2898 return (HTTP_CREATED);
2899}
2900
2901
a74b005d 2902/*
2903 * 'pipe_command()' - Pipe the output of a command to the remote client.
2904 */
2905
879062a9 2906static int /* O - Process ID */
2907pipe_command(client_t *con, /* I - Client connection */
2908 int infile, /* I - Standard input for command */
2909 int *outfile, /* O - Standard output for command */
2910 char *command, /* I - Command to run */
2911 char *options) /* I - Options for command */
a74b005d 2912{
2ae14c39 2913 int i; /* Looping var */
879062a9 2914 int pid; /* Process ID */
2915 char *commptr; /* Command string pointer */
e420f467 2916 char *uriptr; /* URI string pointer */
879062a9 2917 int fd; /* Looping var */
2918 int fds[2]; /* Pipe FDs */
2919 int argc; /* Number of arguments */
2920 int envc; /* Number of environment variables */
2921 char argbuf[10240], /* Argument buffer */
2922 *argv[100], /* Argument strings */
2923 *envp[100]; /* Environment variables */
2ae14c39 2924 char content_length[1024], /* CONTENT_LENGTH environment variable */
2925 content_type[1024], /* CONTENT_TYPE environment variable */
2926 cups_datadir[1024], /* CUPS_DATADIR environment variable */
2927 cups_serverroot[1024], /* CUPS_SERVERROOT environment variable */
2928 http_cookie[1024], /* HTTP_COOKIE environment variable */
2929 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
2930 ipp_port[1024], /* IPP_PORT environment variable */
2931 lang[1024], /* LANG environment variable */
2932 ld_library_path[1024], /* LD_LIBRARY_PATH environment variable */
dd63ebe2 2933 ld_preload[1024], /* LD_PRELOAD environment variable */
2934 dyld_library_path[1024],/* DYLD_LIBRARY_PATH environment variable */
2935 shlib_path[1024], /* SHLIB_PATH environment variable */
879062a9 2936 nlspath[1024], /* NLSPATH environment variable */
2ae14c39 2937 query_string[10240], /* QUERY_STRING env variable */
2938 remote_addr[1024], /* REMOTE_ADDR environment variable */
2939 remote_host[1024], /* REMOTE_HOST environment variable */
2940 remote_user[1024], /* REMOTE_USER environment variable */
2941 script_name[1024], /* SCRIPT_NAME environment variable */
2942 server_name[1024], /* SERVER_NAME environment variable */
2943 server_port[1024], /* SERVER_PORT environment variable */
2944 tmpdir[1024]; /* TMPDIR environment variable */
879062a9 2945#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
c7a9d594 2946 struct sigaction action; /* POSIX signal handler */
879062a9 2947#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
a74b005d 2948
05e63c18 2949
a74b005d 2950 /*
2951 * Copy the command string...
2952 */
2953
def978d5 2954 strlcpy(argbuf, options, sizeof(argbuf));
a74b005d 2955
2956 /*
f63a2256 2957 * Parse the string; arguments can be separated by + and are terminated
2958 * by ?...
a74b005d 2959 */
2960
2961 argv[0] = argbuf;
2962
bfa1abf0 2963 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
f63a2256 2964 if (*commptr == ' ' || *commptr == '+')
a74b005d 2965 {
2966 *commptr++ = '\0';
2967
2968 while (*commptr == ' ')
2969 commptr ++;
2970
2971 if (*commptr != '\0')
2972 {
2973 argv[argc] = commptr;
2974 argc ++;
2975 }
2976
2977 commptr --;
2978 }
2979 else if (*commptr == '%')
2980 {
2981 if (commptr[1] >= '0' && commptr[1] <= '9')
2982 *commptr = (commptr[1] - '0') << 4;
2983 else
2984 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
2985
2986 if (commptr[2] >= '0' && commptr[2] <= '9')
2987 *commptr |= commptr[2] - '0';
2988 else
2989 *commptr |= tolower(commptr[2]) - 'a' + 10;
2990
2991 strcpy(commptr + 1, commptr + 3);
2992 }
f63a2256 2993 else if (*commptr == '?')
2994 break;
a74b005d 2995
2996 argv[argc] = NULL;
2997
bfa1abf0 2998 if (argv[0][0] == '\0')
2999 argv[0] = strrchr(command, '/') + 1;
b14d90ba 3000
3001 /*
3002 * Setup the environment variables as needed...
3003 */
3004
a6988fb1 3005 snprintf(lang, sizeof(lang), "LANG=%s",
3006 con->language ? con->language->language : "C");
0f71c41e 3007 sprintf(ipp_port, "IPP_PORT=%d", LocalPort);
99de6da0 3008#ifdef AF_INET6
3009 if (con->http.hostaddr.addr.sa_family == AF_INET6)
99de6da0 3010 sprintf(server_port, "SERVER_PORT=%d",
3011 ntohs(con->http.hostaddr.ipv6.sin6_port));
99de6da0 3012 else
3013#endif /* AF_INET6 */
99de6da0 3014 sprintf(server_port, "SERVER_PORT=%d",
3015 ntohs(con->http.hostaddr.ipv4.sin_port));
99de6da0 3016
c5d918d8 3017 if (strcmp(con->http.hostname, "localhost") == 0)
3018 strlcpy(server_name, "SERVER_NAME=localhost", sizeof(server_name));
3019 else
3020 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName);
04de52f8 3021 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname);
2ae14c39 3022 strcpy(remote_addr, "REMOTE_ADDR=");
3023 httpAddrString(&(con->http.hostaddr), remote_addr + 12,
3024 sizeof(remote_addr) - 12);
04de52f8 3025 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
3026 snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
2ae14c39 3027 snprintf(cups_datadir, sizeof(cups_datadir), "CUPS_DATADIR=%s", DataDir);
3028 snprintf(cups_serverroot, sizeof(cups_serverroot), "CUPS_SERVERROOT=%s", ServerRoot);
3029
3030 envc = 0;
3031
3032 envp[envc ++] = "PATH=/bin:/usr/bin";
3033 envp[envc ++] = "SERVER_SOFTWARE=CUPS/1.1";
3034 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
3035 if (con->http.version == HTTP_1_1)
3036 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
3037 else if (con->http.version == HTTP_1_0)
3038 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
3039 else
3040 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
3041 envp[envc ++] = "REDIRECT_STATUS=1";
3042 envp[envc ++] = ipp_port;
3043 envp[envc ++] = server_name;
3044 envp[envc ++] = server_port;
3045 envp[envc ++] = remote_addr;
3046 envp[envc ++] = remote_host;
3047 envp[envc ++] = remote_user;
3048 envp[envc ++] = lang;
3049 envp[envc ++] = TZ;
3050 envp[envc ++] = tmpdir;
3051 envp[envc ++] = cups_datadir;
3052 envp[envc ++] = cups_serverroot;
df3f73ff 3053
3054 if (getenv("LD_LIBRARY_PATH") != NULL)
2ae14c39 3055 {
3056 snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s",
1cf29e95 3057 getenv("LD_LIBRARY_PATH"));
2ae14c39 3058 envp[envc ++] = ld_library_path;
3059 }
dd63ebe2 3060
3061 if (getenv("LD_PRELOAD") != NULL)
3062 {
3063 snprintf(ld_preload, sizeof(ld_preload), "LD_PRELOAD=%s",
3064 getenv("LD_PRELOAD"));
3065 envp[envc ++] = ld_preload;
3066 }
3067
3068 if (getenv("DYLD_LIBRARY_PATH") != NULL)
2ae14c39 3069 {
dd63ebe2 3070 snprintf(dyld_library_path, sizeof(dyld_library_path), "DYLD_LIBRARY_PATH=%s",
1cf29e95 3071 getenv("DYLD_LIBRARY_PATH"));
dd63ebe2 3072 envp[envc ++] = dyld_library_path;
2ae14c39 3073 }
dd63ebe2 3074
3075 if (getenv("SHLIB_PATH") != NULL)
2ae14c39 3076 {
dd63ebe2 3077 snprintf(shlib_path, sizeof(shlib_path), "SHLIB_PATH=%s",
103abc03 3078 getenv("SHLIB_PATH"));
dd63ebe2 3079 envp[envc ++] = shlib_path;
2ae14c39 3080 }
7f0679f5 3081
1cf29e95 3082 if (getenv("NLSPATH") != NULL)
2ae14c39 3083 {
1cf29e95 3084 snprintf(nlspath, sizeof(nlspath), "NLSPATH=%s", getenv("NLSPATH"));
1cf29e95 3085 envp[envc ++] = nlspath;
2ae14c39 3086 }
3087
3088 if (con->http.cookie)
3089 {
3090 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
3091 con->http.cookie);
3092 envp[envc ++] = http_cookie;
3093 }
3094
3095 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
3096 {
3097 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
3098 con->http.fields[HTTP_FIELD_USER_AGENT]);
3099 envp[envc ++] = http_user_agent;
3100 }
3101
3102 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
e420f467 3103 if ((uriptr = strchr(script_name, '?')) != NULL)
3104 *uriptr = '\0';
2ae14c39 3105 envp[envc ++] = script_name;
1cf29e95 3106
b14d90ba 3107 if (con->operation == HTTP_GET)
3108 {
43ed090d 3109 for (i = 0; i < argc; i ++)
3110 LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
df3f73ff 3111 envp[envc ++] = "REQUEST_METHOD=GET";
f63a2256 3112
3113 if (*commptr)
3114 {
3115 /*
3116 * Add GET form variables after ?...
3117 */
3118
3119 *commptr++ = '\0';
3120
04de52f8 3121 snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
df3f73ff 3122 envp[envc ++] = query_string;
f63a2256 3123 }
b14d90ba 3124 }
3125 else
3126 {
f63a2256 3127 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
04de52f8 3128 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
970017a4 3129 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
b14d90ba 3130
df3f73ff 3131 envp[envc ++] = "REQUEST_METHOD=POST";
3132 envp[envc ++] = content_length;
3133 envp[envc ++] = content_type;
b14d90ba 3134 }
3135
30d60919 3136 /*
3137 * Tell the CGI if we are using encryption...
3138 */
3139
b92c5a4f 3140 if (con->http.encryption == HTTP_ENCRYPT_ALWAYS)
3141 {
30d60919 3142 envp[envc ++] = "HTTPS=ON";
b92c5a4f 3143 envp[envc ++] = "CUPS_ENCRYPTION=Always";
3144 }
30d60919 3145
df3f73ff 3146 envp[envc] = NULL;
3147
2ae14c39 3148 if (LogLevel == L_DEBUG2)
3149 {
3150 for (i = 0; i < argc; i ++)
3151 LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
3152 for (i = 0; i < envc; i ++)
3153 LogMessage(L_DEBUG2, "envp[%d] = \"%s\"", i, envp[i]);
3154 }
3155
a74b005d 3156 /*
3157 * Create a pipe for the output...
3158 */
3159
3160 if (pipe(fds))
1d6c68fb 3161 {
5ea8888e 3162 LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
1d6c68fb 3163 argv[0], strerror(errno));
a74b005d 3164 return (0);
1d6c68fb 3165 }
a74b005d 3166
879062a9 3167 /*
3168 * Block signals before forking...
3169 */
3170
38743560 3171 HoldSignals();
4d520ad4 3172
a74b005d 3173 /*
1d6c68fb 3174 * Then execute the command...
a74b005d 3175 */
3176
3177 if ((pid = fork()) == 0)
3178 {
3179 /*
3180 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
3181 */
3182
5aeb433c 3183 if (getuid() == 0)
3184 {
3185 /*
753453e4 3186 * Running as root, so change to a non-priviledged user...
5aeb433c 3187 */
3188
3189 if (setgid(Group))
3190 exit(errno);
3191
daf8f5f0 3192 if (setgroups(0, NULL))
3193 exit(errno);
3194
5aeb433c 3195 if (setuid(User))
3196 exit(errno);
3197 }
8914cc4b 3198 else
3199 {
3200 /*
3201 * Reset group membership to just the main one we belong to.
3202 */
5aeb433c 3203
8914cc4b 3204 setgroups(0, NULL);
3205 }
5aeb433c 3206
3207 /*
a9775e85 3208 * Update stdin/stdout/stderr...
5aeb433c 3209 */
b14d90ba 3210
a74b005d 3211 if (infile)
3212 {
3213 close(0);
753453e4 3214 if (dup(infile) < 0)
3215 exit(errno);
a74b005d 3216 }
3217
3218 close(1);
753453e4 3219 if (dup(fds[1]) < 0)
3220 exit(errno);
a74b005d 3221
a9775e85 3222 close(2);
0a968cfb 3223 dup(CGIPipes[1]);
a9775e85 3224
082b40d2 3225 /*
3226 * Close extra file descriptors...
3227 */
3228
93336dbc 3229 for (fd = 3; fd < MaxFDs; fd ++)
082b40d2 3230 close(fd);
3231
3232 /*
3233 * Change umask to restrict permissions on created files...
3234 */
3235
3236 umask(077);
a74b005d 3237
5f152161 3238 /*
3239 * Unblock signals before doing the exec...
3240 */
3241
3242#ifdef HAVE_SIGSET
c7a9d594 3243 sigset(SIGTERM, SIG_DFL);
38743560 3244 sigset(SIGCHLD, SIG_DFL);
5f152161 3245#elif defined(HAVE_SIGACTION)
c7a9d594 3246 memset(&action, 0, sizeof(action));
3247
3248 sigemptyset(&action.sa_mask);
3249 action.sa_handler = SIG_DFL;
3250
3251 sigaction(SIGTERM, &action, NULL);
38743560 3252 sigaction(SIGCHLD, &action, NULL);
c7a9d594 3253#else
3254 signal(SIGTERM, SIG_DFL);
38743560 3255 signal(SIGCHLD, SIG_DFL);
5f152161 3256#endif /* HAVE_SIGSET */
3257
38743560 3258 ReleaseSignals();
3259
a74b005d 3260 /*
3261 * Execute the pipe program; if an error occurs, exit with status 1...
3262 */
3263
b14d90ba 3264 execve(command, argv, envp);
96df88bb 3265 exit(errno);
a74b005d 3266 return (0);
3267 }
3268 else if (pid < 0)
3269 {
3270 /*
3271 * Error - can't fork!
3272 */
3273
5ea8888e 3274 LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
1d6c68fb 3275 strerror(errno));
3276
a74b005d 3277 close(fds[0]);
3278 close(fds[1]);
5c8eff27 3279 pid = 0;
a74b005d 3280 }
3281 else
3282 {
3283 /*
3284 * Fork successful - return the PID...
3285 */
3286
f63a2256 3287 AddCert(pid, con->username);
3288
753453e4 3289 LogMessage(L_DEBUG, "CGI %s started - PID = %d", command, pid);
1d6c68fb 3290
a74b005d 3291 *outfile = fds[0];
3292 close(fds[1]);
5c8eff27 3293 }
a74b005d 3294
38743560 3295 ReleaseSignals();
4d520ad4 3296
5c8eff27 3297 return (pid);
a74b005d 3298}
3299
3300
dcfcaeac 3301#if defined(HAVE_CDSASSL)
3302/*
3303 * 'CDSAReadFunc()' - Read function for CDSA decryption code.
3304 */
3305
3306static OSStatus /* O - -1 on error, 0 on success */
3307CDSAReadFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
3308 void *data, /* I - Data buffer */
3309 size_t *dataLength) /* IO - Number of bytes */
3310{
3311 ssize_t bytes; /* Number of bytes read */
3312
3313
3314 bytes = recv((int)connection, data, *dataLength, 0);
3315 if (bytes >= 0)
3316 {
3317 *dataLength = bytes;
3318 return (0);
3319 }
3320 else
3321 return (-1);
3322}
3323
3324
3325/*
3326 * 'CDSAWriteFunc()' - Write function for CDSA encryption code.
3327 */
3328
3329static OSStatus /* O - -1 on error, 0 on success */
3330CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
3331 const void *data, /* I - Data buffer */
3332 size_t *dataLength) /* IO - Number of bytes */
3333{
3334 ssize_t bytes;
3335
3336
3337 bytes = write((int)connection, data, *dataLength);
3338 if (bytes >= 0)
3339 {
3340 *dataLength = bytes;
3341 return (0);
3342 }
3343 else
3344 return (-1);
3345}
3346#endif /* HAVE_CDSASSL */
3347
3348
93894a43 3349/*
0f71c41e 3350 * End of "$Id: client.c,v 1.91.2.64 2003/07/20 03:13:08 mike Exp $".
a74b005d 3351 */