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