]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/client.c
Copyright 2001...
[thirdparty/cups.git] / scheduler / client.c
1 /*
2 * "$Id: client.c,v 1.80 2001/01/22 15:03:58 mike Exp $"
3 *
4 * Client routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-2001 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 * WriteClient() - Write data to a client as needed.
36 * check_if_modified() - Decode an "If-Modified-Since" line.
37 * decode_auth() - Decode an authorization string.
38 * get_file() - Get a filename and state info.
39 * pipe_command() - Pipe the output of a command to the remote client.
40 */
41
42 /*
43 * Include necessary headers...
44 */
45
46 #include "cupsd.h"
47
48 #ifdef HAVE_LIBSSL
49 # include <openssl/ssl.h>
50 # include <openssl/rand.h>
51 #endif /* HAVE_LIBSSL */
52
53
54 /*
55 * Local functions...
56 */
57
58 static int check_if_modified(client_t *con, struct stat *filestats);
59 static void decode_auth(client_t *con);
60 static char *get_file(client_t *con, struct stat *filestats);
61 static int pipe_command(client_t *con, int infile, int *outfile, char *command, char *options);
62
63
64 /*
65 * 'AcceptClient()' - Accept a new client.
66 */
67
68 void
69 AcceptClient(listener_t *lis) /* I - Listener socket */
70 {
71 int val; /* Parameter value */
72 client_t *con; /* New client pointer */
73 unsigned address;/* Address of client */
74 struct hostent *host; /* Host entry for address */
75
76
77 DEBUG_printf(("AcceptClient(%08x) %d NumClients = %d\n",
78 lis, lis->fd, NumClients));
79
80 /*
81 * Make sure we don't have a full set of clients already...
82 */
83
84 if (NumClients == MaxClients)
85 return;
86
87 /*
88 * Get a pointer to the next available client...
89 */
90
91 con = Clients + NumClients;
92
93 memset(con, 0, sizeof(client_t));
94 con->http.activity = time(NULL);
95
96 /*
97 * Accept the client and get the remote address...
98 */
99
100 val = sizeof(struct sockaddr_in);
101
102 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr),
103 &val)) < 0)
104 {
105 LogMessage(L_ERROR, "accept() failed - %s.", strerror(errno));
106 return;
107 }
108
109 con->http.hostaddr.sin_port = lis->address.sin_port;
110
111 /*
112 * Get the hostname or format the IP address as needed...
113 */
114
115 address = ntohl(con->http.hostaddr.sin_addr.s_addr);
116
117 if (HostNameLookups)
118 #ifndef __sgi
119 host = gethostbyaddr((char *)&(con->http.hostaddr.sin_addr),
120 sizeof(struct in_addr), AF_INET);
121 #else
122 host = gethostbyaddr(&(con->http.hostaddr.sin_addr),
123 sizeof(struct in_addr), AF_INET);
124 #endif /* !__sgi */
125 else
126 host = NULL;
127
128 if (con->http.hostaddr.sin_addr.s_addr == ServerAddr.sin_addr.s_addr)
129 {
130 /*
131 * Map accesses from the same host to the server name.
132 */
133
134 strncpy(con->http.hostname, ServerName, sizeof(con->http.hostname) - 1);
135 }
136 else if (host == NULL)
137 sprintf(con->http.hostname, "%d.%d.%d.%d", (address >> 24) & 255,
138 (address >> 16) & 255, (address >> 8) & 255, address & 255);
139 else
140 strncpy(con->http.hostname, host->h_name, sizeof(con->http.hostname) - 1);
141
142 LogMessage(L_DEBUG, "AcceptClient() %d from %s:%d.", con->http.fd,
143 con->http.hostname, ntohs(con->http.hostaddr.sin_port));
144
145 /*
146 * Add the socket to the select() input mask.
147 */
148
149 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
150
151 DEBUG_printf(("AcceptClient: Adding fd %d to InputSet...\n", con->http.fd));
152 FD_SET(con->http.fd, &InputSet);
153
154 NumClients ++;
155
156 /*
157 * Temporarily suspend accept()'s until we lose a client...
158 */
159
160 if (NumClients == MaxClients)
161 PauseListening();
162
163 #ifdef HAVE_LIBSSL
164 /*
165 * See if we are connecting on port 443...
166 */
167
168 if (ntohs(con->http.hostaddr.sin_port) == 443)
169 {
170 /*
171 * https connection; go secure...
172 */
173
174 EncryptClient(con);
175 }
176 #endif /* HAVE_LIBSSL */
177 }
178
179
180 /*
181 * 'CloseAllClients()' - Close all remote clients immediately.
182 */
183
184 void
185 CloseAllClients(void)
186 {
187 while (NumClients > 0)
188 CloseClient(Clients);
189 }
190
191
192 /*
193 * 'CloseClient()' - Close a remote client.
194 */
195
196 void
197 CloseClient(client_t *con) /* I - Client to close */
198 {
199 int status; /* Exit status of pipe command */
200 #ifdef HAVE_LIBSSL
201 SSL_CTX *context; /* Context for encryption */
202 SSL *conn; /* Connection for encryption */
203 #endif /* HAVE_LIBSSL */
204
205
206 LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd);
207
208 #ifdef HAVE_LIBSSL
209 /*
210 * Shutdown encryption as needed...
211 */
212
213 if (con->http.tls)
214 {
215 conn = (SSL *)(con->http.tls);
216 context = SSL_get_SSL_CTX(conn);
217
218 SSL_shutdown(conn);
219 SSL_CTX_free(context);
220 SSL_free(conn);
221
222 con->http.tls = NULL;
223 }
224 #endif /* HAVE_LIBSSL */
225
226 /*
227 * Close the socket and clear the file from the input set for select()...
228 */
229
230 if (con->http.fd > 0)
231 {
232 DEBUG_printf(("CloseClient: Removing fd %d from InputSet...\n", con->http.fd));
233 close(con->http.fd);
234 FD_CLR(con->http.fd, &InputSet);
235 FD_CLR(con->http.fd, &OutputSet);
236 con->http.fd = 0;
237 }
238
239 if (con->pipe_pid != 0)
240 {
241 DEBUG_printf(("CloseClient: Removing fd %d from InputSet...\n", con->file));
242 FD_CLR(con->file, &InputSet);
243 }
244
245 if (con->file)
246 {
247 /*
248 * Close the open data file...
249 */
250
251 if (con->pipe_pid)
252 {
253 kill(con->pipe_pid, SIGKILL);
254 waitpid(con->pipe_pid, &status, WNOHANG);
255 }
256
257 FD_CLR(con->file, &InputSet);
258 close(con->file);
259 con->file = 0;
260 }
261
262 if (con->request)
263 {
264 ippDelete(con->request);
265 con->request = NULL;
266 }
267
268 if (con->response)
269 {
270 ippDelete(con->response);
271 con->response = NULL;
272 }
273
274 if (con->language)
275 {
276 cupsLangFree(con->language);
277 con->language = NULL;
278 }
279
280 /*
281 * Re-enable new client connections if we are going back under the
282 * limit...
283 */
284
285 if (NumClients == MaxClients)
286 ResumeListening();
287
288 /*
289 * Compact the list of clients as necessary...
290 */
291
292 NumClients --;
293
294 if (con < (Clients + NumClients))
295 memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
296 }
297
298
299 /*
300 * 'EncryptClient()' - Enable encryption for the client...
301 */
302
303 int /* O - 1 on success, 0 on error */
304 EncryptClient(client_t *con) /* I - Client to encrypt */
305 {
306 #ifdef HAVE_LIBSSL
307 SSL_CTX *context; /* Context for encryption */
308 SSL *conn; /* Connection for encryption */
309
310 context = SSL_CTX_new(SSLv23_method());
311 conn = SSL_new(context);
312
313 SSL_use_PrivateKey_file(conn, ServerKey, SSL_FILETYPE_PEM);
314 SSL_use_certificate_file(conn, ServerCertificate, SSL_FILETYPE_PEM);
315
316 SSL_set_fd(conn, con->http.fd);
317 if (SSL_accept(conn) != 1)
318 {
319 SSL_CTX_free(context);
320 SSL_free(conn);
321 return (0);
322 }
323
324 con->http.tls = conn;
325 return (1);
326 #else
327 return (0);
328 #endif /* HAVE_LIBSSL */
329 }
330
331
332 /*
333 * 'ReadClient()' - Read data from a client.
334 */
335
336 int /* O - 1 on success, 0 on error */
337 ReadClient(client_t *con) /* I - Client to read from */
338 {
339 char line[8192], /* Line from client... */
340 operation[64], /* Operation code from socket */
341 version[64]; /* HTTP version number string */
342 int major, minor; /* HTTP version numbers */
343 http_status_t status; /* Transfer status */
344 ipp_state_t ipp_state; /* State of IPP transfer */
345 int bytes; /* Number of bytes to POST */
346 char *filename; /* Name of file for GET/HEAD */
347 struct stat filestats; /* File information */
348 mime_type_t *type; /* MIME type of file */
349 printer_t *p; /* Printer */
350 location_t *best; /* Best match for authentication */
351 static unsigned request_id = 0;/* Request ID for temp files */
352
353
354 status = HTTP_CONTINUE;
355
356 switch (con->http.state)
357 {
358 case HTTP_WAITING :
359 /*
360 * See if we've received a request line...
361 */
362
363 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
364 {
365 CloseClient(con);
366 return (0);
367 }
368
369 /*
370 * Ignore blank request lines...
371 */
372
373 if (line[0] == '\0')
374 break;
375
376 /*
377 * Clear other state variables...
378 */
379
380 httpClearFields(HTTP(con));
381
382 con->http.activity = time(NULL);
383 con->http.version = HTTP_1_0;
384 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
385 con->http.data_encoding = HTTP_ENCODE_LENGTH;
386 con->http.data_remaining = 0;
387 con->operation = HTTP_WAITING;
388 con->bytes = 0;
389 con->file = 0;
390 con->pipe_pid = 0;
391 con->command[0] = '\0';
392 con->username[0] = '\0';
393 con->password[0] = '\0';
394 con->uri[0] = '\0';
395
396 if (con->language != NULL)
397 {
398 cupsLangFree(con->language);
399 con->language = NULL;
400 }
401
402 /*
403 * Grab the request line...
404 */
405
406 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
407 {
408 case 1 :
409 SendError(con, HTTP_BAD_REQUEST);
410 CloseClient(con);
411 return (0);
412 case 2 :
413 con->http.version = HTTP_0_9;
414 break;
415 case 3 :
416 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
417 {
418 SendError(con, HTTP_BAD_REQUEST);
419 CloseClient(con);
420 return (0);
421 }
422
423 if (major < 2)
424 {
425 con->http.version = (http_version_t)(major * 100 + minor);
426 if (con->http.version == HTTP_1_1)
427 con->http.keep_alive = HTTP_KEEPALIVE_ON;
428 else
429 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
430 }
431 else
432 {
433 SendError(con, HTTP_NOT_SUPPORTED);
434 CloseClient(con);
435 return (0);
436 }
437 break;
438 }
439
440 /*
441 * Process the request...
442 */
443
444 if (strcmp(operation, "GET") == 0)
445 con->http.state = HTTP_GET;
446 else if (strcmp(operation, "PUT") == 0)
447 con->http.state = HTTP_PUT;
448 else if (strcmp(operation, "POST") == 0)
449 con->http.state = HTTP_POST;
450 else if (strcmp(operation, "DELETE") == 0)
451 con->http.state = HTTP_DELETE;
452 else if (strcmp(operation, "TRACE") == 0)
453 con->http.state = HTTP_TRACE;
454 else if (strcmp(operation, "OPTIONS") == 0)
455 con->http.state = HTTP_OPTIONS;
456 else if (strcmp(operation, "HEAD") == 0)
457 con->http.state = HTTP_HEAD;
458 else
459 {
460 SendError(con, HTTP_BAD_REQUEST);
461 CloseClient(con);
462 return (0);
463 }
464
465 con->start = time(NULL);
466 con->operation = con->http.state;
467
468 LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
469 operation, con->uri,
470 con->http.version / 100, con->http.version % 100);
471
472 con->http.status = HTTP_OK;
473
474 case HTTP_OPTIONS :
475 case HTTP_DELETE :
476 case HTTP_GET :
477 case HTTP_HEAD :
478 case HTTP_POST :
479 case HTTP_PUT :
480 case HTTP_TRACE :
481 /*
482 * Parse incoming parameters until the status changes...
483 */
484
485 status = httpUpdate(HTTP(con));
486
487 if (status != HTTP_OK && status != HTTP_CONTINUE)
488 {
489 SendError(con, HTTP_BAD_REQUEST);
490 CloseClient(con);
491 return (0);
492 }
493 break;
494
495 default :
496 break; /* Anti-compiler-warning-code */
497 }
498
499 /*
500 * Handle new transfers...
501 */
502
503 if (status == HTTP_OK)
504 {
505 con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
506
507 decode_auth(con);
508
509 if (strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) == 0)
510 con->http.keep_alive = HTTP_KEEPALIVE_ON;
511
512 if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
513 con->http.version >= HTTP_1_1)
514 {
515 /*
516 * HTTP/1.1 and higher require the "Host:" field...
517 */
518
519 if (!SendError(con, HTTP_BAD_REQUEST))
520 {
521 CloseClient(con);
522 return (0);
523 }
524 }
525 else if (con->operation == HTTP_OPTIONS)
526 {
527 /*
528 * Do OPTIONS command...
529 */
530
531 if ((best = FindBest(con)) != NULL &&
532 best->type != AUTH_NONE)
533 {
534 if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL))
535 {
536 CloseClient(con);
537 return (0);
538 }
539 }
540
541 if ((strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 ||
542 (best != NULL && best->encryption == HTTP_ENCRYPT_ALWAYS)) &&
543 con->http.tls == NULL)
544 {
545 #ifdef HAVE_LIBSSL
546 /*
547 * Do encryption stuff...
548 */
549
550 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
551 {
552 CloseClient(con);
553 return (0);
554 }
555
556 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
557 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,SSL/2.0,SSL/3.0\r\n");
558 httpPrintf(HTTP(con), "\r\n");
559
560 EncryptClient(con);
561 #else
562 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
563 {
564 CloseClient(con);
565 return (0);
566 }
567 #endif /* HAVE_LIBSSL */
568 }
569
570 if (!SendHeader(con, HTTP_OK, NULL))
571 {
572 CloseClient(con);
573 return (0);
574 }
575
576 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n");
577 httpPrintf(HTTP(con), "\r\n");
578 }
579 else if (strstr(con->uri, "..") != NULL)
580 {
581 /*
582 * Protect against malicious users!
583 */
584
585 if (!SendError(con, HTTP_FORBIDDEN))
586 {
587 CloseClient(con);
588 return (0);
589 }
590 }
591 else if (con->uri[0] != '/')
592 {
593 /*
594 * Don't allow proxying (yet)...
595 */
596
597 if (!SendError(con, HTTP_METHOD_NOT_ALLOWED))
598 {
599 CloseClient(con);
600 return (0);
601 }
602 }
603 else
604 {
605 if ((status = IsAuthorized(con)) == HTTP_UPGRADE_REQUIRED)
606 {
607 #ifdef HAVE_LIBSSL
608 /*
609 * Do encryption stuff...
610 */
611
612 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
613 {
614 CloseClient(con);
615 return (0);
616 }
617
618 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
619 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,SSL/2.0,SSL/3.0\r\n");
620 httpPrintf(HTTP(con), "\r\n");
621
622 EncryptClient(con);
623 #else
624 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
625 {
626 CloseClient(con);
627 return (0);
628 }
629 #endif /* HAVE_LIBSSL */
630 }
631 else if (status != HTTP_OK)
632 {
633 SendError(con, status);
634 CloseClient(con);
635 return (0);
636 }
637
638 switch (con->http.state)
639 {
640 case HTTP_GET_SEND :
641 if (strncmp(con->uri, "/printers/", 10) == 0 &&
642 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
643 {
644 /*
645 * Send PPD file - get the real printer name since printer
646 * names are not case sensitive but filenames can be...
647 */
648
649 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
650
651 if ((p = FindPrinter(con->uri + 10)) != NULL)
652 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
653 else
654 {
655 if (!SendError(con, HTTP_NOT_FOUND))
656 {
657 CloseClient(con);
658 return (0);
659 }
660
661 break;
662 }
663 }
664
665 if (strncmp(con->uri, "/admin", 6) == 0 ||
666 strncmp(con->uri, "/printers", 9) == 0 ||
667 strncmp(con->uri, "/classes", 8) == 0 ||
668 strncmp(con->uri, "/jobs", 5) == 0)
669 {
670 /*
671 * Send CGI output...
672 */
673
674 if (strncmp(con->uri, "/admin", 6) == 0)
675 {
676 snprintf(con->command, sizeof(con->command),
677 "%s/cgi-bin/admin.cgi", ServerBin);
678 con->options = con->uri + 6;
679 }
680 else if (strncmp(con->uri, "/printers", 9) == 0)
681 {
682 snprintf(con->command, sizeof(con->command),
683 "%s/cgi-bin/printers.cgi", ServerBin);
684 con->options = con->uri + 9;
685 }
686 else if (strncmp(con->uri, "/classes", 8) == 0)
687 {
688 snprintf(con->command, sizeof(con->command),
689 "%s/cgi-bin/classes.cgi", ServerBin);
690 con->options = con->uri + 8;
691 }
692 else
693 {
694 snprintf(con->command, sizeof(con->command),
695 "%s/cgi-bin/jobs.cgi", ServerBin);
696 con->options = con->uri + 5;
697 }
698
699 if (con->options[0] == '/')
700 con->options ++;
701
702 if (!SendCommand(con, con->command, con->options))
703 {
704 if (!SendError(con, HTTP_NOT_FOUND))
705 {
706 CloseClient(con);
707 return (0);
708 }
709 }
710 else
711 LogRequest(con, HTTP_OK);
712
713 if (con->http.version <= HTTP_1_0)
714 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
715 }
716 else
717 {
718 /*
719 * Serve a file...
720 */
721
722 if ((filename = get_file(con, &filestats)) == NULL)
723 {
724 if (!SendError(con, HTTP_NOT_FOUND))
725 {
726 CloseClient(con);
727 return (0);
728 }
729 }
730 else if (!check_if_modified(con, &filestats))
731 {
732 if (!SendError(con, HTTP_NOT_MODIFIED))
733 {
734 CloseClient(con);
735 return (0);
736 }
737 }
738 else
739 {
740 type = mimeFileType(MimeDatabase, filename);
741 if (type == NULL)
742 strcpy(line, "text/plain");
743 else
744 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
745
746 if (!SendFile(con, HTTP_OK, filename, line, &filestats))
747 {
748 CloseClient(con);
749 return (0);
750 }
751 }
752 }
753 break;
754
755 case HTTP_POST_RECV :
756 /*
757 * See if the POST request includes a Content-Length field, and if
758 * so check the length against any limits that are set...
759 */
760
761 LogMessage(L_DEBUG2, "POST %s", con->uri);
762 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
763
764 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
765 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
766 MaxRequestSize > 0)
767 {
768 /*
769 * Request too large...
770 */
771
772 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
773 {
774 CloseClient(con);
775 return (0);
776 }
777
778 break;
779 }
780
781 /*
782 * See what kind of POST request this is; for IPP requests the
783 * content-type field will be "application/ipp"...
784 */
785
786 if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
787 con->request = ippNew();
788 else if (strncmp(con->uri, "/admin", 6) == 0 ||
789 strncmp(con->uri, "/printers", 9) == 0 ||
790 strncmp(con->uri, "/classes", 8) == 0 ||
791 strncmp(con->uri, "/jobs", 5) == 0)
792 {
793 /*
794 * CGI request...
795 */
796
797 if (strncmp(con->uri, "/admin", 6) == 0)
798 {
799 snprintf(con->command, sizeof(con->command),
800 "%s/cgi-bin/admin.cgi", ServerBin);
801 con->options = con->uri + 6;
802 }
803 else if (strncmp(con->uri, "/printers", 9) == 0)
804 {
805 snprintf(con->command, sizeof(con->command),
806 "%s/cgi-bin/printers.cgi", ServerBin);
807 con->options = con->uri + 9;
808 }
809 else if (strncmp(con->uri, "/classes", 8) == 0)
810 {
811 snprintf(con->command, sizeof(con->command),
812 "%s/cgi-bin/classes.cgi", ServerBin);
813 con->options = con->uri + 8;
814 }
815 else
816 {
817 snprintf(con->command, sizeof(con->command),
818 "%s/cgi-bin/jobs.cgi", ServerBin);
819 con->options = con->uri + 5;
820 }
821
822 if (con->options[0] == '/')
823 con->options ++;
824
825 LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"",
826 con->http.fd, con->command, con->options);
827
828 if (con->http.version <= HTTP_1_0)
829 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
830 }
831 else if (!SendError(con, HTTP_UNAUTHORIZED))
832 {
833 CloseClient(con);
834 return (0);
835 }
836 break;
837
838 case HTTP_PUT_RECV :
839 case HTTP_DELETE :
840 case HTTP_TRACE :
841 SendError(con, HTTP_NOT_IMPLEMENTED);
842 CloseClient(con);
843 return (0);
844
845 case HTTP_HEAD :
846 if (strncmp(con->uri, "/printers", 9) == 0 &&
847 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
848 {
849 /*
850 * Send PPD file...
851 */
852
853 snprintf(con->command, sizeof(con->command),
854 "/ppd/%s", con->uri + 10);
855 strcpy(con->uri, con->command);
856 con->command[0] = '\0';
857 }
858
859 if (strncmp(con->uri, "/admin/", 7) == 0 ||
860 strncmp(con->uri, "/printers/", 10) == 0 ||
861 strncmp(con->uri, "/classes/", 9) == 0 ||
862 strncmp(con->uri, "/jobs/", 6) == 0)
863 {
864 /*
865 * CGI output...
866 */
867
868 if (!SendHeader(con, HTTP_OK, "text/html"))
869 {
870 CloseClient(con);
871 return (0);
872 }
873
874 if (httpPrintf(HTTP(con), "\r\n") < 0)
875 {
876 CloseClient(con);
877 return (0);
878 }
879
880 LogRequest(con, HTTP_OK);
881 }
882 else if ((filename = get_file(con, &filestats)) == NULL)
883 {
884 if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
885 {
886 CloseClient(con);
887 return (0);
888 }
889
890 LogRequest(con, HTTP_NOT_FOUND);
891 }
892 else if (!check_if_modified(con, &filestats))
893 {
894 if (!SendError(con, HTTP_NOT_MODIFIED))
895 {
896 CloseClient(con);
897 return (0);
898 }
899
900 LogRequest(con, HTTP_NOT_MODIFIED);
901 }
902 else
903 {
904 /*
905 * Serve a file...
906 */
907
908 type = mimeFileType(MimeDatabase, filename);
909 if (type == NULL)
910 strcpy(line, "text/plain");
911 else
912 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
913
914 if (!SendHeader(con, HTTP_OK, line))
915 {
916 CloseClient(con);
917 return (0);
918 }
919
920 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
921 httpGetDateString(filestats.st_mtime)) < 0)
922 {
923 CloseClient(con);
924 return (0);
925 }
926
927 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
928 filestats.st_size) < 0)
929 {
930 CloseClient(con);
931 return (0);
932 }
933
934 LogRequest(con, HTTP_OK);
935 }
936
937 if (httpPrintf(HTTP(con), "\r\n") < 0)
938 {
939 CloseClient(con);
940 return (0);
941 }
942
943 con->http.state = HTTP_WAITING;
944 break;
945
946 default :
947 break; /* Anti-compiler-warning-code */
948 }
949 }
950 }
951
952 /*
953 * Handle any incoming data...
954 */
955
956 switch (con->http.state)
957 {
958 case HTTP_PUT_RECV :
959 break;
960
961 case HTTP_POST_RECV :
962 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
963 con->http.fd,
964 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
965 con->http.data_remaining, con->file);
966
967 if (con->request != NULL)
968 {
969 /*
970 * Grab any request data from the connection...
971 */
972
973 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
974 {
975 LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!",
976 con->http.fd);
977 CloseClient(con);
978 return (0);
979 }
980 else if (ipp_state != IPP_DATA)
981 break;
982 else
983 con->bytes += ippLength(con->request);
984 }
985
986 if (con->file == 0 && con->http.state != HTTP_POST_SEND)
987 {
988 /*
989 * Create a file as needed for the request data...
990 */
991
992 snprintf(con->filename, sizeof(con->filename), "%s/%08x",
993 RequestRoot, request_id ++);
994 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
995 fchmod(con->file, 0640);
996 fchown(con->file, User, Group);
997
998 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s", con->http.fd,
999 con->filename);
1000
1001 if (con->file < 0)
1002 {
1003 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1004 {
1005 CloseClient(con);
1006 return (0);
1007 }
1008 }
1009 }
1010
1011 if (con->http.state != HTTP_POST_SEND)
1012 {
1013 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
1014 {
1015 CloseClient(con);
1016 return (0);
1017 }
1018 else if (bytes > 0)
1019 {
1020 con->bytes += bytes;
1021
1022 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes",
1023 con->http.fd, bytes);
1024
1025 if (write(con->file, line, bytes) < bytes)
1026 {
1027 close(con->file);
1028 con->file = 0;
1029 unlink(con->filename);
1030
1031 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1032 {
1033 CloseClient(con);
1034 return (0);
1035 }
1036 }
1037 }
1038 else if (con->http.state != HTTP_POST_SEND)
1039 {
1040 CloseClient(con);
1041 return (0);
1042 }
1043 }
1044
1045 if (con->http.state == HTTP_POST_SEND)
1046 {
1047 if (con->file)
1048 {
1049 fstat(con->file, &filestats);
1050 close(con->file);
1051 con->file = 0;
1052
1053 if (filestats.st_size > MaxRequestSize &&
1054 MaxRequestSize > 0)
1055 {
1056 /*
1057 * Request is too big; remove it and send an error...
1058 */
1059
1060 unlink(con->filename);
1061
1062 if (con->request)
1063 {
1064 /*
1065 * Delete any IPP request data...
1066 */
1067
1068 ippDelete(con->request);
1069 con->request = NULL;
1070 }
1071
1072 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1073 {
1074 CloseClient(con);
1075 return (0);
1076 }
1077 }
1078
1079 if (con->command[0])
1080 {
1081 if (!SendCommand(con, con->command, con->options))
1082 {
1083 if (!SendError(con, HTTP_NOT_FOUND))
1084 {
1085 CloseClient(con);
1086 return (0);
1087 }
1088 }
1089 else
1090 LogRequest(con, HTTP_OK);
1091 }
1092 }
1093
1094 if (con->request)
1095 ProcessIPPRequest(con);
1096 }
1097 break;
1098
1099 default :
1100 break; /* Anti-compiler-warning-code */
1101 }
1102
1103 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
1104 {
1105 CloseClient(con);
1106 return (0);
1107 }
1108 else
1109 return (1);
1110 }
1111
1112
1113 /*
1114 * 'SendCommand()' - Send output from a command via HTTP.
1115 */
1116
1117 int
1118 SendCommand(client_t *con,
1119 char *command,
1120 char *options)
1121 {
1122 int fd;
1123
1124
1125 if (con->filename[0])
1126 fd = open(con->filename, O_RDONLY);
1127 else
1128 fd = open("/dev/null", O_RDONLY);
1129
1130 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
1131
1132 close(fd);
1133
1134 LogMessage(L_DEBUG, "SendCommand() %d command=\"%s\" file=%d pipe_pid=%d",
1135 con->http.fd, command, con->file, con->pipe_pid);
1136
1137 if (con->pipe_pid == 0)
1138 return (0);
1139
1140 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1141
1142 DEBUG_printf(("SendCommand: Adding fd %d to InputSet...\n", con->file));
1143 FD_SET(con->file, &InputSet);
1144 FD_SET(con->http.fd, &OutputSet);
1145
1146 if (!SendHeader(con, HTTP_OK, NULL))
1147 return (0);
1148
1149 if (con->http.version == HTTP_1_1)
1150 {
1151 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
1152
1153 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
1154 return (0);
1155 }
1156
1157 con->got_fields = 0;
1158 con->field_col = 0;
1159
1160 return (1);
1161 }
1162
1163
1164 /*
1165 * 'SendError()' - Send an error message via HTTP.
1166 */
1167
1168 int /* O - 1 if successful, 0 otherwise */
1169 SendError(client_t *con, /* I - Connection */
1170 http_status_t code) /* I - Error code */
1171 {
1172 char message[1024]; /* Message for user */
1173
1174
1175 /*
1176 * Put the request in the access_log file...
1177 */
1178
1179 if (con->operation > HTTP_WAITING)
1180 LogRequest(con, code);
1181
1182 /*
1183 * To work around bugs in some proxies, don't use Keep-Alive for some
1184 * error messages...
1185 */
1186
1187 if (code >= HTTP_BAD_REQUEST)
1188 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1189
1190 /*
1191 * Send an error message back to the client. If the error code is a
1192 * 400 or 500 series, make sure the message contains some text, too!
1193 */
1194
1195 if (!SendHeader(con, code, NULL))
1196 return (0);
1197
1198 #ifdef HAVE_LIBSSL
1199 if (code == HTTP_UPGRADE_REQUIRED)
1200 {
1201 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
1202 return (0);
1203
1204 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,SSL/2.0,SSL/3.0\r\n") < 0)
1205 return (0);
1206 }
1207 #endif /* HAVE_LIBSSL */
1208
1209 if (con->http.version >= HTTP_1_1 && !con->http.keep_alive)
1210 {
1211 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
1212 return (0);
1213 }
1214
1215 if (code >= HTTP_BAD_REQUEST)
1216 {
1217 /*
1218 * Send a human-readable error message.
1219 */
1220
1221 snprintf(message, sizeof(message),
1222 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
1223 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
1224 code, httpStatus(code), httpStatus(code),
1225 con->language ? con->language->messages[code] :
1226 httpStatus(code));
1227
1228 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
1229 return (0);
1230 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", strlen(message)) < 0)
1231 return (0);
1232 if (httpPrintf(HTTP(con), "\r\n") < 0)
1233 return (0);
1234 if (httpPrintf(HTTP(con), "%s", message) < 0)
1235 return (0);
1236 }
1237 else if (httpPrintf(HTTP(con), "\r\n") < 0)
1238 return (0);
1239
1240 con->http.state = HTTP_WAITING;
1241
1242 return (1);
1243 }
1244
1245
1246 /*
1247 * 'SendFile()' - Send a file via HTTP.
1248 */
1249
1250 int
1251 SendFile(client_t *con,
1252 http_status_t code,
1253 char *filename,
1254 char *type,
1255 struct stat *filestats)
1256 {
1257 con->file = open(filename, O_RDONLY);
1258
1259 LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
1260
1261 if (con->file < 0)
1262 return (0);
1263
1264 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1265
1266 con->pipe_pid = 0;
1267
1268 if (!SendHeader(con, code, type))
1269 return (0);
1270
1271 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
1272 return (0);
1273 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", filestats->st_size) < 0)
1274 return (0);
1275 if (httpPrintf(HTTP(con), "\r\n") < 0)
1276 return (0);
1277
1278 FD_SET(con->http.fd, &OutputSet);
1279
1280 return (1);
1281 }
1282
1283
1284 /*
1285 * 'SendHeader()' - Send an HTTP request.
1286 */
1287
1288 int /* O - 1 on success, 0 on failure */
1289 SendHeader(client_t *con, /* I - Client to send to */
1290 http_status_t code, /* I - HTTP status code */
1291 char *type) /* I - MIME type of document */
1292 {
1293 location_t *loc; /* Authentication location */
1294
1295
1296 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
1297 con->http.version % 100, code, httpStatus(code)) < 0)
1298 return (0);
1299 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
1300 return (0);
1301 if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
1302 return (0);
1303 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
1304 {
1305 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
1306 return (0);
1307 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
1308 return (0);
1309 }
1310 if (code == HTTP_METHOD_NOT_ALLOWED)
1311 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
1312 return (0);
1313
1314 if (code == HTTP_UNAUTHORIZED)
1315 {
1316 loc = FindBest(con); /* This already succeeded in IsAuthorized */
1317
1318 if (loc->type == AUTH_BASIC)
1319 {
1320 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
1321 return (0);
1322 }
1323 else
1324 {
1325 if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" "
1326 "nonce=\"%s\"\r\n", con->http.hostname) < 0)
1327 return (0);
1328 }
1329 }
1330 if (con->language != NULL)
1331 {
1332 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
1333 con->language->language) < 0)
1334 return (0);
1335
1336 if (type != NULL)
1337 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
1338 cupsLangEncoding(con->language)) < 0)
1339 return (0);
1340 }
1341 else if (type != NULL)
1342 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
1343 return (0);
1344
1345 return (1);
1346 }
1347
1348
1349 /*
1350 * 'WriteClient()' - Write data to a client as needed.
1351 */
1352
1353 int /* O - 1 if success, 0 if fail */
1354 WriteClient(client_t *con) /* I - Client connection */
1355 {
1356 int bytes; /* Number of bytes written */
1357 char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */
1358 char *bufptr; /* Pointer into buffer */
1359 ipp_state_t ipp_state; /* IPP state value */
1360
1361
1362 if (con->http.state != HTTP_GET_SEND &&
1363 con->http.state != HTTP_POST_SEND)
1364 return (1);
1365
1366 if (con->response != NULL)
1367 {
1368 ipp_state = ippWrite(&(con->http), con->response);
1369 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
1370 }
1371 else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
1372 {
1373 if (con->pipe_pid && !con->got_fields)
1374 {
1375 /*
1376 * Inspect the data for Content-Type and other fields.
1377 */
1378
1379 buf[bytes] = '\0';
1380
1381 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
1382 if (*bufptr == '\n')
1383 {
1384 /*
1385 * Send line to client...
1386 */
1387
1388 if (bufptr > buf && bufptr[-1] == '\r')
1389 bufptr[-1] = '\0';
1390 *bufptr++ = '\0';
1391
1392 httpPrintf(HTTP(con), "%s\r\n", buf);
1393 LogMessage(L_DEBUG2, "WriteClient() %d %s", con->http.fd, buf);
1394
1395 /*
1396 * Update buffer...
1397 */
1398
1399 bytes -= (bufptr - buf);
1400 memcpy(buf, bufptr, bytes + 1);
1401 bufptr = buf - 1;
1402
1403 /*
1404 * See if the line was empty...
1405 */
1406
1407 if (con->field_col == 0)
1408 con->got_fields = 1;
1409 else
1410 con->field_col = 0;
1411 }
1412 else if (*bufptr != '\r')
1413 con->field_col ++;
1414
1415 if (bytes > 0 && !con->got_fields)
1416 {
1417 /*
1418 * Remaining text needs to go out...
1419 */
1420
1421 httpPrintf(HTTP(con), "%s", buf);
1422
1423 con->http.activity = time(NULL);
1424 return (1);
1425 }
1426 else if (bytes == 0)
1427 {
1428 con->http.activity = time(NULL);
1429 return (1);
1430 }
1431 }
1432
1433 if (httpWrite(HTTP(con), buf, bytes) < 0)
1434 {
1435 CloseClient(con);
1436 return (0);
1437 }
1438
1439 con->bytes += bytes;
1440 }
1441
1442 if (bytes <= 0)
1443 {
1444 LogRequest(con, HTTP_OK);
1445
1446 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
1447 {
1448 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
1449 {
1450 CloseClient(con);
1451 return (0);
1452 }
1453 }
1454
1455 con->http.state = HTTP_WAITING;
1456
1457 FD_CLR(con->http.fd, &OutputSet);
1458
1459 if (con->file)
1460 {
1461 DEBUG_printf(("WriteClient: Removing fd %d from InputSet...\n", con->file));
1462 FD_CLR(con->file, &InputSet);
1463
1464 if (con->pipe_pid)
1465 kill(con->pipe_pid, SIGTERM);
1466
1467 close(con->file);
1468 con->file = 0;
1469 con->pipe_pid = 0;
1470 }
1471
1472 if (con->filename[0])
1473 unlink(con->filename);
1474
1475 if (con->request != NULL)
1476 {
1477 ippDelete(con->request);
1478 con->request = NULL;
1479 }
1480
1481 if (con->response != NULL)
1482 {
1483 ippDelete(con->response);
1484 con->response = NULL;
1485 }
1486
1487 if (!con->http.keep_alive)
1488 {
1489 CloseClient(con);
1490 return (0);
1491 }
1492 }
1493
1494 if (bytes >= 1024)
1495 LogMessage(L_DEBUG2, "WriteClient() %d %d bytes", con->http.fd, bytes);
1496
1497 con->http.activity = time(NULL);
1498
1499 return (1);
1500 }
1501
1502
1503 /*
1504 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
1505 */
1506
1507 static int /* O - 1 if modified since */
1508 check_if_modified(client_t *con, /* I - Client connection */
1509 struct stat *filestats) /* I - File information */
1510 {
1511 char *ptr; /* Pointer into field */
1512 time_t date; /* Time/date value */
1513 int size; /* Size/length value */
1514
1515
1516 size = 0;
1517 date = 0;
1518 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
1519
1520 if (*ptr == '\0')
1521 return (1);
1522
1523 LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"",
1524 con->http.fd, ptr);
1525
1526 while (*ptr != '\0')
1527 {
1528 while (isspace(*ptr) || *ptr == ';')
1529 ptr ++;
1530
1531 if (strncasecmp(ptr, "length=", 7) == 0)
1532 {
1533 ptr += 7;
1534 size = atoi(ptr);
1535
1536 while (isdigit(*ptr))
1537 ptr ++;
1538 }
1539 else if (isalpha(*ptr))
1540 {
1541 date = httpGetDateTime(ptr);
1542 while (*ptr != '\0' && *ptr != ';')
1543 ptr ++;
1544 }
1545 }
1546
1547 LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
1548 con->http.fd, size, filestats->st_size, date, filestats->st_mtime);
1549
1550 return ((size != filestats->st_size && size != 0) ||
1551 (date < filestats->st_mtime && date != 0) ||
1552 (size == 0 && date == 0));
1553 }
1554
1555
1556 /*
1557 * 'decode_auth()' - Decode an authorization string.
1558 */
1559
1560 static void
1561 decode_auth(client_t *con) /* I - Client to decode to */
1562 {
1563 char *s, /* Authorization string */
1564 value[1024]; /* Value string */
1565 const char *username; /* Certificate username */
1566
1567
1568 /*
1569 * Decode the string...
1570 */
1571
1572 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
1573
1574 DEBUG_printf(("decode_auth(%08x): Authorization string = \"%s\"\n",
1575 con, s));
1576
1577 if (strncmp(s, "Basic", 5) == 0)
1578 {
1579 s += 5;
1580 while (isspace(*s))
1581 s ++;
1582
1583 httpDecode64(value, s);
1584
1585 /*
1586 * Pull the username and password out...
1587 */
1588
1589 if ((s = strchr(value, ':')) == NULL)
1590 {
1591 LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
1592 con->http.fd, value);
1593 return;
1594 }
1595
1596 *s++ = '\0';
1597
1598 strncpy(con->username, value, sizeof(con->username) - 1);
1599 con->username[sizeof(con->username) - 1] = '\0';
1600
1601 strncpy(con->password, s, sizeof(con->password) - 1);
1602 con->password[sizeof(con->password) - 1] = '\0';
1603 }
1604 else if (strncmp(s, "Local", 5) == 0)
1605 {
1606 s += 5;
1607 while (isspace(*s))
1608 s ++;
1609
1610 if ((username = FindCert(s)) != NULL)
1611 {
1612 strncpy(con->username, username, sizeof(con->username) - 1);
1613 con->username[sizeof(con->username) - 1] = '\0';
1614 }
1615 }
1616 else if (strncmp(s, "Digest", 5) == 0)
1617 {
1618 /*
1619 * Get the username and password from the Digest attributes...
1620 */
1621
1622 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username",
1623 value))
1624 {
1625 strncpy(con->username, value, sizeof(con->username) - 1);
1626 con->username[sizeof(con->username) - 1] = '\0';
1627 }
1628
1629 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response",
1630 value))
1631 {
1632 strncpy(con->password, value, sizeof(con->password) - 1);
1633 con->password[sizeof(con->password) - 1] = '\0';
1634 }
1635 }
1636
1637 LogMessage(L_DEBUG, "decode_auth() %d username=\"%s\"",
1638 con->http.fd, con->username);
1639 DEBUG_printf(("decode_auth() %d username=\"%s\" password=\"%s\"\n",
1640 con->http.fd, con->username, con->password));
1641 }
1642
1643
1644 /*
1645 * 'get_file()' - Get a filename and state info.
1646 */
1647
1648 static char * /* O - Real filename */
1649 get_file(client_t *con, /* I - Client connection */
1650 struct stat *filestats)/* O - File information */
1651 {
1652 int status; /* Status of filesystem calls */
1653 char *params; /* Pointer to parameters in URI */
1654 static char filename[1024]; /* Filename buffer */
1655
1656
1657 /*
1658 * Need to add DocumentRoot global...
1659 */
1660
1661 if (strncmp(con->uri, "/ppd/", 5) == 0)
1662 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri);
1663 else if (con->language != NULL)
1664 snprintf(filename, sizeof(filename), "%s/%s%s", DocumentRoot, con->language->language,
1665 con->uri);
1666 else
1667 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
1668
1669 if ((params = strchr(filename, '?')) != NULL)
1670 *params = '\0';
1671
1672 /*
1673 * Grab the status for this language; if there isn't a language-specific file
1674 * then fallback to the default one...
1675 */
1676
1677 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
1678 {
1679 /*
1680 * Drop the language prefix and try the current directory...
1681 */
1682
1683 if (strncmp(con->uri, "/ppd/", 5) != 0)
1684 {
1685 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
1686
1687 status = stat(filename, filestats);
1688 }
1689 }
1690
1691 /*
1692 * If we're found a directory, get the index.html file instead...
1693 */
1694
1695 if (!status && S_ISDIR(filestats->st_mode))
1696 {
1697 if (filename[strlen(filename) - 1] == '/')
1698 strcat(filename, "index.html");
1699 else
1700 strcat(filename, "/index.html");
1701
1702 status = stat(filename, filestats);
1703 }
1704
1705 LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d",
1706 con->http.fd, filename, status ? -1 : filestats->st_size);
1707
1708 if (status)
1709 return (NULL);
1710 else
1711 return (filename);
1712 }
1713
1714
1715 /*
1716 * 'pipe_command()' - Pipe the output of a command to the remote client.
1717 */
1718
1719 static int /* O - Process ID */
1720 pipe_command(client_t *con, /* I - Client connection */
1721 int infile, /* I - Standard input for command */
1722 int *outfile, /* O - Standard output for command */
1723 char *command, /* I - Command to run */
1724 char *options) /* I - Options for command */
1725 {
1726 int pid; /* Process ID */
1727 char *commptr; /* Command string pointer */
1728 int fd; /* Looping var */
1729 int fds[2]; /* Pipe FDs */
1730 int argc; /* Number of arguments */
1731 int envc; /* Number of environment variables */
1732 char argbuf[10240], /* Argument buffer */
1733 *argv[100], /* Argument strings */
1734 *envp[100]; /* Environment variables */
1735 char lang[1024], /* LANG env variable */
1736 content_length[1024], /* CONTENT_LENGTH env variable */
1737 content_type[1024], /* CONTENT_TYPE env variable */
1738 ipp_port[1024], /* Default listen port */
1739 server_port[1024], /* Default server port */
1740 server_name[1024], /* Default listen hostname */
1741 remote_host[1024], /* REMOTE_HOST env variable */
1742 remote_user[1024], /* REMOTE_USER env variable */
1743 tmpdir[1024], /* TMPDIR environment variable */
1744 ldpath[1024], /* LD_LIBRARY_PATH environment variable */
1745 datadir[1024], /* CUPS_DATADIR environment variable */
1746 root[1024], /* CUPS_SERVERROOT environment variable */
1747 query_string[10240]; /* QUERY_STRING env variable */
1748
1749
1750 /*
1751 * Copy the command string...
1752 */
1753
1754 strncpy(argbuf, options, sizeof(argbuf) - 1);
1755 argbuf[sizeof(argbuf) - 1] = '\0';
1756
1757 /*
1758 * Parse the string; arguments can be separated by + and are terminated
1759 * by ?...
1760 */
1761
1762 argv[0] = argbuf;
1763
1764 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
1765 if (*commptr == ' ' || *commptr == '+')
1766 {
1767 *commptr++ = '\0';
1768
1769 while (*commptr == ' ')
1770 commptr ++;
1771
1772 if (*commptr != '\0')
1773 {
1774 argv[argc] = commptr;
1775 argc ++;
1776 }
1777
1778 commptr --;
1779 }
1780 else if (*commptr == '%')
1781 {
1782 if (commptr[1] >= '0' && commptr[1] <= '9')
1783 *commptr = (commptr[1] - '0') << 4;
1784 else
1785 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
1786
1787 if (commptr[2] >= '0' && commptr[2] <= '9')
1788 *commptr |= commptr[2] - '0';
1789 else
1790 *commptr |= tolower(commptr[2]) - 'a' + 10;
1791
1792 strcpy(commptr + 1, commptr + 3);
1793 }
1794 else if (*commptr == '?')
1795 break;
1796
1797 argv[argc] = NULL;
1798
1799 if (argv[0][0] == '\0')
1800 argv[0] = strrchr(command, '/') + 1;
1801
1802 /*
1803 * Setup the environment variables as needed...
1804 */
1805
1806 snprintf(lang, sizeof(lang), "LANG=%s",
1807 con->language ? con->language->language : "C");
1808 sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.sin_port));
1809 sprintf(server_port, "SERVER_PORT=%d", ntohs(con->http.hostaddr.sin_port));
1810 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName);
1811 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname);
1812 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
1813 snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
1814 snprintf(datadir, sizeof(datadir), "CUPS_DATADIR=%s", DataDir);
1815 snprintf(root, sizeof(root), "CUPS_SERVERROOT=%s", ServerRoot);
1816
1817 if (getenv("LD_LIBRARY_PATH") != NULL)
1818 snprintf(ldpath, sizeof(ldpath), "LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
1819 else
1820 ldpath[0] = '\0';
1821
1822 envp[0] = "PATH=/bin:/usr/bin";
1823 envp[1] = "SERVER_SOFTWARE=CUPS/1.1";
1824 envp[2] = "GATEWAY_INTERFACE=CGI/1.1";
1825 envp[3] = "SERVER_PROTOCOL=HTTP/1.1";
1826 envp[4] = ipp_port;
1827 envp[5] = server_name;
1828 envp[6] = server_port;
1829 envp[7] = remote_host;
1830 envp[8] = remote_user;
1831 envp[9] = lang;
1832 envp[10] = TZ;
1833 envp[11] = tmpdir;
1834 envp[12] = datadir;
1835 envp[13] = root;
1836
1837 envc = 14;
1838
1839 if (ldpath[0])
1840 envp[envc ++] = ldpath;
1841
1842 if (con->operation == HTTP_GET)
1843 {
1844 envp[envc ++] = "REQUEST_METHOD=GET";
1845
1846 if (*commptr)
1847 {
1848 /*
1849 * Add GET form variables after ?...
1850 */
1851
1852 *commptr++ = '\0';
1853
1854 snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
1855 envp[envc ++] = query_string;
1856 }
1857 }
1858 else
1859 {
1860 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
1861 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
1862 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1863
1864 envp[envc ++] = "REQUEST_METHOD=POST";
1865 envp[envc ++] = content_length;
1866 envp[envc ++] = content_type;
1867 }
1868
1869 envp[envc] = NULL;
1870
1871 /*
1872 * Create a pipe for the output...
1873 */
1874
1875 if (pipe(fds))
1876 {
1877 LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
1878 argv[0], strerror(errno));
1879 return (0);
1880 }
1881
1882 /*
1883 * Then execute the command...
1884 */
1885
1886 if ((pid = fork()) == 0)
1887 {
1888 /*
1889 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
1890 */
1891
1892 setgid(Group);
1893 setuid(User);
1894
1895 if (infile)
1896 {
1897 close(0);
1898 dup(infile);
1899 }
1900
1901 close(1);
1902 dup(fds[1]);
1903
1904 /*
1905 * Close extra file descriptors...
1906 */
1907
1908 for (fd = 3; fd < MaxFDs; fd ++)
1909 close(fd);
1910
1911 /*
1912 * Change umask to restrict permissions on created files...
1913 */
1914
1915 umask(077);
1916
1917 /*
1918 * Execute the pipe program; if an error occurs, exit with status 1...
1919 */
1920
1921 execve(command, argv, envp);
1922 perror("execve failed");
1923 exit(errno);
1924 return (0);
1925 }
1926 else if (pid < 0)
1927 {
1928 /*
1929 * Error - can't fork!
1930 */
1931
1932 LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
1933 strerror(errno));
1934
1935 close(fds[0]);
1936 close(fds[1]);
1937 return (0);
1938 }
1939 else
1940 {
1941 /*
1942 * Fork successful - return the PID...
1943 */
1944
1945 AddCert(pid, con->username);
1946
1947 LogMessage(L_DEBUG, "CGI %s started - PID = %d", argv[0], pid);
1948
1949 *outfile = fds[0];
1950 close(fds[1]);
1951
1952 return (pid);
1953 }
1954 }
1955
1956
1957 /*
1958 * End of "$Id: client.c,v 1.80 2001/01/22 15:03:58 mike Exp $".
1959 */