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