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