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