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