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