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