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