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