]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/client.c
Mirror 1.1.x change.
[thirdparty/cups.git] / scheduler / client.c
1 /*
2 * "$Id: client.c,v 1.91.2.36 2003/01/17 14:45:00 mike Exp $"
3 *
4 * Client routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-2003 by Easy Software Products, all rights reserved.
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
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * AcceptClient() - Accept a new client.
27 * CloseAllClients() - Close all remote clients immediately.
28 * CloseClient() - Close a remote client.
29 * EncryptClient() - Enable encryption for the client...
30 * ReadClient() - Read data from a client.
31 * SendCommand() - Send output from a command via HTTP.
32 * SendError() - Send an error message via HTTP.
33 * SendFile() - Send a file via HTTP.
34 * SendHeader() - Send an HTTP request.
35 * ShutdownClient() - Shutdown the receiving end of a connection.
36 * WriteClient() - Write data to a client as needed.
37 * check_if_modified() - Decode an "If-Modified-Since" line.
38 * decode_auth() - Decode an authorization string.
39 * get_file() - Get a filename and state info.
40 * install_conf_file() - Install a configuration file.
41 * pipe_command() - Pipe the output of a command to the remote client.
42 */
43
44 /*
45 * Include necessary headers...
46 */
47
48 #include "cupsd.h"
49 #include <grp.h>
50 #include <cups/http-private.h>
51
52
53 /*
54 * Local functions...
55 */
56
57 static int check_if_modified(client_t *con,
58 struct stat *filestats);
59 static void decode_auth(client_t *con);
60 static char *get_file(client_t *con, struct stat *filestats);
61 static http_status_t install_conf_file(client_t *con);
62 static int pipe_command(client_t *con, int infile, int *outfile,
63 char *command, char *options);
64
65
66 /*
67 * 'AcceptClient()' - Accept a new client.
68 */
69
70 void
71 AcceptClient(listener_t *lis) /* I - Listener socket */
72 {
73 int i; /* Looping var */
74 int count; /* Count of connections on a host */
75 int val; /* Parameter value */
76 client_t *con; /* New client pointer */
77 const struct hostent *host; /* Host entry for address */
78 char *hostname;/* Hostname for address */
79 http_addr_t temp; /* Temporary address variable */
80 static time_t last_dos = 0;
81 /* Time of last DoS attack */
82
83
84 LogMessage(L_DEBUG2, "AcceptClient(%p) %d NumClients = %d",
85 lis, lis->fd, NumClients);
86
87 /*
88 * Make sure we don't have a full set of clients already...
89 */
90
91 if (NumClients == MaxClients)
92 return;
93
94 /*
95 * Get a pointer to the next available client...
96 */
97
98 con = Clients + NumClients;
99
100 memset(con, 0, sizeof(client_t));
101 con->http.activity = time(NULL);
102
103 /*
104 * Accept the client and get the remote address...
105 */
106
107 val = sizeof(struct sockaddr_in);
108
109 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr),
110 &val)) < 0)
111 {
112 LogMessage(L_ERROR, "Unable to accept client connection - %s.",
113 strerror(errno));
114 return;
115 }
116
117 #ifdef AF_INET6
118 if (lis->address.addr.sa_family == AF_INET6)
119 con->http.hostaddr.ipv6.sin6_port = lis->address.ipv6.sin6_port;
120 else
121 #endif /* AF_INET6 */
122 con->http.hostaddr.ipv4.sin_port = lis->address.ipv4.sin_port;
123
124 /*
125 * Check the number of clients on the same address...
126 */
127
128 for (i = 0, count = 0; i < NumClients; i ++)
129 if (memcmp(&(Clients[i].http.hostaddr), &(con->http.hostaddr),
130 sizeof(con->http.hostaddr)) == 0)
131 {
132 count ++;
133 if (count >= MaxClientsPerHost)
134 break;
135 }
136
137 if (count >= MaxClientsPerHost)
138 {
139 if ((time(NULL) - last_dos) >= 60)
140 {
141 last_dos = time(NULL);
142 LogMessage(L_WARN, "Possible DoS attack - more than %d clients connecting from %s!",
143 MaxClientsPerHost, Clients[i].http.hostname);
144 }
145
146 #ifdef WIN32
147 closesocket(con->http.fd);
148 #else
149 close(con->http.fd);
150 #endif /* WIN32 */
151
152 return;
153 }
154
155 /*
156 * Get the hostname or format the IP address as needed...
157 */
158
159 if (HostNameLookups)
160 hostname = httpAddrLookup(&(con->http.hostaddr), con->http.hostname,
161 sizeof(con->http.hostname));
162 else
163 {
164 hostname = NULL;
165 httpAddrString(&(con->http.hostaddr), con->http.hostname,
166 sizeof(con->http.hostname));
167 }
168
169 if (httpAddrLocalhost(&(con->http.hostaddr)))
170 {
171 /*
172 * Map accesses from the loopback interface to "localhost"...
173 */
174
175 strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname));
176 }
177 else if (httpAddrEqual(&(con->http.hostaddr), &ServerAddr))
178 {
179 /*
180 * Map accesses from the same host to the server name.
181 */
182
183 strlcpy(con->http.hostname, ServerName, sizeof(con->http.hostname));
184 }
185
186 if (hostname == NULL && HostNameLookups == 2)
187 {
188 /*
189 * Can't have an unresolved IP address with double-lookups enabled...
190 */
191
192 LogMessage(L_DEBUG2, "AcceptClient: Closing connection %d...",
193 con->http.fd);
194
195 #ifdef WIN32
196 closesocket(con->http.fd);
197 #else
198 close(con->http.fd);
199 #endif /* WIN32 */
200
201 LogMessage(L_WARN, "Name lookup failed - connection from %s closed!",
202 con->http.hostname);
203 return;
204 }
205
206 if (HostNameLookups == 2)
207 {
208 /*
209 * Do double lookups as needed...
210 */
211
212 if ((host = httpGetHostByName(con->http.hostname)) != NULL)
213 {
214 /*
215 * See if the hostname maps to the same IP address...
216 */
217
218 if (host->h_addrtype != con->http.hostaddr.addr.sa_family)
219 {
220 /*
221 * Not the right type of address...
222 */
223
224 host = NULL;
225 }
226 else
227 {
228 /*
229 * Compare all of the addresses against this one...
230 */
231
232 for (i = 0; host->h_addr_list[i]; i ++)
233 {
234 httpAddrLoad(host, 0, i, &temp);
235
236 if (httpAddrEqual(&(con->http.hostaddr), &temp))
237 break;
238 }
239
240 if (!host->h_addr_list[i])
241 host = NULL;
242 }
243 }
244
245 if (host == NULL)
246 {
247 /*
248 * Can't have a hostname that doesn't resolve to the same IP address
249 * with double-lookups enabled...
250 */
251
252 LogMessage(L_DEBUG2, "AcceptClient: Closing connection %d...",
253 con->http.fd);
254
255 #ifdef WIN32
256 closesocket(con->http.fd);
257 #else
258 close(con->http.fd);
259 #endif /* WIN32 */
260
261 LogMessage(L_WARN, "IP lookup failed - connection from %s closed!",
262 con->http.hostname);
263 return;
264 }
265 }
266
267 #ifdef AF_INET6
268 if (con->http.hostaddr.addr.sa_family == AF_INET6)
269 LogMessage(L_DEBUG, "AcceptClient: %d from %s:%d.", con->http.fd,
270 con->http.hostname, ntohs(con->http.hostaddr.ipv6.sin6_port));
271 else
272 #endif /* AF_INET6 */
273 LogMessage(L_DEBUG, "AcceptClient: %d from %s:%d.", con->http.fd,
274 con->http.hostname, ntohs(con->http.hostaddr.ipv4.sin_port));
275
276 /*
277 * Using TCP_NODELAY improves responsiveness, especially on systems
278 * with a slow loopback interface... Since we write large buffers
279 * when sending print files and requests, there shouldn't be any
280 * performance penalty for this...
281 */
282
283 val = 1;
284 setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
285
286 /*
287 * Add the socket to the select() input mask.
288 */
289
290 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
291
292 LogMessage(L_DEBUG2, "AcceptClient: Adding fd %d to InputSet...",
293 con->http.fd);
294 FD_SET(con->http.fd, &InputSet);
295
296 NumClients ++;
297
298 /*
299 * Temporarily suspend accept()'s until we lose a client...
300 */
301
302 if (NumClients == MaxClients)
303 PauseListening();
304
305 #ifdef HAVE_SSL
306 /*
307 * See if we are connecting on a secure port...
308 */
309
310 if (lis->encryption == HTTP_ENCRYPT_ALWAYS)
311 {
312 /*
313 * https connection; go secure...
314 */
315
316 con->http.encryption = HTTP_ENCRYPT_ALWAYS;
317
318 EncryptClient(con);
319 }
320 #endif /* HAVE_SSL */
321 }
322
323
324 /*
325 * 'CloseAllClients()' - Close all remote clients immediately.
326 */
327
328 void
329 CloseAllClients(void)
330 {
331 while (NumClients > 0)
332 CloseClient(Clients);
333 }
334
335
336 /*
337 * 'CloseClient()' - Close a remote client.
338 */
339
340 void
341 CloseClient(client_t *con) /* I - Client to close */
342 {
343 int status; /* Exit status of pipe command */
344 #if defined HAVE_LIBSSL
345 SSL_CTX *context; /* Context for encryption */
346 SSL *conn; /* Connection for encryption */
347 unsigned long error; /* Error code */
348 #elif defined HAVE_GNUTLS /* HAVE_LIBSSL */
349 http_tls_t *conn; /* TLS connection information */
350 int error; /* Error code */
351 gnutls_certificate_server_credentials *credentials; /* TLS credentials */
352 #endif /* HAVE_GNUTLS */
353
354
355 LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd);
356
357 #ifdef HAVE_SSL
358 /*
359 * Shutdown encryption as needed...
360 */
361
362 if (con->http.tls)
363 {
364 #if defined HAVE_LIBSSL
365 conn = (SSL *)(con->http.tls);
366 context = SSL_get_SSL_CTX(conn);
367
368 switch (SSL_shutdown(conn))
369 {
370 case 1 :
371 LogMessage(L_INFO, "CloseClient: SSL shutdown successful!");
372 break;
373 case -1 :
374 LogMessage(L_ERROR, "CloseClient: Fatal error during SSL shutdown!");
375 default :
376 while ((error = ERR_get_error()) != 0)
377 LogMessage(L_ERROR, "CloseClient: %s", ERR_error_string(error, NULL));
378 break;
379 }
380
381 SSL_CTX_free(context);
382 SSL_free(conn);
383 #elif defined HAVE_GNUTLS /* HAVE_LIBSSL */
384 conn = (http_tls_t *)(con->http.tls);
385 credentials = (gnutls_certificate_server_credentials *)(conn->credentials);
386
387 error = gnutls_bye(conn->session, GNUTLS_SHUT_WR);
388 switch (error)
389 {
390 case GNUTLS_E_SUCCESS:
391 LogMessage(L_INFO, "CloseClient: SSL shutdown successful!");
392 break;
393 default:
394 LogMessage(L_ERROR, "CloseClient: %s", gnutls_strerror(error));
395 break;
396 }
397
398 gnutls_deinit(conn->session);
399 gnutls_certificate_free_credentials(*credentials);
400 free(credentials);
401 free(conn);
402 #endif /* HAVE_GNUTLS */
403
404 con->http.tls = NULL;
405 }
406 #endif /* HAVE_LIBSSL */
407
408 /*
409 * Close the socket and clear the file from the input set for select()...
410 */
411
412 if (con->http.fd >= 0)
413 {
414 LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...",
415 con->http.fd);
416 close(con->http.fd);
417 FD_CLR(con->http.fd, &InputSet);
418 FD_CLR(con->http.fd, &OutputSet);
419 con->http.fd = 0;
420 }
421
422 if (con->pipe_pid != 0)
423 {
424 LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet...",
425 con->file);
426 FD_CLR(con->file, &InputSet);
427 }
428
429 if (con->file)
430 {
431 /*
432 * Close the open data file...
433 */
434
435 if (con->pipe_pid)
436 {
437 kill(con->pipe_pid, SIGKILL);
438 waitpid(con->pipe_pid, &status, WNOHANG);
439 }
440
441 LogMessage(L_DEBUG2, "CloseClient() %d Closing data file %d.",
442 con->http.fd, con->file);
443 LogMessage(L_DEBUG2, "CloseClient() %d Removing fd %d from InputSet.",
444 con->http.fd, con->file);
445
446 FD_CLR(con->file, &InputSet);
447 close(con->file);
448 con->file = 0;
449 }
450
451 if (con->request)
452 {
453 ippDelete(con->request);
454 con->request = NULL;
455 }
456
457 if (con->response)
458 {
459 ippDelete(con->response);
460 con->response = NULL;
461 }
462
463 if (con->language)
464 {
465 cupsLangFree(con->language);
466 con->language = NULL;
467 }
468
469 /*
470 * Re-enable new client connections if we are going back under the
471 * limit...
472 */
473
474 if (NumClients == MaxClients)
475 ResumeListening();
476
477 /*
478 * Compact the list of clients as necessary...
479 */
480
481 NumClients --;
482
483 if (con < (Clients + NumClients))
484 memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
485 }
486
487
488 /*
489 * 'EncryptClient()' - Enable encryption for the client...
490 */
491
492 int /* O - 1 on success, 0 on error */
493 EncryptClient(client_t *con) /* I - Client to encrypt */
494 {
495 #if defined HAVE_LIBSSL
496 SSL_CTX *context; /* Context for encryption */
497 SSL *conn; /* Connection for encryption */
498 unsigned long error; /* Error code */
499
500
501 /*
502 * Create the SSL context and accept the connection...
503 */
504
505 context = SSL_CTX_new(SSLv23_server_method());
506
507 SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
508 SSL_CTX_use_certificate_file(context, ServerCertificate, SSL_FILETYPE_PEM);
509
510 conn = SSL_new(context);
511
512 SSL_set_fd(conn, con->http.fd);
513 if (SSL_accept(conn) != 1)
514 {
515 while ((error = ERR_get_error()) != 0)
516 LogMessage(L_ERROR, "EncryptClient: %s", ERR_error_string(error, NULL));
517
518 SSL_CTX_free(context);
519 SSL_free(conn);
520 return (0);
521 }
522
523 con->http.tls = conn;
524 return (1);
525 #elif defined HAVE_GNUTLS /* HAVE_LIBSSL */
526 http_tls_t *conn; /* TLS session object */
527 int error; /* Error code */
528 gnutls_certificate_server_credentials *credentials; /* TLS credentials */
529
530 /*
531 * Create the SSL object and perform the SSL handshake...
532 */
533
534 conn = (http_tls_t *)malloc(sizeof(gnutls_session));
535 if (conn == NULL)
536 return (0);
537 credentials = (gnutls_certificate_server_credentials *)
538 malloc(sizeof(gnutls_certificate_server_credentials));
539 if (credentials == NULL)
540 {
541 free(conn);
542 return (0);
543 }
544
545 gnutls_certificate_allocate_credentials(credentials);
546 gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
547 ServerKey, GNUTLS_X509_FMT_PEM);
548
549 gnutls_init(&(conn->session), GNUTLS_SERVER);
550 gnutls_set_default_priority(conn->session);
551 gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials);
552 gnutls_transport_set_ptr(conn->session, con->http.fd);
553
554 error = gnutls_handshake(conn->session);
555
556 if (error != GNUTLS_E_SUCCESS)
557 {
558 LogMessage(L_ERROR, "EncryptClient: %s", gnutls_strerror(error));
559 gnutls_deinit(conn->session);
560 gnutls_certificate_free_credentials(*credentials);
561 free(conn);
562 free(credentials);
563 return (0);
564 }
565
566 LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.",
567 con->http.fd);
568
569 conn->credentials = credentials;
570 con->http.tls = conn;
571 return (1);
572 #else
573 return (0);
574 #endif /* HAVE_GNUTLS */
575 }
576
577
578 /*
579 * 'ReadClient()' - Read data from a client.
580 */
581
582 int /* O - 1 on success, 0 on error */
583 ReadClient(client_t *con) /* I - Client to read from */
584 {
585 char line[32768], /* Line from client... */
586 operation[64], /* Operation code from socket */
587 version[64]; /* HTTP version number string */
588 int major, minor; /* HTTP version numbers */
589 http_status_t status; /* Transfer status */
590 ipp_state_t ipp_state; /* State of IPP transfer */
591 int bytes; /* Number of bytes to POST */
592 char *filename; /* Name of file for GET/HEAD */
593 struct stat filestats; /* File information */
594 mime_type_t *type; /* MIME type of file */
595 printer_t *p; /* Printer */
596 location_t *best; /* Best match for authentication */
597 static unsigned request_id = 0;/* Request ID for temp files */
598
599
600 status = HTTP_CONTINUE;
601
602 LogMessage(L_DEBUG2, "ReadClient() %d, used=%d", con->http.fd,
603 con->http.used);
604
605 if (con->http.error)
606 {
607 CloseClient(con);
608 return (0);
609 }
610
611 switch (con->http.state)
612 {
613 case HTTP_WAITING :
614 /*
615 * See if we've received a request line...
616 */
617
618 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
619 {
620 CloseClient(con);
621 return (0);
622 }
623
624 /*
625 * Ignore blank request lines...
626 */
627
628 if (line[0] == '\0')
629 break;
630
631 /*
632 * Clear other state variables...
633 */
634
635 httpClearFields(HTTP(con));
636
637 con->http.activity = time(NULL);
638 con->http.version = HTTP_1_0;
639 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
640 con->http.data_encoding = HTTP_ENCODE_LENGTH;
641 con->http.data_remaining = 0;
642 con->operation = HTTP_WAITING;
643 con->bytes = 0;
644 con->file = 0;
645 con->pipe_pid = 0;
646 con->command[0] = '\0';
647 con->username[0] = '\0';
648 con->password[0] = '\0';
649 con->uri[0] = '\0';
650
651 if (con->language != NULL)
652 {
653 cupsLangFree(con->language);
654 con->language = NULL;
655 }
656
657 /*
658 * Grab the request line...
659 */
660
661 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
662 {
663 case 1 :
664 LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
665 SendError(con, HTTP_BAD_REQUEST);
666 ShutdownClient(con);
667 return (1);
668 case 2 :
669 con->http.version = HTTP_0_9;
670 break;
671 case 3 :
672 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
673 {
674 LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
675 SendError(con, HTTP_BAD_REQUEST);
676 ShutdownClient(con);
677 return (1);
678 }
679
680 if (major < 2)
681 {
682 con->http.version = (http_version_t)(major * 100 + minor);
683 if (con->http.version == HTTP_1_1 && KeepAlive)
684 con->http.keep_alive = HTTP_KEEPALIVE_ON;
685 else
686 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
687 }
688 else
689 {
690 SendError(con, HTTP_NOT_SUPPORTED);
691 ShutdownClient(con);
692 return (1);
693 }
694 break;
695 }
696
697 /*
698 * Process the request...
699 */
700
701 if (strcmp(operation, "GET") == 0)
702 con->http.state = HTTP_GET;
703 else if (strcmp(operation, "PUT") == 0)
704 con->http.state = HTTP_PUT;
705 else if (strcmp(operation, "POST") == 0)
706 con->http.state = HTTP_POST;
707 else if (strcmp(operation, "DELETE") == 0)
708 con->http.state = HTTP_DELETE;
709 else if (strcmp(operation, "TRACE") == 0)
710 con->http.state = HTTP_TRACE;
711 else if (strcmp(operation, "OPTIONS") == 0)
712 con->http.state = HTTP_OPTIONS;
713 else if (strcmp(operation, "HEAD") == 0)
714 con->http.state = HTTP_HEAD;
715 else
716 {
717 LogMessage(L_ERROR, "Bad operation \"%s\"!", operation);
718 SendError(con, HTTP_BAD_REQUEST);
719 ShutdownClient(con);
720 return (1);
721 }
722
723 con->start = time(NULL);
724 con->operation = con->http.state;
725
726 LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
727 operation, con->uri,
728 con->http.version / 100, con->http.version % 100);
729
730 con->http.status = HTTP_OK;
731
732 case HTTP_OPTIONS :
733 case HTTP_DELETE :
734 case HTTP_GET :
735 case HTTP_HEAD :
736 case HTTP_POST :
737 case HTTP_PUT :
738 case HTTP_TRACE :
739 /*
740 * Parse incoming parameters until the status changes...
741 */
742
743 status = httpUpdate(HTTP(con));
744
745 if (status != HTTP_OK && status != HTTP_CONTINUE)
746 {
747 SendError(con, HTTP_BAD_REQUEST);
748 ShutdownClient(con);
749 return (1);
750 }
751 break;
752
753 default :
754 break; /* Anti-compiler-warning-code */
755 }
756
757 /*
758 * Handle new transfers...
759 */
760
761 if (status == HTTP_OK)
762 {
763 con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
764
765 decode_auth(con);
766
767 if (strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) == 0 &&
768 KeepAlive)
769 con->http.keep_alive = HTTP_KEEPALIVE_ON;
770
771 if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
772 con->http.version >= HTTP_1_1)
773 {
774 /*
775 * HTTP/1.1 and higher require the "Host:" field...
776 */
777
778 if (!SendError(con, HTTP_BAD_REQUEST))
779 {
780 CloseClient(con);
781 return (0);
782 }
783 }
784 else if (con->operation == HTTP_OPTIONS)
785 {
786 /*
787 * Do OPTIONS command...
788 */
789
790 if ((best = FindBest(con->uri, con->http.state)) != NULL &&
791 best->type != AUTH_NONE)
792 {
793 if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL))
794 {
795 CloseClient(con);
796 return (0);
797 }
798 }
799
800 if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
801 con->http.tls == NULL)
802 {
803 #ifdef HAVE_SSL
804 /*
805 * Do encryption stuff...
806 */
807
808 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
809 {
810 CloseClient(con);
811 return (0);
812 }
813
814 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
815 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
816 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
817 httpPrintf(HTTP(con), "\r\n");
818
819 EncryptClient(con);
820 #else
821 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
822 {
823 CloseClient(con);
824 return (0);
825 }
826 #endif /* HAVE_SSL */
827 }
828
829 if (!SendHeader(con, HTTP_OK, NULL))
830 {
831 CloseClient(con);
832 return (0);
833 }
834
835 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
836 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
837 httpPrintf(HTTP(con), "\r\n");
838 }
839 else if (strstr(con->uri, "..") != NULL)
840 {
841 /*
842 * Protect against malicious users!
843 */
844
845 if (!SendError(con, HTTP_FORBIDDEN))
846 {
847 CloseClient(con);
848 return (0);
849 }
850 }
851 else if (con->uri[0] != '/')
852 {
853 /*
854 * Don't allow proxying (yet)...
855 */
856
857 if (!SendError(con, HTTP_METHOD_NOT_ALLOWED))
858 {
859 CloseClient(con);
860 return (0);
861 }
862 }
863 else
864 {
865 if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
866 con->http.tls == NULL)
867 {
868 #ifdef HAVE_SSL
869 /*
870 * Do encryption stuff...
871 */
872
873 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
874 {
875 CloseClient(con);
876 return (0);
877 }
878
879 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
880 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
881 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
882 httpPrintf(HTTP(con), "\r\n");
883
884 EncryptClient(con);
885
886 status = IsAuthorized(con);
887 #else
888 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
889 {
890 CloseClient(con);
891 return (0);
892 }
893 #endif /* HAVE_SSL */
894 }
895
896 if (status != HTTP_OK)
897 {
898 LogMessage(L_DEBUG2, "ReadClient: Unauthorized request for %s...\n",
899 con->uri);
900 SendError(con, status);
901 ShutdownClient(con);
902 return (1);
903 }
904
905 switch (con->http.state)
906 {
907 case HTTP_GET_SEND :
908 if (strncmp(con->uri, "/printers/", 10) == 0 &&
909 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
910 {
911 /*
912 * Send PPD file - get the real printer name since printer
913 * names are not case sensitive but filenames can be...
914 */
915
916 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
917
918 if ((p = FindPrinter(con->uri + 10)) != NULL)
919 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
920 else
921 {
922 if (!SendError(con, HTTP_NOT_FOUND))
923 {
924 CloseClient(con);
925 return (0);
926 }
927
928 break;
929 }
930 }
931
932 if ((strncmp(con->uri, "/admin", 6) == 0 &&
933 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
934 strncmp(con->uri, "/printers", 9) == 0 ||
935 strncmp(con->uri, "/classes", 8) == 0 ||
936 strncmp(con->uri, "/jobs", 5) == 0)
937 {
938 /*
939 * Send CGI output...
940 */
941
942 if (strncmp(con->uri, "/admin", 6) == 0)
943 {
944 snprintf(con->command, sizeof(con->command),
945 "%s/cgi-bin/admin.cgi", ServerBin);
946 con->options = con->uri + 6;
947 }
948 else if (strncmp(con->uri, "/printers", 9) == 0)
949 {
950 snprintf(con->command, sizeof(con->command),
951 "%s/cgi-bin/printers.cgi", ServerBin);
952 con->options = con->uri + 9;
953 }
954 else if (strncmp(con->uri, "/classes", 8) == 0)
955 {
956 snprintf(con->command, sizeof(con->command),
957 "%s/cgi-bin/classes.cgi", ServerBin);
958 con->options = con->uri + 8;
959 }
960 else
961 {
962 snprintf(con->command, sizeof(con->command),
963 "%s/cgi-bin/jobs.cgi", ServerBin);
964 con->options = con->uri + 5;
965 }
966
967 if (con->options[0] == '/')
968 con->options ++;
969
970 if (!SendCommand(con, con->command, con->options))
971 {
972 if (!SendError(con, HTTP_NOT_FOUND))
973 {
974 CloseClient(con);
975 return (0);
976 }
977 }
978 else
979 LogRequest(con, HTTP_OK);
980
981 if (con->http.version <= HTTP_1_0)
982 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
983 }
984 else if (strncmp(con->uri, "/admin/conf/", 12) == 0 &&
985 (strchr(con->uri + 12, '/') != NULL ||
986 strlen(con->uri) == 12))
987 {
988 /*
989 * GET can only be done to configuration files under
990 * /admin/conf...
991 */
992
993 if (!SendError(con, HTTP_FORBIDDEN))
994 {
995 CloseClient(con);
996 return (0);
997 }
998
999 break;
1000 }
1001 else
1002 {
1003 /*
1004 * Serve a file...
1005 */
1006
1007 if ((filename = get_file(con, &filestats)) == NULL)
1008 {
1009 if (!SendError(con, HTTP_NOT_FOUND))
1010 {
1011 CloseClient(con);
1012 return (0);
1013 }
1014 }
1015 else if (!check_if_modified(con, &filestats))
1016 {
1017 if (!SendError(con, HTTP_NOT_MODIFIED))
1018 {
1019 CloseClient(con);
1020 return (0);
1021 }
1022 }
1023 else
1024 {
1025 type = mimeFileType(MimeDatabase, filename);
1026 if (type == NULL)
1027 strcpy(line, "text/plain");
1028 else
1029 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1030
1031 if (!SendFile(con, HTTP_OK, filename, line, &filestats))
1032 {
1033 CloseClient(con);
1034 return (0);
1035 }
1036 }
1037 }
1038 break;
1039
1040 case HTTP_POST_RECV :
1041 /*
1042 * See if the POST request includes a Content-Length field, and if
1043 * so check the length against any limits that are set...
1044 */
1045
1046 LogMessage(L_DEBUG2, "POST %s", con->uri);
1047 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1048
1049 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1050 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
1051 MaxRequestSize > 0)
1052 {
1053 /*
1054 * Request too large...
1055 */
1056
1057 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1058 {
1059 CloseClient(con);
1060 return (0);
1061 }
1062
1063 break;
1064 }
1065 else if (atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) < 0)
1066 {
1067 /*
1068 * Negative content lengths are invalid!
1069 */
1070
1071 if (!SendError(con, HTTP_BAD_REQUEST))
1072 {
1073 CloseClient(con);
1074 return (0);
1075 }
1076
1077 break;
1078 }
1079
1080 /*
1081 * See what kind of POST request this is; for IPP requests the
1082 * content-type field will be "application/ipp"...
1083 */
1084
1085 if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
1086 con->request = ippNew();
1087 else if ((strncmp(con->uri, "/admin", 6) == 0 &&
1088 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
1089 strncmp(con->uri, "/printers", 9) == 0 ||
1090 strncmp(con->uri, "/classes", 8) == 0 ||
1091 strncmp(con->uri, "/jobs", 5) == 0)
1092 {
1093 /*
1094 * CGI request...
1095 */
1096
1097 if (strncmp(con->uri, "/admin", 6) == 0)
1098 {
1099 snprintf(con->command, sizeof(con->command),
1100 "%s/cgi-bin/admin.cgi", ServerBin);
1101 con->options = con->uri + 6;
1102 }
1103 else if (strncmp(con->uri, "/printers", 9) == 0)
1104 {
1105 snprintf(con->command, sizeof(con->command),
1106 "%s/cgi-bin/printers.cgi", ServerBin);
1107 con->options = con->uri + 9;
1108 }
1109 else if (strncmp(con->uri, "/classes", 8) == 0)
1110 {
1111 snprintf(con->command, sizeof(con->command),
1112 "%s/cgi-bin/classes.cgi", ServerBin);
1113 con->options = con->uri + 8;
1114 }
1115 else
1116 {
1117 snprintf(con->command, sizeof(con->command),
1118 "%s/cgi-bin/jobs.cgi", ServerBin);
1119 con->options = con->uri + 5;
1120 }
1121
1122 if (con->options[0] == '/')
1123 con->options ++;
1124
1125 LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"",
1126 con->http.fd, con->command, con->options);
1127
1128 if (con->http.version <= HTTP_1_0)
1129 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1130 }
1131 else if (!SendError(con, HTTP_UNAUTHORIZED))
1132 {
1133 CloseClient(con);
1134 return (0);
1135 }
1136 break;
1137
1138 case HTTP_PUT_RECV :
1139 /*
1140 * Validate the resource name...
1141 */
1142
1143 if (strncmp(con->uri, "/admin/conf/", 12) != 0 ||
1144 strchr(con->uri + 12, '/') != NULL ||
1145 strlen(con->uri) == 12)
1146 {
1147 /*
1148 * PUT can only be done to configuration files under
1149 * /admin/conf...
1150 */
1151
1152 if (!SendError(con, HTTP_FORBIDDEN))
1153 {
1154 CloseClient(con);
1155 return (0);
1156 }
1157
1158 break;
1159 }
1160
1161 /*
1162 * See if the PUT request includes a Content-Length field, and if
1163 * so check the length against any limits that are set...
1164 */
1165
1166 LogMessage(L_DEBUG2, "PUT %s", con->uri);
1167 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1168
1169 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1170 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
1171 MaxRequestSize > 0)
1172 {
1173 /*
1174 * Request too large...
1175 */
1176
1177 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1178 {
1179 CloseClient(con);
1180 return (0);
1181 }
1182
1183 break;
1184 }
1185
1186 /*
1187 * Open a temporary file to hold the request...
1188 */
1189
1190 snprintf(con->filename, sizeof(con->filename), "%s/%08x",
1191 RequestRoot, request_id ++);
1192 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1193 fchmod(con->file, 0640);
1194 fchown(con->file, User, Group);
1195
1196 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
1197 con->filename, con->file);
1198
1199 if (con->file < 0)
1200 {
1201 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1202 {
1203 CloseClient(con);
1204 return (0);
1205 }
1206 }
1207 break;
1208
1209 case HTTP_DELETE :
1210 case HTTP_TRACE :
1211 SendError(con, HTTP_NOT_IMPLEMENTED);
1212 ShutdownClient(con);
1213 return (1);
1214
1215 case HTTP_HEAD :
1216 if (strncmp(con->uri, "/printers/", 10) == 0 &&
1217 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
1218 {
1219 /*
1220 * Send PPD file - get the real printer name since printer
1221 * names are not case sensitive but filenames can be...
1222 */
1223
1224 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1225
1226 if ((p = FindPrinter(con->uri + 10)) != NULL)
1227 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1228 else
1229 {
1230 if (!SendError(con, HTTP_NOT_FOUND))
1231 {
1232 CloseClient(con);
1233 return (0);
1234 }
1235
1236 break;
1237 }
1238 }
1239
1240 if ((strncmp(con->uri, "/admin/", 7) == 0 &&
1241 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
1242 strncmp(con->uri, "/printers/", 10) == 0 ||
1243 strncmp(con->uri, "/classes/", 9) == 0 ||
1244 strncmp(con->uri, "/jobs/", 6) == 0)
1245 {
1246 /*
1247 * CGI output...
1248 */
1249
1250 if (!SendHeader(con, HTTP_OK, "text/html"))
1251 {
1252 CloseClient(con);
1253 return (0);
1254 }
1255
1256 if (httpPrintf(HTTP(con), "\r\n") < 0)
1257 {
1258 CloseClient(con);
1259 return (0);
1260 }
1261
1262 LogRequest(con, HTTP_OK);
1263 }
1264 else if (strncmp(con->uri, "/admin/conf/", 12) == 0 &&
1265 (strchr(con->uri + 12, '/') != NULL ||
1266 strlen(con->uri) == 12))
1267 {
1268 /*
1269 * HEAD can only be done to configuration files under
1270 * /admin/conf...
1271 */
1272
1273 if (!SendError(con, HTTP_FORBIDDEN))
1274 {
1275 CloseClient(con);
1276 return (0);
1277 }
1278
1279 break;
1280 }
1281 else if ((filename = get_file(con, &filestats)) == NULL)
1282 {
1283 if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
1284 {
1285 CloseClient(con);
1286 return (0);
1287 }
1288
1289 LogRequest(con, HTTP_NOT_FOUND);
1290 }
1291 else if (!check_if_modified(con, &filestats))
1292 {
1293 if (!SendError(con, HTTP_NOT_MODIFIED))
1294 {
1295 CloseClient(con);
1296 return (0);
1297 }
1298
1299 LogRequest(con, HTTP_NOT_MODIFIED);
1300 }
1301 else
1302 {
1303 /*
1304 * Serve a file...
1305 */
1306
1307 type = mimeFileType(MimeDatabase, filename);
1308 if (type == NULL)
1309 strcpy(line, "text/plain");
1310 else
1311 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1312
1313 if (!SendHeader(con, HTTP_OK, line))
1314 {
1315 CloseClient(con);
1316 return (0);
1317 }
1318
1319 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1320 httpGetDateString(filestats.st_mtime)) < 0)
1321 {
1322 CloseClient(con);
1323 return (0);
1324 }
1325
1326 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1327 (unsigned long)filestats.st_size) < 0)
1328 {
1329 CloseClient(con);
1330 return (0);
1331 }
1332
1333 LogRequest(con, HTTP_OK);
1334 }
1335
1336 if (httpPrintf(HTTP(con), "\r\n") < 0)
1337 {
1338 CloseClient(con);
1339 return (0);
1340 }
1341
1342 con->http.state = HTTP_WAITING;
1343 break;
1344
1345 default :
1346 break; /* Anti-compiler-warning-code */
1347 }
1348 }
1349 }
1350
1351 /*
1352 * Handle any incoming data...
1353 */
1354
1355 switch (con->http.state)
1356 {
1357 case HTTP_PUT_RECV :
1358 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
1359 con->http.fd,
1360 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
1361 con->http.data_remaining, con->file);
1362
1363 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
1364 {
1365 CloseClient(con);
1366 return (0);
1367 }
1368 else if (bytes > 0)
1369 {
1370 con->bytes += bytes;
1371
1372 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
1373 con->http.fd, bytes, con->file);
1374
1375 if (write(con->file, line, bytes) < bytes)
1376 {
1377 LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s",
1378 bytes, con->filename, strerror(errno));
1379
1380 LogMessage(L_DEBUG2, "ReadClient: Closing data file %d...",
1381 con->file);
1382
1383 close(con->file);
1384 con->file = 0;
1385 unlink(con->filename);
1386 con->filename[0] = '\0';
1387
1388 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1389 {
1390 CloseClient(con);
1391 return (0);
1392 }
1393 }
1394 }
1395
1396 if (con->http.state == HTTP_WAITING)
1397 {
1398 /*
1399 * End of file, see how big it is...
1400 */
1401
1402 fstat(con->file, &filestats);
1403
1404 LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
1405 con->http.fd, con->file, (int)filestats.st_size);
1406
1407 close(con->file);
1408 con->file = 0;
1409
1410 if (filestats.st_size > MaxRequestSize &&
1411 MaxRequestSize > 0)
1412 {
1413 /*
1414 * Request is too big; remove it and send an error...
1415 */
1416
1417 LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
1418 con->http.fd, con->filename);
1419 unlink(con->filename);
1420 con->filename[0] = '\0';
1421
1422 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1423 {
1424 CloseClient(con);
1425 return (0);
1426 }
1427 }
1428
1429 /*
1430 * Install the configuration file...
1431 */
1432
1433 status = install_conf_file(con);
1434
1435 /*
1436 * Return the status to the client...
1437 */
1438
1439 if (!SendError(con, status))
1440 {
1441 CloseClient(con);
1442 return (0);
1443 }
1444 }
1445 break;
1446
1447 case HTTP_POST_RECV :
1448 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
1449 con->http.fd,
1450 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
1451 con->http.data_remaining, con->file);
1452
1453 if (con->request != NULL)
1454 {
1455 /*
1456 * Grab any request data from the connection...
1457 */
1458
1459 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
1460 {
1461 LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!",
1462 con->http.fd);
1463
1464 if (!SendError(con, HTTP_BAD_REQUEST))
1465 {
1466 CloseClient(con);
1467 return (0);
1468 }
1469
1470 ShutdownClient(con);
1471 return (1);
1472 }
1473 else if (ipp_state != IPP_DATA)
1474 break;
1475 else
1476 con->bytes += ippLength(con->request);
1477 }
1478
1479 if (con->file == 0 && con->http.state != HTTP_POST_SEND)
1480 {
1481 /*
1482 * Create a file as needed for the request data...
1483 */
1484
1485 snprintf(con->filename, sizeof(con->filename), "%s/%08x",
1486 RequestRoot, request_id ++);
1487 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1488 fchmod(con->file, 0640);
1489 fchown(con->file, User, Group);
1490
1491 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
1492 con->filename, con->file);
1493
1494 if (con->file < 0)
1495 {
1496 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1497 {
1498 CloseClient(con);
1499 return (0);
1500 }
1501 }
1502 }
1503
1504 if (con->http.state != HTTP_POST_SEND)
1505 {
1506 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
1507 {
1508 CloseClient(con);
1509 return (0);
1510 }
1511 else if (bytes > 0)
1512 {
1513 con->bytes += bytes;
1514
1515 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
1516 con->http.fd, bytes, con->file);
1517
1518 if (write(con->file, line, bytes) < bytes)
1519 {
1520 LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s",
1521 bytes, con->filename, strerror(errno));
1522
1523 LogMessage(L_DEBUG2, "ReadClient: Closing file %d...",
1524 con->file);
1525
1526 close(con->file);
1527 con->file = 0;
1528 unlink(con->filename);
1529 con->filename[0] = '\0';
1530
1531 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1532 {
1533 CloseClient(con);
1534 return (0);
1535 }
1536 }
1537 }
1538 else if (con->http.state != HTTP_POST_SEND)
1539 {
1540 CloseClient(con);
1541 return (0);
1542 }
1543 }
1544
1545 if (con->http.state == HTTP_POST_SEND)
1546 {
1547 if (con->file)
1548 {
1549 fstat(con->file, &filestats);
1550
1551 LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
1552 con->http.fd, con->file, (int)filestats.st_size);
1553
1554 close(con->file);
1555 con->file = 0;
1556
1557 if (filestats.st_size > MaxRequestSize &&
1558 MaxRequestSize > 0)
1559 {
1560 /*
1561 * Request is too big; remove it and send an error...
1562 */
1563
1564 LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
1565 con->http.fd, con->filename);
1566 unlink(con->filename);
1567 con->filename[0] = '\0';
1568
1569 if (con->request)
1570 {
1571 /*
1572 * Delete any IPP request data...
1573 */
1574
1575 ippDelete(con->request);
1576 con->request = NULL;
1577 }
1578
1579 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1580 {
1581 CloseClient(con);
1582 return (0);
1583 }
1584 }
1585
1586 if (con->command[0])
1587 {
1588 if (!SendCommand(con, con->command, con->options))
1589 {
1590 if (!SendError(con, HTTP_NOT_FOUND))
1591 {
1592 CloseClient(con);
1593 return (0);
1594 }
1595 }
1596 else
1597 LogRequest(con, HTTP_OK);
1598 }
1599 }
1600
1601 if (con->request)
1602 return (ProcessIPPRequest(con));
1603 }
1604 break;
1605
1606 default :
1607 break; /* Anti-compiler-warning-code */
1608 }
1609
1610 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
1611 {
1612 CloseClient(con);
1613 return (0);
1614 }
1615 else
1616 return (1);
1617 }
1618
1619
1620 /*
1621 * 'SendCommand()' - Send output from a command via HTTP.
1622 */
1623
1624 int
1625 SendCommand(client_t *con,
1626 char *command,
1627 char *options)
1628 {
1629 int fd;
1630
1631
1632 if (con->filename[0])
1633 fd = open(con->filename, O_RDONLY);
1634 else
1635 fd = open("/dev/null", O_RDONLY);
1636
1637 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
1638
1639 close(fd);
1640
1641 LogMessage(L_INFO, "Started \"%s\" (pid=%d)", command, con->pipe_pid);
1642
1643 LogMessage(L_DEBUG, "SendCommand() %d file=%d", con->http.fd, con->file);
1644
1645 if (con->pipe_pid == 0)
1646 return (0);
1647
1648 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1649
1650 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to InputSet...", con->file);
1651 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to OutputSet...",
1652 con->http.fd);
1653
1654 FD_SET(con->file, &InputSet);
1655 FD_SET(con->http.fd, &OutputSet);
1656
1657 if (!SendHeader(con, HTTP_OK, NULL))
1658 return (0);
1659
1660 if (con->http.version == HTTP_1_1)
1661 {
1662 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
1663
1664 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
1665 return (0);
1666 }
1667
1668 con->got_fields = 0;
1669 con->field_col = 0;
1670
1671 return (1);
1672 }
1673
1674
1675 /*
1676 * 'SendError()' - Send an error message via HTTP.
1677 */
1678
1679 int /* O - 1 if successful, 0 otherwise */
1680 SendError(client_t *con, /* I - Connection */
1681 http_status_t code) /* I - Error code */
1682 {
1683 char message[1024]; /* Message for user */
1684
1685
1686 /*
1687 * Put the request in the access_log file...
1688 */
1689
1690 if (con->operation > HTTP_WAITING)
1691 LogRequest(con, code);
1692
1693 LogMessage(L_DEBUG, "SendError() %d code=%d", con->http.fd, code);
1694
1695 /*
1696 * To work around bugs in some proxies, don't use Keep-Alive for some
1697 * error messages...
1698 */
1699
1700 if (code >= HTTP_BAD_REQUEST)
1701 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1702
1703 /*
1704 * Send an error message back to the client. If the error code is a
1705 * 400 or 500 series, make sure the message contains some text, too!
1706 */
1707
1708 if (!SendHeader(con, code, NULL))
1709 return (0);
1710
1711 #ifdef HAVE_SSL
1712 if (code == HTTP_UPGRADE_REQUIRED)
1713 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
1714 return (0);
1715
1716 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0)
1717 return (0);
1718 #endif /* HAVE_SSL */
1719
1720 if ((con->http.version >= HTTP_1_1 && !con->http.keep_alive) ||
1721 (code >= HTTP_BAD_REQUEST && code != HTTP_UPGRADE_REQUIRED))
1722 {
1723 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
1724 return (0);
1725 }
1726
1727 if (code >= HTTP_BAD_REQUEST)
1728 {
1729 /*
1730 * Send a human-readable error message.
1731 */
1732
1733 snprintf(message, sizeof(message),
1734 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
1735 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
1736 code, httpStatus(code), httpStatus(code),
1737 con->language ? con->language->messages[code] :
1738 httpStatus(code));
1739
1740 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
1741 return (0);
1742 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
1743 (int)strlen(message)) < 0)
1744 return (0);
1745 if (httpPrintf(HTTP(con), "\r\n") < 0)
1746 return (0);
1747 if (httpPrintf(HTTP(con), "%s", message) < 0)
1748 return (0);
1749 }
1750 else if (httpPrintf(HTTP(con), "\r\n") < 0)
1751 return (0);
1752
1753 con->http.state = HTTP_WAITING;
1754
1755 return (1);
1756 }
1757
1758
1759 /*
1760 * 'SendFile()' - Send a file via HTTP.
1761 */
1762
1763 int
1764 SendFile(client_t *con,
1765 http_status_t code,
1766 char *filename,
1767 char *type,
1768 struct stat *filestats)
1769 {
1770 con->file = open(filename, O_RDONLY);
1771
1772 LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
1773
1774 if (con->file < 0)
1775 return (0);
1776
1777 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1778
1779 con->pipe_pid = 0;
1780
1781 if (!SendHeader(con, code, type))
1782 return (0);
1783
1784 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
1785 return (0);
1786 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1787 (unsigned long)filestats->st_size) < 0)
1788 return (0);
1789 if (httpPrintf(HTTP(con), "\r\n") < 0)
1790 return (0);
1791
1792 LogMessage(L_DEBUG2, "SendFile: Adding fd %d to OutputSet...", con->http.fd);
1793
1794 FD_SET(con->http.fd, &OutputSet);
1795
1796 return (1);
1797 }
1798
1799
1800 /*
1801 * 'SendHeader()' - Send an HTTP request.
1802 */
1803
1804 int /* O - 1 on success, 0 on failure */
1805 SendHeader(client_t *con, /* I - Client to send to */
1806 http_status_t code, /* I - HTTP status code */
1807 char *type) /* I - MIME type of document */
1808 {
1809 location_t *loc; /* Authentication location */
1810
1811
1812 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
1813 con->http.version % 100, code, httpStatus(code)) < 0)
1814 return (0);
1815 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
1816 return (0);
1817 if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
1818 return (0);
1819 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
1820 {
1821 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
1822 return (0);
1823 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
1824 return (0);
1825 }
1826 if (code == HTTP_METHOD_NOT_ALLOWED)
1827 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
1828 return (0);
1829
1830 if (code == HTTP_UNAUTHORIZED)
1831 {
1832 /*
1833 * This already succeeded in IsAuthorized...
1834 */
1835
1836 loc = FindBest(con->uri, con->http.state);
1837
1838 if (loc->type != AUTH_DIGEST)
1839 {
1840 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
1841 return (0);
1842 }
1843 else
1844 {
1845 if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" "
1846 "nonce=\"%s\"\r\n", con->http.hostname) < 0)
1847 return (0);
1848 }
1849 }
1850 if (con->language != NULL)
1851 {
1852 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
1853 con->language->language) < 0)
1854 return (0);
1855
1856 if (type != NULL)
1857 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
1858 cupsLangEncoding(con->language)) < 0)
1859 return (0);
1860 }
1861 else if (type != NULL)
1862 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
1863 return (0);
1864
1865 return (1);
1866 }
1867
1868
1869 /*
1870 * 'ShutdownClient()' - Shutdown the receiving end of a connection.
1871 */
1872
1873 void
1874 ShutdownClient(client_t *con) /* I - Client connection */
1875 {
1876 /*
1877 * Shutdown the receiving end of the socket, since the client
1878 * still needs to read the error message...
1879 */
1880
1881 shutdown(con->http.fd, 0);
1882 con->http.used = 0;
1883
1884 /*
1885 * Update the activity time so that we timeout after 30 seconds rather
1886 * then the current Timeout setting (300 by default). This prevents
1887 * some DoS situations...
1888 */
1889
1890 con->http.activity = time(NULL) - Timeout + 30;
1891
1892 LogMessage(L_DEBUG2, "ShutdownClient: Removing fd %d from InputSet...",
1893 con->http.fd);
1894
1895 FD_CLR(con->http.fd, &InputSet);
1896 }
1897
1898
1899 /*
1900 * 'WriteClient()' - Write data to a client as needed.
1901 */
1902
1903 int /* O - 1 if success, 0 if fail */
1904 WriteClient(client_t *con) /* I - Client connection */
1905 {
1906 int bytes; /* Number of bytes written */
1907 char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */
1908 char *bufptr; /* Pointer into buffer */
1909 ipp_state_t ipp_state; /* IPP state value */
1910
1911
1912 if (con->http.state != HTTP_GET_SEND &&
1913 con->http.state != HTTP_POST_SEND)
1914 return (1);
1915
1916 if (con->response != NULL)
1917 {
1918 ipp_state = ippWrite(&(con->http), con->response);
1919 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
1920 }
1921 else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
1922 {
1923 if (con->pipe_pid && !con->got_fields)
1924 {
1925 /*
1926 * Inspect the data for Content-Type and other fields.
1927 */
1928
1929 buf[bytes] = '\0';
1930
1931 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
1932 if (*bufptr == '\n')
1933 {
1934 /*
1935 * Send line to client...
1936 */
1937
1938 if (bufptr > buf && bufptr[-1] == '\r')
1939 bufptr[-1] = '\0';
1940 *bufptr++ = '\0';
1941
1942 httpPrintf(HTTP(con), "%s\r\n", buf);
1943 LogMessage(L_DEBUG2, "WriteClient() %d %s", con->http.fd, buf);
1944
1945 /*
1946 * Update buffer...
1947 */
1948
1949 bytes -= (bufptr - buf);
1950 memcpy(buf, bufptr, bytes + 1);
1951 bufptr = buf - 1;
1952
1953 /*
1954 * See if the line was empty...
1955 */
1956
1957 if (con->field_col == 0)
1958 con->got_fields = 1;
1959 else
1960 con->field_col = 0;
1961 }
1962 else if (*bufptr != '\r')
1963 con->field_col ++;
1964
1965 if (bytes > 0 && !con->got_fields)
1966 {
1967 /*
1968 * Remaining text needs to go out...
1969 */
1970
1971 httpPrintf(HTTP(con), "%s", buf);
1972
1973 con->http.activity = time(NULL);
1974 return (1);
1975 }
1976 else if (bytes == 0)
1977 {
1978 con->http.activity = time(NULL);
1979 return (1);
1980 }
1981 }
1982
1983 if (httpWrite(HTTP(con), buf, bytes) < 0)
1984 {
1985 CloseClient(con);
1986 return (0);
1987 }
1988
1989 con->bytes += bytes;
1990 }
1991
1992 if (bytes <= 0)
1993 {
1994 LogRequest(con, HTTP_OK);
1995
1996 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
1997 {
1998 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
1999 {
2000 CloseClient(con);
2001 return (0);
2002 }
2003 }
2004
2005 con->http.state = HTTP_WAITING;
2006
2007 LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from OutputSet...",
2008 con->http.fd);
2009
2010 FD_CLR(con->http.fd, &OutputSet);
2011
2012 if (con->file)
2013 {
2014 LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from InputSet...",
2015 con->file);
2016 FD_CLR(con->file, &InputSet);
2017
2018 if (con->pipe_pid)
2019 kill(con->pipe_pid, SIGTERM);
2020
2021 LogMessage(L_DEBUG2, "WriteClient() %d Closing data file %d.",
2022 con->http.fd, con->file);
2023
2024 close(con->file);
2025 con->file = 0;
2026 con->pipe_pid = 0;
2027 }
2028
2029 if (con->filename[0])
2030 {
2031 LogMessage(L_DEBUG2, "WriteClient() %d Removing temp file %s",
2032 con->http.fd, con->filename);
2033 unlink(con->filename);
2034 con->filename[0] = '\0';
2035 }
2036
2037 if (con->request != NULL)
2038 {
2039 ippDelete(con->request);
2040 con->request = NULL;
2041 }
2042
2043 if (con->response != NULL)
2044 {
2045 ippDelete(con->response);
2046 con->response = NULL;
2047 }
2048
2049 if (!con->http.keep_alive)
2050 {
2051 CloseClient(con);
2052 return (0);
2053 }
2054 }
2055
2056 if (bytes >= 1024)
2057 LogMessage(L_DEBUG2, "WriteClient() %d %d bytes", con->http.fd, bytes);
2058
2059 con->http.activity = time(NULL);
2060
2061 return (1);
2062 }
2063
2064
2065 /*
2066 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
2067 */
2068
2069 static int /* O - 1 if modified since */
2070 check_if_modified(client_t *con, /* I - Client connection */
2071 struct stat *filestats) /* I - File information */
2072 {
2073 char *ptr; /* Pointer into field */
2074 time_t date; /* Time/date value */
2075 int size; /* Size/length value */
2076
2077
2078 size = 0;
2079 date = 0;
2080 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
2081
2082 if (*ptr == '\0')
2083 return (1);
2084
2085 LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"",
2086 con->http.fd, ptr);
2087
2088 while (*ptr != '\0')
2089 {
2090 while (isspace(*ptr) || *ptr == ';')
2091 ptr ++;
2092
2093 if (strncasecmp(ptr, "length=", 7) == 0)
2094 {
2095 ptr += 7;
2096 size = atoi(ptr);
2097
2098 while (isdigit(*ptr))
2099 ptr ++;
2100 }
2101 else if (isalpha(*ptr))
2102 {
2103 date = httpGetDateTime(ptr);
2104 while (*ptr != '\0' && *ptr != ';')
2105 ptr ++;
2106 }
2107 }
2108
2109 LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
2110 con->http.fd, size, (int)filestats->st_size, (int)date,
2111 (int)filestats->st_mtime);
2112
2113 return ((size != filestats->st_size && size != 0) ||
2114 (date < filestats->st_mtime && date != 0) ||
2115 (size == 0 && date == 0));
2116 }
2117
2118
2119 /*
2120 * 'decode_auth()' - Decode an authorization string.
2121 */
2122
2123 static void
2124 decode_auth(client_t *con) /* I - Client to decode to */
2125 {
2126 char *s, /* Authorization string */
2127 value[1024]; /* Value string */
2128 const char *username; /* Certificate username */
2129
2130
2131 /*
2132 * Decode the string...
2133 */
2134
2135 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
2136
2137 LogMessage(L_DEBUG2, "decode_auth(%p): Authorization string = \"%s\"",
2138 con, s);
2139
2140 if (strncmp(s, "Basic", 5) == 0)
2141 {
2142 s += 5;
2143 while (isspace(*s))
2144 s ++;
2145
2146 httpDecode64(value, s);
2147
2148 /*
2149 * Pull the username and password out...
2150 */
2151
2152 if ((s = strchr(value, ':')) == NULL)
2153 {
2154 LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
2155 con->http.fd, value);
2156 return;
2157 }
2158
2159 *s++ = '\0';
2160
2161 strlcpy(con->username, value, sizeof(con->username));
2162 strlcpy(con->password, s, sizeof(con->password));
2163 }
2164 else if (strncmp(s, "Local", 5) == 0)
2165 {
2166 s += 5;
2167 while (isspace(*s))
2168 s ++;
2169
2170 if ((username = FindCert(s)) != NULL)
2171 strlcpy(con->username, username, sizeof(con->username));
2172 }
2173 else if (strncmp(s, "Digest", 5) == 0)
2174 {
2175 /*
2176 * Get the username and password from the Digest attributes...
2177 */
2178
2179 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username",
2180 value))
2181 strlcpy(con->username, value, sizeof(con->username));
2182
2183 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response",
2184 value))
2185 strlcpy(con->password, value, sizeof(con->password) - 1);
2186 }
2187
2188 LogMessage(L_DEBUG2, "decode_auth() %d username=\"%s\"",
2189 con->http.fd, con->username);
2190 }
2191
2192
2193 /*
2194 * 'get_file()' - Get a filename and state info.
2195 */
2196
2197 static char * /* O - Real filename */
2198 get_file(client_t *con, /* I - Client connection */
2199 struct stat *filestats)/* O - File information */
2200 {
2201 int status; /* Status of filesystem calls */
2202 char *params; /* Pointer to parameters in URI */
2203 static char filename[1024]; /* Filename buffer */
2204
2205
2206 /*
2207 * Need to add DocumentRoot global...
2208 */
2209
2210 if (strncmp(con->uri, "/ppd/", 5) == 0)
2211 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri);
2212 else if (strncmp(con->uri, "/admin/conf/", 12) == 0)
2213 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri + 11);
2214 else if (con->language != NULL)
2215 snprintf(filename, sizeof(filename), "%s/%s%s", DocumentRoot, con->language->language,
2216 con->uri);
2217 else
2218 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
2219
2220 if ((params = strchr(filename, '?')) != NULL)
2221 *params = '\0';
2222
2223 /*
2224 * Grab the status for this language; if there isn't a language-specific file
2225 * then fallback to the default one...
2226 */
2227
2228 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
2229 {
2230 /*
2231 * Drop the language prefix and try the current directory...
2232 */
2233
2234 if (strncmp(con->uri, "/ppd/", 5) != 0 &&
2235 strncmp(con->uri, "/admin/conf/", 12) != 0)
2236 {
2237 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
2238
2239 status = stat(filename, filestats);
2240 }
2241 }
2242
2243 /*
2244 * If we're found a directory, get the index.html file instead...
2245 */
2246
2247 if (!status && S_ISDIR(filestats->st_mode))
2248 {
2249 if (filename[strlen(filename) - 1] == '/')
2250 strlcat(filename, "index.html", sizeof(filename));
2251 else
2252 strlcat(filename, "/index.html", sizeof(filename));
2253
2254 status = stat(filename, filestats);
2255 }
2256
2257 LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d",
2258 con->http.fd, filename, status ? -1 : (int)filestats->st_size);
2259
2260 if (status)
2261 return (NULL);
2262 else
2263 return (filename);
2264 }
2265
2266
2267 /*
2268 * 'install_conf_file()' - Install a configuration file.
2269 */
2270
2271 static http_status_t /* O - Status */
2272 install_conf_file(client_t *con) /* I - Connection */
2273 {
2274 FILE *in, /* Input file */
2275 *out; /* Output file */
2276 char buffer[1024]; /* Copy buffer */
2277 int bytes; /* Number of bytes */
2278 char conffile[1024], /* Configuration filename */
2279 newfile[1024], /* New config filename */
2280 oldfile[1024]; /* Old config filename */
2281 struct stat confinfo; /* Config file info */
2282
2283
2284 /*
2285 * First construct the filenames...
2286 */
2287
2288 snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11);
2289 snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11);
2290 snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11);
2291
2292 LogMessage(L_INFO, "Installing config file \"%s\"...", conffile);
2293
2294 /*
2295 * Get the owner, group, and permissions of the configuration file.
2296 * If it doesn't exist, assign it to the User and Group in the
2297 * cupsd.conf file with mode 0640 permissions.
2298 */
2299
2300 if (stat(conffile, &confinfo))
2301 {
2302 confinfo.st_uid = User;
2303 confinfo.st_gid = Group;
2304 confinfo.st_mode = 0640;
2305 }
2306
2307 /*
2308 * Open the request file and new config file...
2309 */
2310
2311 if ((in = fopen(con->filename, "rb")) == NULL)
2312 {
2313 LogMessage(L_ERROR, "Unable to open request file \"%s\" - %s",
2314 con->filename, strerror(errno));
2315 return (HTTP_SERVER_ERROR);
2316 }
2317
2318 if ((out = fopen(newfile, "wb")) == NULL)
2319 {
2320 fclose(in);
2321 LogMessage(L_ERROR, "Unable to open config file \"%s\" - %s",
2322 newfile, strerror(errno));
2323 return (HTTP_SERVER_ERROR);
2324 }
2325
2326 fchmod(fileno(out), confinfo.st_mode);
2327 fchown(fileno(out), confinfo.st_uid, confinfo.st_gid);
2328
2329 /*
2330 * Copy from the request to the new config file...
2331 */
2332
2333 while ((bytes = fread(buffer, 1, sizeof(buffer), in)) > 0)
2334 if (fwrite(buffer, 1, bytes, out) < bytes)
2335 {
2336 LogMessage(L_ERROR, "Unable to copy to config file \"%s\" - %s",
2337 newfile, strerror(errno));
2338
2339 fclose(in);
2340 fclose(out);
2341 unlink(newfile);
2342
2343 return (HTTP_SERVER_ERROR);
2344 }
2345
2346 /*
2347 * Close the files...
2348 */
2349
2350 fclose(in);
2351 if (fclose(out))
2352 {
2353 LogMessage(L_ERROR, "Error file closing config file \"%s\" - %s",
2354 newfile, strerror(errno));
2355
2356 unlink(newfile);
2357
2358 return (HTTP_SERVER_ERROR);
2359 }
2360
2361 /*
2362 * Remove the request file...
2363 */
2364
2365 unlink(con->filename);
2366 con->filename[0] = '\0';
2367
2368 /*
2369 * Unlink the old backup, rename the current config file to the backup
2370 * filename, and rename the new config file to the config file name...
2371 */
2372
2373 if (unlink(oldfile))
2374 if (errno != ENOENT)
2375 {
2376 LogMessage(L_ERROR, "Unable to remove backup config file \"%s\" - %s",
2377 oldfile, strerror(errno));
2378
2379 unlink(newfile);
2380
2381 return (HTTP_SERVER_ERROR);
2382 }
2383
2384 if (rename(conffile, oldfile))
2385 if (errno != ENOENT)
2386 {
2387 LogMessage(L_ERROR, "Unable to rename old config file \"%s\" - %s",
2388 conffile, strerror(errno));
2389
2390 unlink(newfile);
2391
2392 return (HTTP_SERVER_ERROR);
2393 }
2394
2395 if (rename(newfile, conffile))
2396 {
2397 LogMessage(L_ERROR, "Unable to rename new config file \"%s\" - %s",
2398 newfile, strerror(errno));
2399
2400 rename(oldfile, conffile);
2401 unlink(newfile);
2402
2403 return (HTTP_SERVER_ERROR);
2404 }
2405
2406 /*
2407 * If the cupsd.conf file was updated, set the NeedReload flag...
2408 */
2409
2410 if (strcmp(con->uri, "/admin/conf/cupsd.conf") == 0)
2411 NeedReload = TRUE;
2412
2413 /*
2414 * Return that the file was created successfully...
2415 */
2416
2417 return (HTTP_CREATED);
2418 }
2419
2420
2421 /*
2422 * 'pipe_command()' - Pipe the output of a command to the remote client.
2423 */
2424
2425 static int /* O - Process ID */
2426 pipe_command(client_t *con, /* I - Client connection */
2427 int infile, /* I - Standard input for command */
2428 int *outfile, /* O - Standard output for command */
2429 char *command, /* I - Command to run */
2430 char *options) /* I - Options for command */
2431 {
2432 int pid; /* Process ID */
2433 char *commptr; /* Command string pointer */
2434 int fd; /* Looping var */
2435 int fds[2]; /* Pipe FDs */
2436 int argc; /* Number of arguments */
2437 int envc; /* Number of environment variables */
2438 char argbuf[10240], /* Argument buffer */
2439 *argv[100], /* Argument strings */
2440 *envp[100]; /* Environment variables */
2441 char lang[1024], /* LANG env variable */
2442 content_length[1024], /* CONTENT_LENGTH env variable */
2443 content_type[1024], /* CONTENT_TYPE env variable */
2444 ipp_port[1024], /* Default listen port */
2445 server_port[1024], /* Default server port */
2446 server_name[1024], /* Default listen hostname */
2447 remote_host[1024], /* REMOTE_HOST env variable */
2448 remote_user[1024], /* REMOTE_USER env variable */
2449 tmpdir[1024], /* TMPDIR environment variable */
2450 ldpath[1024], /* LD_LIBRARY_PATH environment variable */
2451 nlspath[1024], /* NLSPATH environment variable */
2452 datadir[1024], /* CUPS_DATADIR environment variable */
2453 root[1024], /* CUPS_SERVERROOT environment variable */
2454 query_string[10240]; /* QUERY_STRING env variable */
2455 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
2456 sigset_t oldmask, /* POSIX signal masks */
2457 newmask;
2458 struct sigaction action; /* POSIX signal handler */
2459 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
2460
2461
2462 /*
2463 * Copy the command string...
2464 */
2465
2466 strlcpy(argbuf, options, sizeof(argbuf));
2467
2468 /*
2469 * Parse the string; arguments can be separated by + and are terminated
2470 * by ?...
2471 */
2472
2473 argv[0] = argbuf;
2474
2475 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
2476 if (*commptr == ' ' || *commptr == '+')
2477 {
2478 *commptr++ = '\0';
2479
2480 while (*commptr == ' ')
2481 commptr ++;
2482
2483 if (*commptr != '\0')
2484 {
2485 argv[argc] = commptr;
2486 argc ++;
2487 }
2488
2489 commptr --;
2490 }
2491 else if (*commptr == '%')
2492 {
2493 if (commptr[1] >= '0' && commptr[1] <= '9')
2494 *commptr = (commptr[1] - '0') << 4;
2495 else
2496 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
2497
2498 if (commptr[2] >= '0' && commptr[2] <= '9')
2499 *commptr |= commptr[2] - '0';
2500 else
2501 *commptr |= tolower(commptr[2]) - 'a' + 10;
2502
2503 strcpy(commptr + 1, commptr + 3);
2504 }
2505 else if (*commptr == '?')
2506 break;
2507
2508 argv[argc] = NULL;
2509
2510 if (argv[0][0] == '\0')
2511 argv[0] = strrchr(command, '/') + 1;
2512
2513 /*
2514 * Setup the environment variables as needed...
2515 */
2516
2517 snprintf(lang, sizeof(lang), "LANG=%s",
2518 con->language ? con->language->language : "C");
2519 #ifdef AF_INET6
2520 if (con->http.hostaddr.addr.sa_family == AF_INET6)
2521 {
2522 sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.ipv6.sin6_port));
2523 sprintf(server_port, "SERVER_PORT=%d",
2524 ntohs(con->http.hostaddr.ipv6.sin6_port));
2525 }
2526 else
2527 #endif /* AF_INET6 */
2528 {
2529 sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.ipv4.sin_port));
2530 sprintf(server_port, "SERVER_PORT=%d",
2531 ntohs(con->http.hostaddr.ipv4.sin_port));
2532 }
2533
2534 if (strcmp(con->http.hostname, "localhost") == 0)
2535 strlcpy(server_name, "SERVER_NAME=localhost", sizeof(server_name));
2536 else
2537 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName);
2538 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname);
2539 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
2540 snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
2541 snprintf(datadir, sizeof(datadir), "CUPS_DATADIR=%s", DataDir);
2542 snprintf(root, sizeof(root), "CUPS_SERVERROOT=%s", ServerRoot);
2543
2544 if (getenv("LD_LIBRARY_PATH") != NULL)
2545 snprintf(ldpath, sizeof(ldpath), "LD_LIBRARY_PATH=%s",
2546 getenv("LD_LIBRARY_PATH"));
2547 else if (getenv("DYLD_LIBRARY_PATH") != NULL)
2548 snprintf(ldpath, sizeof(ldpath), "DYLD_LIBRARY_PATH=%s",
2549 getenv("DYLD_LIBRARY_PATH"));
2550 else if (getenv("SHLIB_PATH") != NULL)
2551 snprintf(ldpath, sizeof(ldpath), "SHLIB_PATH=%s",
2552 getenv("SHLIB_PATH"));
2553 else
2554 ldpath[0] = '\0';
2555
2556 if (getenv("NLSPATH") != NULL)
2557 snprintf(nlspath, sizeof(nlspath), "NLSPATH=%s", getenv("NLSPATH"));
2558 else
2559 nlspath[0] = '\0';
2560
2561 envp[0] = "PATH=/bin:/usr/bin";
2562 envp[1] = "SERVER_SOFTWARE=CUPS/1.2";
2563 envp[2] = "GATEWAY_INTERFACE=CGI/1.1";
2564 envp[3] = "SERVER_PROTOCOL=HTTP/1.1";
2565 envp[4] = ipp_port;
2566 envp[5] = server_name;
2567 envp[6] = server_port;
2568 envp[7] = remote_host;
2569 envp[8] = remote_user;
2570 envp[9] = lang;
2571 envp[10] = TZ;
2572 envp[11] = tmpdir;
2573 envp[12] = datadir;
2574 envp[13] = root;
2575
2576 envc = 14;
2577
2578 if (ldpath[0])
2579 envp[envc ++] = ldpath;
2580
2581 if (nlspath[0])
2582 envp[envc ++] = nlspath;
2583
2584 if (con->operation == HTTP_GET)
2585 {
2586 envp[envc ++] = "REQUEST_METHOD=GET";
2587
2588 if (*commptr)
2589 {
2590 /*
2591 * Add GET form variables after ?...
2592 */
2593
2594 *commptr++ = '\0';
2595
2596 snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
2597 envp[envc ++] = query_string;
2598 }
2599 }
2600 else
2601 {
2602 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
2603 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
2604 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
2605
2606 envp[envc ++] = "REQUEST_METHOD=POST";
2607 envp[envc ++] = content_length;
2608 envp[envc ++] = content_type;
2609 }
2610
2611 /*
2612 * Tell the CGI if we are using encryption...
2613 */
2614
2615 if (con->http.encryption == HTTP_ENCRYPT_ALWAYS)
2616 {
2617 envp[envc ++] = "HTTPS=ON";
2618 envp[envc ++] = "CUPS_ENCRYPTION=Always";
2619 }
2620
2621 envp[envc] = NULL;
2622
2623 /*
2624 * Create a pipe for the output...
2625 */
2626
2627 if (pipe(fds))
2628 {
2629 LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
2630 argv[0], strerror(errno));
2631 return (0);
2632 }
2633
2634 /*
2635 * Block signals before forking...
2636 */
2637
2638 #ifdef HAVE_SIGSET
2639 sighold(SIGTERM);
2640 sighold(SIGCHLD);
2641 #elif defined(HAVE_SIGACTION)
2642 sigemptyset(&newmask);
2643 sigaddset(&newmask, SIGTERM);
2644 sigaddset(&newmask, SIGCHLD);
2645 sigprocmask(SIG_BLOCK, &newmask, &oldmask);
2646 #endif /* HAVE_SIGSET */
2647
2648 /*
2649 * Then execute the command...
2650 */
2651
2652 if ((pid = fork()) == 0)
2653 {
2654 /*
2655 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
2656 */
2657
2658 if (getuid() == 0)
2659 {
2660 /*
2661 * Running as root, so change to a non-priviledged user...
2662 */
2663
2664 if (setgid(Group))
2665 exit(errno);
2666
2667 if (setuid(User))
2668 exit(errno);
2669 }
2670
2671 /*
2672 * Reset group membership to just the main one we belong to.
2673 */
2674
2675 setgroups(0, NULL);
2676
2677 /*
2678 * Update stdin/stdout/stderr...
2679 */
2680
2681 if (infile)
2682 {
2683 close(0);
2684 if (dup(infile) < 0)
2685 exit(errno);
2686 }
2687
2688 close(1);
2689 if (dup(fds[1]) < 0)
2690 exit(errno);
2691
2692 close(2);
2693 open("/dev/null", O_WRONLY);
2694
2695 /*
2696 * Close extra file descriptors...
2697 */
2698
2699 for (fd = 3; fd < MaxFDs; fd ++)
2700 close(fd);
2701
2702 /*
2703 * Change umask to restrict permissions on created files...
2704 */
2705
2706 umask(077);
2707
2708 /*
2709 * Unblock signals before doing the exec...
2710 */
2711
2712 #ifdef HAVE_SIGSET
2713 sigset(SIGTERM, SIG_DFL);
2714 sigset(SIGCHLD, SIG_DFL);
2715
2716 sigrelse(SIGTERM);
2717 sigrelse(SIGCHLD);
2718 #elif defined(HAVE_SIGACTION)
2719 memset(&action, 0, sizeof(action));
2720
2721 sigemptyset(&action.sa_mask);
2722 action.sa_handler = SIG_DFL;
2723
2724 sigaction(SIGTERM, &action, NULL);
2725 sigaction(SIGCHLD, &action, NULL);
2726
2727 sigprocmask(SIG_SETMASK, &oldmask, NULL);
2728 #else
2729 signal(SIGTERM, SIG_DFL);
2730 signal(SIGCHLD, SIG_DFL);
2731 #endif /* HAVE_SIGSET */
2732
2733
2734 /*
2735 * Execute the pipe program; if an error occurs, exit with status 1...
2736 */
2737
2738 execve(command, argv, envp);
2739 exit(errno);
2740 return (0);
2741 }
2742 else if (pid < 0)
2743 {
2744 /*
2745 * Error - can't fork!
2746 */
2747
2748 LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
2749 strerror(errno));
2750
2751 close(fds[0]);
2752 close(fds[1]);
2753 pid = 0;
2754 }
2755 else
2756 {
2757 /*
2758 * Fork successful - return the PID...
2759 */
2760
2761 AddCert(pid, con->username);
2762
2763 LogMessage(L_DEBUG, "CGI %s started - PID = %d", command, pid);
2764
2765 *outfile = fds[0];
2766 close(fds[1]);
2767 }
2768
2769 #ifdef HAVE_SIGSET
2770 sigrelse(SIGTERM);
2771 sigrelse(SIGCHLD);
2772 #elif defined(HAVE_SIGACTION)
2773 sigprocmask(SIG_SETMASK, &oldmask, NULL);
2774 #endif /* HAVE_SIGSET */
2775
2776 return (pid);
2777 }
2778
2779
2780 /*
2781 * End of "$Id: client.c,v 1.91.2.36 2003/01/17 14:45:00 mike Exp $".
2782 */