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