]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/client.c
Added TempDir directive support, and corresponding TMPDIR environment
[thirdparty/cups.git] / scheduler / client.c
1 /*
2 * "$Id: client.c,v 1.25 1999/06/21 19:43:48 mike Exp $"
3 *
4 * Client routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-1999 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 * ReadClient() - Read data from a client.
30 * SendCommand() - Send output from a command via HTTP.
31 * SendError() - Send an error message via HTTP.
32 * SendFile() - Send a file via HTTP.
33 * SendHeader() - Send an HTTP request.
34 * WriteClient() - Write data to a client as needed.
35 * check_if_modified() - Decode an "If-Modified-Since" line.
36 * decode_basic_auth() - Decode a Basic authorization string.
37 * get_file() - Get a filename and state info.
38 * pipe_command() - Pipe the output of a command to the remote client.
39 */
40
41 /*
42 * Include necessary headers...
43 */
44
45 #include "cupsd.h"
46
47
48 /*
49 * Local functions...
50 */
51
52 static int check_if_modified(client_t *con, struct stat *filestats);
53 static void decode_basic_auth(client_t *con);
54 static char *get_file(client_t *con, struct stat *filestats);
55 static int pipe_command(client_t *con, int infile, int *outfile, char *command, char *options);
56
57
58 /*
59 * 'AcceptClient()' - Accept a new client.
60 */
61
62 void
63 AcceptClient(listener_t *lis) /* I - Listener socket */
64 {
65 int i; /* Looping var */
66 int val; /* Parameter value */
67 client_t *con; /* New client pointer */
68 unsigned address;/* Address of client */
69 struct hostent *host; /* Host entry for address */
70
71
72 DEBUG_printf(("AcceptClient(%08x) %d NumClients = %d\n",
73 lis, lis->fd, NumClients));
74
75 /*
76 * Get a pointer to the next available client...
77 */
78
79 con = Clients + NumClients;
80
81 memset(con, 0, sizeof(client_t));
82 con->http.activity = time(NULL);
83
84 /*
85 * Accept the client and get the remote address...
86 */
87
88 val = sizeof(struct sockaddr_in);
89
90 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr),
91 &val)) < 0)
92 {
93 LogMessage(LOG_ERROR, "accept() failed - %s.", strerror(errno));
94 return;
95 }
96
97 con->http.hostaddr.sin_port = lis->address.sin_port;
98
99 /*
100 * Get the hostname or format the IP address as needed...
101 */
102
103 address = ntohl(con->http.hostaddr.sin_addr.s_addr);
104
105 if (HostNameLookups)
106 #ifndef __sgi
107 host = gethostbyaddr((char *)&address, sizeof(address), AF_INET);
108 #else
109 host = gethostbyaddr(&address, sizeof(address), AF_INET);
110 #endif /* !__sgi */
111 else
112 host = NULL;
113
114 if (host == NULL)
115 sprintf(con->http.hostname, "%d.%d.%d.%d", (address >> 24) & 255,
116 (address >> 16) & 255, (address >> 8) & 255, address & 255);
117 else
118 strncpy(con->http.hostname, host->h_name, sizeof(con->http.hostname) - 1);
119
120 LogMessage(LOG_DEBUG, "accept() %d from %s:%d.", con->http.fd,
121 con->http.hostname, ntohs(con->http.hostaddr.sin_port));
122
123 /*
124 * Add the socket to the select() input mask.
125 */
126
127 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
128
129 DEBUG_printf(("AcceptClient: Adding fd %d to InputSet...\n", con->http.fd));
130 FD_SET(con->http.fd, &InputSet);
131
132 NumClients ++;
133
134 /*
135 * Temporarily suspend accept()'s until we lose a client...
136 */
137
138 if (NumClients == MAX_CLIENTS)
139 for (i = 0; i < NumListeners; i ++)
140 {
141 DEBUG_printf(("AcceptClient: Removing fd %d from InputSet...\n", Listeners[i].fd));
142 FD_CLR(Listeners[i].fd, &InputSet);
143 }
144 }
145
146
147 /*
148 * 'CloseAllClients()' - Close all remote clients immediately.
149 */
150
151 void
152 CloseAllClients(void)
153 {
154 while (NumClients > 0)
155 CloseClient(Clients);
156 }
157
158
159 /*
160 * 'CloseClient()' - Close a remote client.
161 */
162
163 void
164 CloseClient(client_t *con) /* I - Client to close */
165 {
166 int i; /* Looping var */
167 int status; /* Exit status of pipe command */
168
169
170 LogMessage(LOG_DEBUG, "CloseClient() %d", con->http.fd);
171
172 /*
173 * Close the socket and clear the file from the input set for select()...
174 */
175
176 if (con->http.fd > 0)
177 {
178 DEBUG_printf(("CloseClient: Removing fd %d from InputSet...\n", con->http.fd));
179 close(con->http.fd);
180 FD_CLR(con->http.fd, &InputSet);
181 FD_CLR(con->http.fd, &OutputSet);
182 con->http.fd = 0;
183 }
184
185 for (i = 0; i < NumListeners; i ++)
186 {
187 DEBUG_printf(("CloseClient: Adding fd %d to InputSet...\n", Listeners[i].fd));
188 FD_SET(Listeners[i].fd, &InputSet);
189 }
190
191 if (con->pipe_pid != 0)
192 {
193 DEBUG_printf(("CloseClient: Removing fd %d from InputSet...\n", con->file));
194 FD_CLR(con->file, &InputSet);
195 }
196
197 /*
198 * If we have a data file open, close it...
199 */
200
201 if (con->file)
202 {
203 if (con->pipe_pid)
204 {
205 kill(con->pipe_pid, SIGKILL);
206 waitpid(con->pipe_pid, &status, WNOHANG);
207 }
208
209 FD_CLR(con->file, &InputSet);
210 close(con->file);
211 con->file = 0;
212 }
213
214 /*
215 * Compact the list of clients as necessary...
216 */
217
218 NumClients --;
219
220 if (con < (Clients + NumClients))
221 memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
222 }
223
224
225 /*
226 * 'ReadClient()' - Read data from a client.
227 */
228
229 int /* O - 1 on success, 0 on error */
230 ReadClient(client_t *con) /* I - Client to read from */
231 {
232 char line[8192], /* Line from client... */
233 operation[64], /* Operation code from socket */
234 version[64]; /* HTTP version number string */
235 int major, minor; /* HTTP version numbers */
236 http_status_t status; /* Transfer status */
237 ipp_state_t ipp_state; /* State of IPP transfer */
238 int bytes; /* Number of bytes to POST */
239 char *filename; /* Name of file for GET/HEAD */
240 struct stat filestats; /* File information */
241 mime_type_t *type; /* MIME type of file */
242 char command[1024], /* Command to run */
243 *options; /* Options/CGI data */
244 printer_t *p; /* Printer */
245
246
247 status = HTTP_CONTINUE;
248
249 switch (con->http.state)
250 {
251 case HTTP_WAITING :
252 /*
253 * See if we've received a request line...
254 */
255
256 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
257 {
258 CloseClient(con);
259 return (0);
260 }
261
262 /*
263 * Ignore blank request lines...
264 */
265
266 if (line[0] == '\0')
267 break;
268
269 /*
270 * Clear other state variables...
271 */
272
273 httpClearFields(HTTP(con));
274
275 con->http.activity = time(NULL);
276 con->http.version = HTTP_1_0;
277 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
278 con->http.data_encoding = HTTP_ENCODE_LENGTH;
279 con->http.data_remaining = 0;
280 con->operation = HTTP_WAITING;
281 con->bytes = 0;
282 con->file = 0;
283 con->pipe_pid = 0;
284 con->username[0] = '\0';
285 con->password[0] = '\0';
286 con->uri[0] = '\0';
287
288 if (con->language != NULL)
289 {
290 cupsLangFree(con->language);
291 con->language = NULL;
292 }
293
294 /*
295 * Grab the request line...
296 */
297
298 switch (sscanf(line, "%63s%1023s%s", operation, con->uri, version))
299 {
300 case 1 :
301 SendError(con, HTTP_BAD_REQUEST);
302 CloseClient(con);
303 return (0);
304 case 2 :
305 con->http.version = HTTP_0_9;
306 break;
307 case 3 :
308 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
309 {
310 SendError(con, HTTP_BAD_REQUEST);
311 CloseClient(con);
312 return (0);
313 }
314
315 if (major < 2)
316 {
317 con->http.version = (http_version_t)(major * 100 + minor);
318 if (con->http.version == HTTP_1_1)
319 con->http.keep_alive = HTTP_KEEPALIVE_ON;
320 else
321 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
322 }
323 else
324 {
325 SendError(con, HTTP_NOT_SUPPORTED);
326 CloseClient(con);
327 return (0);
328 }
329 break;
330 }
331
332 /*
333 * Process the request...
334 */
335
336 if (strcmp(operation, "GET") == 0)
337 con->http.state = HTTP_GET;
338 else if (strcmp(operation, "PUT") == 0)
339 con->http.state = HTTP_PUT;
340 else if (strcmp(operation, "POST") == 0)
341 con->http.state = HTTP_POST;
342 else if (strcmp(operation, "DELETE") == 0)
343 con->http.state = HTTP_DELETE;
344 else if (strcmp(operation, "TRACE") == 0)
345 con->http.state = HTTP_TRACE;
346 else if (strcmp(operation, "CLOSE") == 0)
347 con->http.state = HTTP_CLOSE;
348 else if (strcmp(operation, "OPTIONS") == 0)
349 con->http.state = HTTP_OPTIONS;
350 else if (strcmp(operation, "HEAD") == 0)
351 con->http.state = HTTP_HEAD;
352 else
353 {
354 SendError(con, HTTP_BAD_REQUEST);
355 CloseClient(con);
356 return (0);
357 }
358
359 con->start = time(NULL);
360 con->operation = con->http.state;
361
362 LogMessage(LOG_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
363 operation, con->uri,
364 con->http.version / 100, con->http.version % 100);
365
366 con->http.status = HTTP_OK;
367 break;
368
369 case HTTP_CLOSE :
370 case HTTP_DELETE :
371 case HTTP_GET :
372 case HTTP_HEAD :
373 case HTTP_POST :
374 case HTTP_PUT :
375 case HTTP_TRACE :
376 /*
377 * Parse incoming parameters until the status changes...
378 */
379
380 status = httpUpdate(HTTP(con));
381
382 if (status != HTTP_OK && status != HTTP_CONTINUE)
383 {
384 SendError(con, HTTP_BAD_REQUEST);
385 CloseClient(con);
386 return (0);
387 }
388 break;
389 }
390
391 /*
392 * Handle new transfers...
393 */
394
395 if (status == HTTP_OK)
396 {
397 con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
398
399 decode_basic_auth(con);
400
401 if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
402 con->http.version >= HTTP_1_0)
403 {
404 if (!SendError(con, HTTP_BAD_REQUEST))
405 {
406 CloseClient(con);
407 return (0);
408 }
409 }
410 else if (strncmp(con->uri, "..", 2) == 0)
411 {
412 /*
413 * Protect against malicious users!
414 */
415
416 if (!SendError(con, HTTP_FORBIDDEN))
417 {
418 CloseClient(con);
419 return (0);
420 }
421 }
422 else if (con->uri[0] != '/')
423 {
424 /*
425 * Don't allow proxying (yet)...
426 */
427
428 if (!SendError(con, HTTP_METHOD_NOT_ALLOWED))
429 {
430 CloseClient(con);
431 return (0);
432 }
433 }
434 else if ((status = IsAuthorized(con)) != HTTP_OK)
435 {
436 if (!SendError(con, status))
437 {
438 CloseClient(con);
439 return (0);
440 }
441 }
442 else switch (con->http.state)
443 {
444 case HTTP_GET_SEND :
445 if (strncmp(con->uri, "/printers", 9) == 0 &&
446 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
447 {
448 /*
449 * Send PPD file - get the real printer name since printer
450 * names are not case sensitive but filename can be...
451 */
452
453 if ((p = FindPrinter(con->uri + 10)) != NULL)
454 sprintf(con->uri, "/ppd/%s.ppd", p->name);
455 else
456 {
457 if (!SendError(con, HTTP_NOT_FOUND))
458 {
459 CloseClient(con);
460 return (0);
461 }
462
463 break;
464 }
465 }
466
467 if (strncmp(con->uri, "/printers", 9) == 0 ||
468 strncmp(con->uri, "/classes", 8) == 0 ||
469 strncmp(con->uri, "/jobs", 5) == 0)
470 {
471 /*
472 * Send CGI output...
473 */
474
475 if (strncmp(con->uri, "/printers", 9) == 0)
476 {
477 sprintf(command, "%s/cgi-bin/printers.cgi", ServerRoot);
478 options = con->uri + 9;
479 }
480 else if (strncmp(con->uri, "/classes", 8) == 0)
481 {
482 sprintf(command, "%s/cgi-bin/classes.cgi", ServerRoot);
483 options = con->uri + 8;
484 }
485 else
486 {
487 sprintf(command, "%s/cgi-bin/jobs.cgi", ServerRoot);
488 options = con->uri + 5;
489 }
490
491 if (*options == '/')
492 options ++;
493
494 if (!SendCommand(con, command, options))
495 {
496 if (!SendError(con, HTTP_NOT_FOUND))
497 {
498 CloseClient(con);
499 return (0);
500 }
501 }
502 else
503 LogRequest(con, HTTP_OK);
504
505 if (con->http.version <= HTTP_1_0)
506 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
507 }
508 else
509 {
510 /*
511 * Serve a file...
512 */
513
514 if ((filename = get_file(con, &filestats)) == NULL)
515 {
516 if (!SendError(con, HTTP_NOT_FOUND))
517 {
518 CloseClient(con);
519 return (0);
520 }
521 }
522 else if (!check_if_modified(con, &filestats))
523 {
524 if (!SendError(con, HTTP_NOT_MODIFIED))
525 {
526 CloseClient(con);
527 return (0);
528 }
529 }
530 else
531 {
532 type = mimeFileType(MimeDatabase, filename);
533 if (type == NULL)
534 strcpy(line, "text/plain");
535 else
536 sprintf(line, "%s/%s", type->super, type->type);
537
538 if (!SendFile(con, HTTP_OK, filename, line, &filestats))
539 {
540 CloseClient(con);
541 return (0);
542 }
543 }
544 }
545 break;
546
547 case HTTP_POST_RECV :
548 /*
549 * See if the POST request includes a Content-Length field, and if
550 * so check the length against any limits that are set...
551 */
552
553 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
554 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
555 MaxRequestSize > 0)
556 {
557 /*
558 * Request too large...
559 */
560
561 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
562 {
563 CloseClient(con);
564 return (0);
565 }
566
567 break;
568 }
569
570 /*
571 * See what kind of POST request this is; for IPP requests the
572 * content-type field will be "application/ipp"...
573 */
574
575 if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
576 con->request = ippNew();
577 else if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/x-www-form-urlencoded") == 0 &&
578 (strncmp(con->uri, "/printers", 9) == 0 ||
579 strncmp(con->uri, "/classes", 8) == 0 ||
580 strncmp(con->uri, "/jobs", 5) == 0))
581 {
582 /*
583 * CGI request...
584 */
585
586 if (strncmp(con->uri, "/printers", 9) == 0)
587 {
588 sprintf(command, "%s/cgi-bin/printers", ServerRoot);
589 options = con->uri + 9;
590 }
591 else if (strncmp(con->uri, "/classes", 8) == 0)
592 {
593 sprintf(command, "%s/cgi-bin/classes", ServerRoot);
594 options = con->uri + 8;
595 }
596 else
597 {
598 sprintf(command, "%s/cgi-bin/jobs", ServerRoot);
599 options = con->uri + 5;
600 }
601
602 if (*options == '/')
603 options ++;
604
605 if (!SendCommand(con, command, options))
606 {
607 if (!SendError(con, HTTP_NOT_FOUND))
608 {
609 CloseClient(con);
610 return (0);
611 }
612 }
613 else
614 LogRequest(con, HTTP_OK);
615
616 if (con->http.version <= HTTP_1_0)
617 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
618 }
619 else if (!SendError(con, HTTP_UNAUTHORIZED))
620 {
621 CloseClient(con);
622 return (0);
623 }
624 break;
625
626 case HTTP_PUT_RECV :
627 case HTTP_DELETE :
628 case HTTP_TRACE :
629 SendError(con, HTTP_NOT_IMPLEMENTED);
630
631 case HTTP_CLOSE :
632 CloseClient(con);
633 return (0);
634
635 case HTTP_HEAD :
636 if (strncmp(con->uri, "/printers", 9) == 0 &&
637 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
638 {
639 /*
640 * Send PPD file...
641 */
642
643 sprintf(command, "/ppd/%s", con->uri + 10);
644 strcpy(con->uri, command);
645 }
646
647 if (strncmp(con->uri, "/printers/", 10) == 0 ||
648 strncmp(con->uri, "/classes/", 9) == 0 ||
649 strncmp(con->uri, "/jobs/", 6) == 0)
650 {
651 /*
652 * CGI output...
653 */
654
655 if (!SendHeader(con, HTTP_OK, "text/html"))
656 {
657 CloseClient(con);
658 return (0);
659 }
660
661 if (httpPrintf(HTTP(con), "\r\n") < 0)
662 {
663 CloseClient(con);
664 return (0);
665 }
666
667 LogRequest(con, HTTP_OK);
668 }
669 else if ((filename = get_file(con, &filestats)) == NULL)
670 {
671 if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
672 {
673 CloseClient(con);
674 return (0);
675 }
676
677 LogRequest(con, HTTP_NOT_FOUND);
678 }
679 else if (!check_if_modified(con, &filestats))
680 {
681 if (!SendError(con, HTTP_NOT_MODIFIED))
682 {
683 CloseClient(con);
684 return (0);
685 }
686
687 LogRequest(con, HTTP_NOT_MODIFIED);
688 }
689 else
690 {
691 /*
692 * Serve a file...
693 */
694
695 type = mimeFileType(MimeDatabase, filename);
696 if (type == NULL)
697 strcpy(line, "text/plain");
698 else
699 sprintf(line, "%s/%s", type->super, type->type);
700
701 if (!SendHeader(con, HTTP_OK, line))
702 {
703 CloseClient(con);
704 return (0);
705 }
706
707 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
708 httpGetDateString(filestats.st_mtime)) < 0)
709 {
710 CloseClient(con);
711 return (0);
712 }
713
714 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
715 filestats.st_size) < 0)
716 {
717 CloseClient(con);
718 return (0);
719 }
720
721 LogRequest(con, HTTP_OK);
722 }
723
724 if (send(con->http.fd, "\r\n", 2, 0) < 0)
725 {
726 CloseClient(con);
727 return (0);
728 }
729
730 con->http.state = HTTP_WAITING;
731 break;
732 }
733 }
734
735 /*
736 * Handle any incoming data...
737 */
738
739 switch (con->http.state)
740 {
741 case HTTP_PUT_RECV :
742 break;
743
744 case HTTP_POST_RECV :
745 LogMessage(LOG_DEBUG, "ReadClient() %d con->data_encoding = %s con->data_remaining = %d",
746 con->http.fd,
747 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
748 con->http.data_remaining);
749 DEBUG_printf(("ReadClient() %d con->data_encoding = %s con->data_remaining = %d\n",
750 con->http.fd,
751 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
752 con->http.data_remaining));
753
754 if (con->request != NULL)
755 {
756 /*
757 * Grab any request data from the connection...
758 */
759
760 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
761 {
762 LogMessage(LOG_ERROR, "ReadClient() %d IPP Read Error!",
763 con->http.fd);
764 CloseClient(con);
765 return (0);
766 }
767 else if (ipp_state != IPP_DATA)
768 break;
769
770 if (con->file == 0 && con->http.state != HTTP_POST_SEND)
771 {
772 /*
773 * Create a file as needed for the request data...
774 */
775
776 sprintf(con->filename, "%s/requests/XXXXXX", ServerRoot);
777 con->file = mkstemp(con->filename);
778 fchmod(con->file, 0644);
779
780 LogMessage(LOG_DEBUG, "ReadClient() %d REQUEST %s", con->http.fd,
781 con->filename);
782
783 if (con->file < 0)
784 {
785 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
786 {
787 CloseClient(con);
788 return (0);
789 }
790 }
791 }
792 }
793
794 if (con->http.state != HTTP_POST_SEND)
795 {
796 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
797 {
798 CloseClient(con);
799 return (0);
800 }
801 else if (bytes > 0)
802 {
803 con->bytes += bytes;
804
805 LogMessage(LOG_DEBUG, "ReadClient() %d writing %d bytes", bytes);
806
807 if (write(con->file, line, bytes) < bytes)
808 {
809 close(con->file);
810 con->file = 0;
811 unlink(con->filename);
812
813 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
814 {
815 CloseClient(con);
816 return (0);
817 }
818 }
819 }
820 else if (con->http.state != HTTP_POST_SEND)
821 {
822 CloseClient(con);
823 return (0);
824 }
825 }
826
827 if (con->http.state == HTTP_POST_SEND)
828 {
829 if (con->file)
830 {
831 fstat(con->file, &filestats);
832 close(con->file);
833 con->file = 0;
834
835 if (filestats.st_size > MaxRequestSize &&
836 MaxRequestSize > 0)
837 {
838 /*
839 * Request is too big; remove it and send an error...
840 */
841
842 unlink(con->filename);
843
844 if (con->request)
845 {
846 /*
847 * Delete any IPP request data...
848 */
849
850 ippDelete(con->request);
851 con->request = NULL;
852 }
853
854 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
855 {
856 CloseClient(con);
857 return (0);
858 }
859 }
860 }
861
862 if (con->request)
863 ProcessIPPRequest(con);
864 }
865 break;
866 }
867
868 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
869 {
870 CloseClient(con);
871 return (0);
872 }
873 else
874 return (1);
875 }
876
877
878 /*
879 * 'SendCommand()' - Send output from a command via HTTP.
880 */
881
882 int
883 SendCommand(client_t *con,
884 char *command,
885 char *options)
886 {
887 con->pipe_pid = pipe_command(con, 0, &(con->file), command, options);
888
889 LogMessage(LOG_DEBUG, "SendCommand() %d command=\"%s\" file=%d pipe_pid=%d",
890 con->http.fd, command, con->file, con->pipe_pid);
891
892 if (con->pipe_pid == 0)
893 return (0);
894
895 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
896
897 DEBUG_printf(("SendCommand: Adding fd %d to InputSet...\n", con->file));
898 FD_SET(con->file, &InputSet);
899 FD_SET(con->http.fd, &OutputSet);
900
901 if (!SendHeader(con, HTTP_OK, NULL))
902 return (0);
903
904 if (con->http.version == HTTP_1_1)
905 {
906 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
907
908 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
909 return (0);
910 }
911
912 return (1);
913 }
914
915
916 /*
917 * 'SendError()' - Send an error message via HTTP.
918 */
919
920 int /* O - 1 if successful, 0 otherwise */
921 SendError(client_t *con, /* I - Connection */
922 http_status_t code) /* I - Error code */
923 {
924 char message[1024]; /* Text version of error code */
925
926
927 /*
928 * Put the request in the access_log file...
929 */
930
931 LogRequest(con, code);
932
933 /*
934 * To work around bugs in some proxies, don't use Keep-Alive for some
935 * error messages...
936 */
937
938 if (code >= HTTP_BAD_REQUEST)
939 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
940
941 /*
942 * Send an error message back to the client. If the error code is a
943 * 400 or 500 series, make sure the message contains some text, too!
944 */
945
946 if (!SendHeader(con, code, NULL))
947 return (0);
948
949 if (code == HTTP_UNAUTHORIZED)
950 {
951 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
952 return (0);
953 }
954
955 if (con->http.version >= HTTP_1_1 && !con->http.keep_alive)
956 {
957 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
958 return (0);
959 }
960
961 if (code >= HTTP_BAD_REQUEST)
962 {
963 /*
964 * Send a human-readable error message.
965 */
966
967 sprintf(message, "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
968 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
969 code, httpStatus(code), httpStatus(code),
970 con->language ? con->language->messages[code] : httpStatus(code));
971
972 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
973 return (0);
974 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", strlen(message)) < 0)
975 return (0);
976 if (httpPrintf(HTTP(con), "\r\n") < 0)
977 return (0);
978 if (send(con->http.fd, message, strlen(message), 0) < 0)
979 return (0);
980 }
981 else if (httpPrintf(HTTP(con), "\r\n") < 0)
982 return (0);
983
984 con->http.state = HTTP_WAITING;
985
986 return (1);
987 }
988
989
990 /*
991 * 'SendFile()' - Send a file via HTTP.
992 */
993
994 int
995 SendFile(client_t *con,
996 http_status_t code,
997 char *filename,
998 char *type,
999 struct stat *filestats)
1000 {
1001 con->file = open(filename, O_RDONLY);
1002
1003 LogMessage(LOG_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
1004
1005 if (con->file < 0)
1006 return (0);
1007
1008 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1009
1010 con->pipe_pid = 0;
1011
1012 if (!SendHeader(con, code, type))
1013 return (0);
1014
1015 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
1016 return (0);
1017 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", filestats->st_size) < 0)
1018 return (0);
1019 if (httpPrintf(HTTP(con), "\r\n") < 0)
1020 return (0);
1021
1022 FD_SET(con->http.fd, &OutputSet);
1023
1024 return (1);
1025 }
1026
1027
1028 /*
1029 * 'SendHeader()' - Send an HTTP request.
1030 */
1031
1032 int /* O - 1 on success, 0 on failure */
1033 SendHeader(client_t *con, /* I - Client to send to */
1034 http_status_t code, /* I - HTTP status code */
1035 char *type) /* I - MIME type of document */
1036 {
1037 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
1038 con->http.version % 100, code, httpStatus(code)) < 0)
1039 return (0);
1040 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
1041 return (0);
1042 if (httpPrintf(HTTP(con), "Server: CUPS/1.0\r\n") < 0)
1043 return (0);
1044 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
1045 {
1046 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
1047 return (0);
1048 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
1049 return (0);
1050 }
1051 if (con->language != NULL)
1052 {
1053 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
1054 con->language->language) < 0)
1055 return (0);
1056
1057 if (type != NULL)
1058 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
1059 cupsLangEncoding(con->language)) < 0)
1060 return (0);
1061 }
1062 else if (type != NULL)
1063 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
1064 return (0);
1065
1066 return (1);
1067 }
1068
1069
1070 /*
1071 * 'WriteClient()' - Write data to a client as needed.
1072 */
1073
1074 int /* O - 1 if success, 0 if fail */
1075 WriteClient(client_t *con) /* I - Client connection */
1076 {
1077 int bytes; /* Number of bytes written */
1078 char buf[HTTP_MAX_BUFFER]; /* Data buffer */
1079 ipp_state_t ipp_state; /* IPP state value */
1080
1081
1082 if (con->http.state != HTTP_GET_SEND &&
1083 con->http.state != HTTP_POST_SEND)
1084 return (1);
1085
1086 if (con->response != NULL)
1087 {
1088 ipp_state = ippWrite(&(con->http), con->response);
1089 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
1090 }
1091 else if ((bytes = read(con->file, buf, sizeof(buf))) > 0)
1092 {
1093 if (httpWrite(HTTP(con), buf, bytes) < 0)
1094 {
1095 CloseClient(con);
1096 return (0);
1097 }
1098
1099 con->bytes += bytes;
1100 }
1101
1102 if (bytes <= 0)
1103 {
1104 LogRequest(con, HTTP_OK);
1105
1106 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
1107 {
1108 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
1109 {
1110 CloseClient(con);
1111 return (0);
1112 }
1113 }
1114
1115 con->http.state = HTTP_WAITING;
1116
1117 FD_CLR(con->http.fd, &OutputSet);
1118
1119 if (con->file)
1120 {
1121 DEBUG_printf(("WriteClient: Removing fd %d from InputSet...\n", con->file));
1122 FD_CLR(con->file, &InputSet);
1123
1124 if (con->pipe_pid)
1125 kill(con->pipe_pid, SIGTERM);
1126
1127 close(con->file);
1128 con->file = 0;
1129 con->pipe_pid = 0;
1130 }
1131
1132 if (con->request != NULL)
1133 {
1134 ippDelete(con->request);
1135 con->request = NULL;
1136 }
1137
1138 if (con->response != NULL)
1139 {
1140 ippDelete(con->response);
1141 con->response = NULL;
1142 }
1143
1144 if (!con->http.keep_alive)
1145 {
1146 CloseClient(con);
1147 return (0);
1148 }
1149 }
1150
1151 LogMessage(LOG_DEBUG, "WriteClient() %d %d bytes", con->http.fd, bytes);
1152
1153 con->http.activity = time(NULL);
1154
1155 return (1);
1156 }
1157
1158
1159 /*
1160 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
1161 */
1162
1163 static int /* O - 1 if modified since */
1164 check_if_modified(client_t *con, /* I - Client connection */
1165 struct stat *filestats) /* I - File information */
1166 {
1167 char *ptr; /* Pointer into field */
1168 time_t date; /* Time/date value */
1169 int size; /* Size/length value */
1170
1171
1172 size = 0;
1173 date = 0;
1174 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
1175
1176 if (*ptr == '\0')
1177 return (1);
1178
1179 LogMessage(LOG_DEBUG, "check_if_modified() %d If-Modified-Since=\"%s\"",
1180 con->http.fd, ptr);
1181
1182 while (*ptr != '\0')
1183 {
1184 while (isspace(*ptr) || *ptr == ';')
1185 ptr ++;
1186
1187 if (strncasecmp(ptr, "length=", 7) == 0)
1188 {
1189 ptr += 7;
1190 size = atoi(ptr);
1191
1192 while (isdigit(*ptr))
1193 ptr ++;
1194 }
1195 else if (isalpha(*ptr))
1196 {
1197 date = httpGetDateTime(ptr);
1198 while (*ptr != '\0' && *ptr != ';')
1199 ptr ++;
1200 }
1201 }
1202
1203 LogMessage(LOG_DEBUG, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
1204 con->http.fd, size, filestats->st_size, date, filestats->st_mtime);
1205
1206 return ((size != filestats->st_size && size != 0) ||
1207 (date < filestats->st_mtime && date != 0) ||
1208 (size == 0 && date == 0));
1209 }
1210
1211
1212 /*
1213 * 'decode_basic_auth()' - Decode a Basic authorization string.
1214 */
1215
1216 static void
1217 decode_basic_auth(client_t *con) /* I - Client to decode to */
1218 {
1219 char *s, /* Authorization string */
1220 value[1024]; /* Value string */
1221
1222
1223 /*
1224 * Decode the string and pull the username and password out...
1225 */
1226
1227 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
1228 if (strncmp(s, "Basic", 5) != 0)
1229 return;
1230
1231 s += 5;
1232 while (isspace(*s))
1233 s ++;
1234
1235 httpDecode64(value, s);
1236
1237 LogMessage(LOG_DEBUG, "decode_basic_auth() %d Authorization=\"%s\"",
1238 con->http.fd, value);
1239
1240 sscanf(value, "%[^:]:%[^\n]", con->username, con->password);
1241 }
1242
1243
1244 /*
1245 * 'get_file()' - Get a filename and state info.
1246 */
1247
1248 static char * /* O - Real filename */
1249 get_file(client_t *con, /* I - Client connection */
1250 struct stat *filestats)/* O - File information */
1251 {
1252 int status; /* Status of filesystem calls */
1253 char *params; /* Pointer to parameters in URI */
1254 static char filename[1024]; /* Filename buffer */
1255
1256
1257 /*
1258 * Need to add DocumentRoot global...
1259 */
1260
1261 if (strncmp(con->uri, "/ppd/", 5) == 0)
1262 sprintf(filename, "%s%s", ServerRoot, con->uri);
1263 else if (con->language != NULL)
1264 sprintf(filename, "%s/%s%s", DocumentRoot, con->language->language,
1265 con->uri);
1266 else
1267 sprintf(filename, "%s%s", DocumentRoot, con->uri);
1268
1269 if ((params = strchr(filename, '?')) != NULL)
1270 *params = '\0';
1271
1272 /*
1273 * Grab the status for this language; if there isn't a language-specific file
1274 * then fallback to the default one...
1275 */
1276
1277 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
1278 {
1279 /*
1280 * Drop the language prefix and try the current directory...
1281 */
1282
1283 if (strncmp(con->uri, "/ppd/", 5) != 0)
1284 {
1285 sprintf(filename, "%s%s", DocumentRoot, con->uri);
1286
1287 status = stat(filename, filestats);
1288 }
1289 }
1290
1291 /*
1292 * If we're found a directory, get the index.html file instead...
1293 */
1294
1295 if (!status && S_ISDIR(filestats->st_mode))
1296 {
1297 if (filename[strlen(filename) - 1] == '/')
1298 strcat(filename, "index.html");
1299 else
1300 strcat(filename, "/index.html");
1301
1302 status = stat(filename, filestats);
1303 }
1304
1305 LogMessage(LOG_DEBUG, "get_filename() %d filename=%s size=%d",
1306 con->http.fd, filename, filestats->st_size);
1307
1308 if (status)
1309 return (NULL);
1310 else
1311 return (filename);
1312 }
1313
1314
1315 /*
1316 * 'pipe_command()' - Pipe the output of a command to the remote client.
1317 */
1318
1319 static int /* O - Process ID */
1320 pipe_command(client_t *con, /* I - Client connection */
1321 int infile, /* I - Standard input for command */
1322 int *outfile, /* O - Standard output for command */
1323 char *command, /* I - Command to run */
1324 char *options) /* I - Options for command */
1325 {
1326 int pid; /* Process ID */
1327 char *commptr; /* Command string pointer */
1328 int fds[2]; /* Pipe FDs */
1329 int argc; /* Number of arguments */
1330 char argbuf[1024], /* Argument buffer */
1331 *argv[100], /* Argument strings */
1332 *envp[100]; /* Environment variables */
1333 char hostname[1024]; /* Hostname string */
1334 static char lang[1024]; /* LANG env variable */
1335 static char content_length[1024]; /* CONTENT_LENGTH env variable */
1336 static char content_type[1024]; /* CONTENT_TYPE env variable */
1337 static char server_port[1024]; /* Default listen port */
1338 static char server_name[1024]; /* Default listen hostname */
1339 static char remote_host[1024]; /* REMOTE_HOST env variable */
1340 static char remote_user[1024]; /* REMOTE_HOST env variable */
1341 static char tmpdir[1024]; /* TMPDIR env variable */
1342
1343 /*
1344 * Copy the command string...
1345 */
1346
1347 strncpy(argbuf, options, sizeof(argbuf) - 1);
1348 argbuf[sizeof(argbuf) - 1] = '\0';
1349
1350 /*
1351 * Parse the string; arguments can be separated by spaces or by ? or +...
1352 */
1353
1354 argv[0] = argbuf;
1355
1356 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
1357 if (*commptr == ' ' || *commptr == '?' || *commptr == '+')
1358 {
1359 *commptr++ = '\0';
1360
1361 while (*commptr == ' ')
1362 commptr ++;
1363
1364 if (*commptr != '\0')
1365 {
1366 argv[argc] = commptr;
1367 argc ++;
1368 }
1369
1370 commptr --;
1371 }
1372 else if (*commptr == '%')
1373 {
1374 if (commptr[1] >= '0' && commptr[1] <= '9')
1375 *commptr = (commptr[1] - '0') << 4;
1376 else
1377 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
1378
1379 if (commptr[2] >= '0' && commptr[2] <= '9')
1380 *commptr |= commptr[2] - '0';
1381 else
1382 *commptr |= tolower(commptr[2]) - 'a' + 10;
1383
1384 strcpy(commptr + 1, commptr + 3);
1385 }
1386
1387 argv[argc] = NULL;
1388
1389 if (argv[0][0] == '\0')
1390 argv[0] = strrchr(command, '/') + 1;
1391
1392 /*
1393 * Setup the environment variables as needed...
1394 */
1395
1396 gethostname(hostname, sizeof(hostname) - 1);
1397
1398 sprintf(lang, "LANG=%s", con->language ? con->language->language : "C");
1399 sprintf(server_port, "SERVER_PORT=%d", ntohs(con->http.hostaddr.sin_port));
1400 sprintf(server_name, "SERVER_NAME=%s", hostname);
1401 sprintf(remote_host, "REMOTE_HOST=%s", con->http.hostname);
1402 sprintf(remote_user, "REMOTE_USER=%s", con->username);
1403 sprintf(tmpdir, "TMPDIR=%s", TempDir);
1404
1405 envp[0] = "PATH=/bin:/usr/bin";
1406 envp[1] = "SERVER_SOFTWARE=CUPS/1.0";
1407 envp[2] = "GATEWAY_INTERFACE=CGI/1.1";
1408 envp[3] = "SERVER_PROTOCOL=HTTP/1.1";
1409 envp[4] = server_name;
1410 envp[5] = server_port;
1411 envp[6] = remote_host;
1412 envp[7] = remote_user;
1413 envp[8] = lang;
1414 envp[9] = "TZ=GMT";
1415 envp[10] = tmpdir;
1416
1417 if (con->operation == HTTP_GET)
1418 {
1419 envp[11] = "REQUEST_METHOD=GET";
1420 envp[12] = NULL;
1421 }
1422 else
1423 {
1424 sprintf(content_length, "CONTENT_LENGTH=%d", con->http.data_remaining);
1425 sprintf(content_type, "CONTENT_TYPE=%s",
1426 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1427
1428 envp[11] = "REQUEST_METHOD=POST";
1429 envp[12] = content_length;
1430 envp[13] = content_type;
1431 envp[14] = NULL;
1432 }
1433
1434 /*
1435 * Create a pipe for the output...
1436 */
1437
1438 if (pipe(fds))
1439 return (0);
1440
1441 /*
1442 * Then execute the pipe command...
1443 */
1444
1445 if ((pid = fork()) == 0)
1446 {
1447 /*
1448 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
1449 */
1450
1451 setuid(User);
1452 setgid(Group);
1453
1454 if (infile)
1455 {
1456 close(0);
1457 dup(infile);
1458 }
1459
1460 close(1);
1461 dup(fds[1]);
1462
1463 close(fds[0]);
1464 close(fds[1]);
1465
1466 /*
1467 * Execute the pipe program; if an error occurs, exit with status 1...
1468 */
1469
1470 execve(command, argv, envp);
1471 perror("execve failed");
1472 exit(errno);
1473 return (0);
1474 }
1475 else if (pid < 0)
1476 {
1477 /*
1478 * Error - can't fork!
1479 */
1480
1481 close(fds[0]);
1482 close(fds[1]);
1483 return (0);
1484 }
1485 else
1486 {
1487 /*
1488 * Fork successful - return the PID...
1489 */
1490
1491 *outfile = fds[0];
1492 close(fds[1]);
1493
1494 return (pid);
1495 }
1496 }
1497
1498
1499 /*
1500 * End of "$Id: client.c,v 1.25 1999/06/21 19:43:48 mike Exp $".
1501 */