]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/client.c
Updated POST code to report the number of IPP bytes, too.
[thirdparty/cups.git] / scheduler / client.c
1 /*
2 * "$Id: client.c,v 1.62 2000/07/10 15:32:27 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 else
821 con->bytes += ippLength(con->request);
822 }
823
824 if (con->file == 0 && con->http.state != HTTP_POST_SEND)
825 {
826 /*
827 * Create a file as needed for the request data...
828 */
829
830 snprintf(con->filename, sizeof(con->filename), "%s/%08x",
831 RequestRoot, request_id ++);
832 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
833 fchmod(con->file, 0640);
834 fchown(con->file, User, Group);
835
836 LogMessage(L_DEBUG, "ReadClient() %d REQUEST %s", con->http.fd,
837 con->filename);
838
839 if (con->file < 0)
840 {
841 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
842 {
843 CloseClient(con);
844 return (0);
845 }
846 }
847 }
848
849 if (con->http.state != HTTP_POST_SEND)
850 {
851 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
852 {
853 CloseClient(con);
854 return (0);
855 }
856 else if (bytes > 0)
857 {
858 con->bytes += bytes;
859
860 LogMessage(L_DEBUG, "ReadClient() %d writing %d bytes",
861 con->http.fd, bytes);
862
863 if (write(con->file, line, bytes) < bytes)
864 {
865 close(con->file);
866 con->file = 0;
867 unlink(con->filename);
868
869 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
870 {
871 CloseClient(con);
872 return (0);
873 }
874 }
875 }
876 else if (con->http.state != HTTP_POST_SEND)
877 {
878 CloseClient(con);
879 return (0);
880 }
881 }
882
883 if (con->http.state == HTTP_POST_SEND)
884 {
885 if (con->file)
886 {
887 fstat(con->file, &filestats);
888 close(con->file);
889 con->file = 0;
890
891 if (filestats.st_size > MaxRequestSize &&
892 MaxRequestSize > 0)
893 {
894 /*
895 * Request is too big; remove it and send an error...
896 */
897
898 unlink(con->filename);
899
900 if (con->request)
901 {
902 /*
903 * Delete any IPP request data...
904 */
905
906 ippDelete(con->request);
907 con->request = NULL;
908 }
909
910 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
911 {
912 CloseClient(con);
913 return (0);
914 }
915 }
916
917 if (con->command[0])
918 {
919 if (!SendCommand(con, con->command, con->options))
920 {
921 if (!SendError(con, HTTP_NOT_FOUND))
922 {
923 CloseClient(con);
924 return (0);
925 }
926 }
927 else
928 LogRequest(con, HTTP_OK);
929 }
930 }
931
932 if (con->request)
933 ProcessIPPRequest(con);
934 }
935 break;
936
937 default :
938 break; /* Anti-compiler-warning-code */
939 }
940
941 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
942 {
943 CloseClient(con);
944 return (0);
945 }
946 else
947 return (1);
948 }
949
950
951 /*
952 * 'SendCommand()' - Send output from a command via HTTP.
953 */
954
955 int
956 SendCommand(client_t *con,
957 char *command,
958 char *options)
959 {
960 int fd;
961
962
963 if (con->filename[0])
964 fd = open(con->filename, O_RDONLY);
965 else
966 fd = open("/dev/null", O_RDONLY);
967
968 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
969
970 close(fd);
971
972 LogMessage(L_DEBUG, "SendCommand() %d command=\"%s\" file=%d pipe_pid=%d",
973 con->http.fd, command, con->file, con->pipe_pid);
974
975 if (con->pipe_pid == 0)
976 return (0);
977
978 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
979
980 DEBUG_printf(("SendCommand: Adding fd %d to InputSet...\n", con->file));
981 FD_SET(con->file, &InputSet);
982 FD_SET(con->http.fd, &OutputSet);
983
984 if (!SendHeader(con, HTTP_OK, NULL))
985 return (0);
986
987 if (con->http.version == HTTP_1_1)
988 {
989 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
990
991 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
992 return (0);
993 }
994
995 con->got_fields = 0;
996 con->field_col = 0;
997
998 return (1);
999 }
1000
1001
1002 /*
1003 * 'SendError()' - Send an error message via HTTP.
1004 */
1005
1006 int /* O - 1 if successful, 0 otherwise */
1007 SendError(client_t *con, /* I - Connection */
1008 http_status_t code) /* I - Error code */
1009 {
1010 char message[1024]; /* Message for user */
1011
1012
1013 /*
1014 * Put the request in the access_log file...
1015 */
1016
1017 if (con->operation > HTTP_WAITING)
1018 LogRequest(con, code);
1019
1020 /*
1021 * To work around bugs in some proxies, don't use Keep-Alive for some
1022 * error messages...
1023 */
1024
1025 if (code >= HTTP_BAD_REQUEST)
1026 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1027
1028 /*
1029 * Send an error message back to the client. If the error code is a
1030 * 400 or 500 series, make sure the message contains some text, too!
1031 */
1032
1033 if (!SendHeader(con, code, NULL))
1034 return (0);
1035
1036 if (con->http.version >= HTTP_1_1 && !con->http.keep_alive)
1037 {
1038 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
1039 return (0);
1040 }
1041
1042 if (code >= HTTP_BAD_REQUEST)
1043 {
1044 /*
1045 * Send a human-readable error message.
1046 */
1047
1048 snprintf(message, sizeof(message),
1049 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
1050 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
1051 code, httpStatus(code), httpStatus(code),
1052 con->language ? con->language->messages[code] :
1053 httpStatus(code));
1054
1055 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
1056 return (0);
1057 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", strlen(message)) < 0)
1058 return (0);
1059 if (httpPrintf(HTTP(con), "\r\n") < 0)
1060 return (0);
1061 if (httpPrintf(HTTP(con), "%s", message) < 0)
1062 return (0);
1063 }
1064 else if (httpPrintf(HTTP(con), "\r\n") < 0)
1065 return (0);
1066
1067 con->http.state = HTTP_WAITING;
1068
1069 return (1);
1070 }
1071
1072
1073 /*
1074 * 'SendFile()' - Send a file via HTTP.
1075 */
1076
1077 int
1078 SendFile(client_t *con,
1079 http_status_t code,
1080 char *filename,
1081 char *type,
1082 struct stat *filestats)
1083 {
1084 con->file = open(filename, O_RDONLY);
1085
1086 LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
1087
1088 if (con->file < 0)
1089 return (0);
1090
1091 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1092
1093 con->pipe_pid = 0;
1094
1095 if (!SendHeader(con, code, type))
1096 return (0);
1097
1098 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
1099 return (0);
1100 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", filestats->st_size) < 0)
1101 return (0);
1102 if (httpPrintf(HTTP(con), "\r\n") < 0)
1103 return (0);
1104
1105 FD_SET(con->http.fd, &OutputSet);
1106
1107 return (1);
1108 }
1109
1110
1111 /*
1112 * 'SendHeader()' - Send an HTTP request.
1113 */
1114
1115 int /* O - 1 on success, 0 on failure */
1116 SendHeader(client_t *con, /* I - Client to send to */
1117 http_status_t code, /* I - HTTP status code */
1118 char *type) /* I - MIME type of document */
1119 {
1120 location_t *loc; /* Authentication location */
1121
1122
1123 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
1124 con->http.version % 100, code, httpStatus(code)) < 0)
1125 return (0);
1126 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
1127 return (0);
1128 if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
1129 return (0);
1130 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
1131 {
1132 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
1133 return (0);
1134 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
1135 return (0);
1136 }
1137 if (code == HTTP_METHOD_NOT_ALLOWED)
1138 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
1139 return (0);
1140
1141 if (code == HTTP_UNAUTHORIZED)
1142 {
1143 loc = FindBest(con); /* This already succeeded in IsAuthorized */
1144
1145 if (loc->type == AUTH_BASIC)
1146 {
1147 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
1148 return (0);
1149 }
1150 else
1151 {
1152 if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" "
1153 "nonce=\"%s\"\r\n", con->http.hostname) < 0)
1154 return (0);
1155 }
1156 }
1157 if (con->language != NULL)
1158 {
1159 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
1160 con->language->language) < 0)
1161 return (0);
1162
1163 if (type != NULL)
1164 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
1165 cupsLangEncoding(con->language)) < 0)
1166 return (0);
1167 }
1168 else if (type != NULL)
1169 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
1170 return (0);
1171
1172 return (1);
1173 }
1174
1175
1176 /*
1177 * 'WriteClient()' - Write data to a client as needed.
1178 */
1179
1180 int /* O - 1 if success, 0 if fail */
1181 WriteClient(client_t *con) /* I - Client connection */
1182 {
1183 int bytes; /* Number of bytes written */
1184 char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */
1185 char *bufptr; /* Pointer into buffer */
1186 ipp_state_t ipp_state; /* IPP state value */
1187
1188
1189 if (con->http.state != HTTP_GET_SEND &&
1190 con->http.state != HTTP_POST_SEND)
1191 return (1);
1192
1193 if (con->response != NULL)
1194 {
1195 ipp_state = ippWrite(&(con->http), con->response);
1196 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
1197 }
1198 else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
1199 {
1200 if (con->pipe_pid && !con->got_fields)
1201 {
1202 /*
1203 * Inspect the data for Content-Type and other fields.
1204 */
1205
1206 buf[bytes] = '\0';
1207
1208 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
1209 if (*bufptr == '\n')
1210 {
1211 /*
1212 * Send line to client...
1213 */
1214
1215 if (bufptr > buf && bufptr[-1] == '\r')
1216 bufptr[-1] = '\0';
1217 *bufptr++ = '\0';
1218
1219 httpPrintf(HTTP(con), "%s\r\n", buf);
1220 LogMessage(L_DEBUG, "WriteClient() %d %s", con->http.fd, buf);
1221
1222 /*
1223 * Update buffer...
1224 */
1225
1226 bytes -= (bufptr - buf);
1227 memcpy(buf, bufptr, bytes + 1);
1228 bufptr = buf - 1;
1229
1230 /*
1231 * See if the line was empty...
1232 */
1233
1234 if (con->field_col == 0)
1235 con->got_fields = 1;
1236 else
1237 con->field_col = 0;
1238 }
1239 else if (*bufptr != '\r')
1240 con->field_col ++;
1241
1242 if (bytes > 0 && !con->got_fields)
1243 {
1244 /*
1245 * Remaining text needs to go out...
1246 */
1247
1248 httpPrintf(HTTP(con), "%s", buf);
1249
1250 con->http.activity = time(NULL);
1251 return (1);
1252 }
1253 else if (bytes == 0)
1254 {
1255 con->http.activity = time(NULL);
1256 return (1);
1257 }
1258 }
1259
1260 if (httpWrite(HTTP(con), buf, bytes) < 0)
1261 {
1262 CloseClient(con);
1263 return (0);
1264 }
1265
1266 con->bytes += bytes;
1267 }
1268
1269 if (bytes <= 0)
1270 {
1271 LogRequest(con, HTTP_OK);
1272
1273 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
1274 {
1275 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
1276 {
1277 CloseClient(con);
1278 return (0);
1279 }
1280 }
1281
1282 con->http.state = HTTP_WAITING;
1283
1284 FD_CLR(con->http.fd, &OutputSet);
1285
1286 if (con->file)
1287 {
1288 DEBUG_printf(("WriteClient: Removing fd %d from InputSet...\n", con->file));
1289 FD_CLR(con->file, &InputSet);
1290
1291 if (con->pipe_pid)
1292 kill(con->pipe_pid, SIGTERM);
1293
1294 close(con->file);
1295 con->file = 0;
1296 con->pipe_pid = 0;
1297 }
1298
1299 if (con->filename[0])
1300 unlink(con->filename);
1301
1302 if (con->request != NULL)
1303 {
1304 ippDelete(con->request);
1305 con->request = NULL;
1306 }
1307
1308 if (con->response != NULL)
1309 {
1310 ippDelete(con->response);
1311 con->response = NULL;
1312 }
1313
1314 if (!con->http.keep_alive)
1315 {
1316 CloseClient(con);
1317 return (0);
1318 }
1319 }
1320
1321 if (bytes >= 1024)
1322 LogMessage(L_DEBUG, "WriteClient() %d %d bytes", con->http.fd, bytes);
1323
1324 con->http.activity = time(NULL);
1325
1326 return (1);
1327 }
1328
1329
1330 /*
1331 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
1332 */
1333
1334 static int /* O - 1 if modified since */
1335 check_if_modified(client_t *con, /* I - Client connection */
1336 struct stat *filestats) /* I - File information */
1337 {
1338 char *ptr; /* Pointer into field */
1339 time_t date; /* Time/date value */
1340 int size; /* Size/length value */
1341
1342
1343 size = 0;
1344 date = 0;
1345 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
1346
1347 if (*ptr == '\0')
1348 return (1);
1349
1350 LogMessage(L_DEBUG, "check_if_modified() %d If-Modified-Since=\"%s\"",
1351 con->http.fd, ptr);
1352
1353 while (*ptr != '\0')
1354 {
1355 while (isspace(*ptr) || *ptr == ';')
1356 ptr ++;
1357
1358 if (strncasecmp(ptr, "length=", 7) == 0)
1359 {
1360 ptr += 7;
1361 size = atoi(ptr);
1362
1363 while (isdigit(*ptr))
1364 ptr ++;
1365 }
1366 else if (isalpha(*ptr))
1367 {
1368 date = httpGetDateTime(ptr);
1369 while (*ptr != '\0' && *ptr != ';')
1370 ptr ++;
1371 }
1372 }
1373
1374 LogMessage(L_DEBUG, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
1375 con->http.fd, size, filestats->st_size, date, filestats->st_mtime);
1376
1377 return ((size != filestats->st_size && size != 0) ||
1378 (date < filestats->st_mtime && date != 0) ||
1379 (size == 0 && date == 0));
1380 }
1381
1382
1383 /*
1384 * 'decode_auth()' - Decode an authorization string.
1385 */
1386
1387 static void
1388 decode_auth(client_t *con) /* I - Client to decode to */
1389 {
1390 char *s, /* Authorization string */
1391 value[1024]; /* Value string */
1392 const char *username; /* Certificate username */
1393
1394
1395 /*
1396 * Decode the string...
1397 */
1398
1399 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
1400
1401 DEBUG_printf(("decode_auth(%08x): Authorization string = \"%s\"\n",
1402 con, s));
1403
1404 if (strncmp(s, "Basic", 5) == 0)
1405 {
1406 s += 5;
1407 while (isspace(*s))
1408 s ++;
1409
1410 httpDecode64(value, s);
1411
1412 /*
1413 * Pull the username and password out...
1414 */
1415
1416 if ((s = strchr(value, ':')) == NULL)
1417 {
1418 LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
1419 con->http.fd, value);
1420 return;
1421 }
1422
1423 *s++ = '\0';
1424
1425 strncpy(con->username, value, sizeof(con->username) - 1);
1426 con->username[sizeof(con->username) - 1] = '\0';
1427
1428 strncpy(con->password, s, sizeof(con->password) - 1);
1429 con->password[sizeof(con->password) - 1] = '\0';
1430 }
1431 else if (strncmp(s, "Local", 5) == 0)
1432 {
1433 s += 5;
1434 while (isspace(*s))
1435 s ++;
1436
1437 if ((username = FindCert(s)) != NULL)
1438 strcpy(con->username, username);
1439 }
1440 else if (strncmp(s, "Digest", 5) == 0)
1441 {
1442 /*
1443 * Get the username and password from the Digest attributes...
1444 */
1445
1446 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username",
1447 value))
1448 {
1449 strncpy(con->username, value, sizeof(con->username) - 1);
1450 con->username[sizeof(con->username) - 1] = '\0';
1451 }
1452
1453 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response",
1454 value))
1455 {
1456 strncpy(con->password, value, sizeof(con->password) - 1);
1457 con->password[sizeof(con->password) - 1] = '\0';
1458 }
1459 }
1460
1461 LogMessage(L_DEBUG, "decode_auth() %d username=\"%s\"",
1462 con->http.fd, con->username);
1463 DEBUG_printf(("decode_auth() %d username=\"%s\" password=\"%s\"\n",
1464 con->http.fd, con->username, con->password));
1465 }
1466
1467
1468 /*
1469 * 'get_file()' - Get a filename and state info.
1470 */
1471
1472 static char * /* O - Real filename */
1473 get_file(client_t *con, /* I - Client connection */
1474 struct stat *filestats)/* O - File information */
1475 {
1476 int status; /* Status of filesystem calls */
1477 char *params; /* Pointer to parameters in URI */
1478 static char filename[1024]; /* Filename buffer */
1479
1480
1481 /*
1482 * Need to add DocumentRoot global...
1483 */
1484
1485 if (strncmp(con->uri, "/ppd/", 5) == 0)
1486 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri);
1487 else if (con->language != NULL)
1488 snprintf(filename, sizeof(filename), "%s/%s%s", DocumentRoot, con->language->language,
1489 con->uri);
1490 else
1491 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
1492
1493 if ((params = strchr(filename, '?')) != NULL)
1494 *params = '\0';
1495
1496 /*
1497 * Grab the status for this language; if there isn't a language-specific file
1498 * then fallback to the default one...
1499 */
1500
1501 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
1502 {
1503 /*
1504 * Drop the language prefix and try the current directory...
1505 */
1506
1507 if (strncmp(con->uri, "/ppd/", 5) != 0)
1508 {
1509 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
1510
1511 status = stat(filename, filestats);
1512 }
1513 }
1514
1515 /*
1516 * If we're found a directory, get the index.html file instead...
1517 */
1518
1519 if (!status && S_ISDIR(filestats->st_mode))
1520 {
1521 if (filename[strlen(filename) - 1] == '/')
1522 strcat(filename, "index.html");
1523 else
1524 strcat(filename, "/index.html");
1525
1526 status = stat(filename, filestats);
1527 }
1528
1529 LogMessage(L_DEBUG, "get_file() %d filename=%s size=%d",
1530 con->http.fd, filename, status ? -1 : filestats->st_size);
1531
1532 if (status)
1533 return (NULL);
1534 else
1535 return (filename);
1536 }
1537
1538
1539 /*
1540 * 'pipe_command()' - Pipe the output of a command to the remote client.
1541 */
1542
1543 static int /* O - Process ID */
1544 pipe_command(client_t *con, /* I - Client connection */
1545 int infile, /* I - Standard input for command */
1546 int *outfile, /* O - Standard output for command */
1547 char *command, /* I - Command to run */
1548 char *options) /* I - Options for command */
1549 {
1550 int pid; /* Process ID */
1551 char *commptr; /* Command string pointer */
1552 int fds[2]; /* Pipe FDs */
1553 int argc; /* Number of arguments */
1554 int envc; /* Number of environment variables */
1555 char argbuf[10240], /* Argument buffer */
1556 *argv[100], /* Argument strings */
1557 *envp[100]; /* Environment variables */
1558 char hostname[1024], /* Hostname string */
1559 lang[1024], /* LANG env variable */
1560 content_length[1024], /* CONTENT_LENGTH env variable */
1561 content_type[1024], /* CONTENT_TYPE env variable */
1562 ipp_port[1024], /* Default listen port */
1563 server_port[1024], /* Default listen port */
1564 server_name[1024], /* Default listen hostname */
1565 remote_host[1024], /* REMOTE_HOST env variable */
1566 remote_user[1024], /* REMOTE_HOST env variable */
1567 tmpdir[1024], /* TMPDIR environment variable */
1568 ldpath[1024], /* LD_LIBRARY_PATH environment variable */
1569 datadir[1024], /* CUPS_DATADIR environment variable */
1570 root[1024], /* CUPS_SERVERROOT environment variable */
1571 query_string[10240]; /* QUERY_STRING env variable */
1572
1573
1574 /*
1575 * Copy the command string...
1576 */
1577
1578 strncpy(argbuf, options, sizeof(argbuf) - 1);
1579 argbuf[sizeof(argbuf) - 1] = '\0';
1580
1581 /*
1582 * Parse the string; arguments can be separated by + and are terminated
1583 * by ?...
1584 */
1585
1586 argv[0] = argbuf;
1587
1588 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
1589 if (*commptr == ' ' || *commptr == '+')
1590 {
1591 *commptr++ = '\0';
1592
1593 while (*commptr == ' ')
1594 commptr ++;
1595
1596 if (*commptr != '\0')
1597 {
1598 argv[argc] = commptr;
1599 argc ++;
1600 }
1601
1602 commptr --;
1603 }
1604 else if (*commptr == '%')
1605 {
1606 if (commptr[1] >= '0' && commptr[1] <= '9')
1607 *commptr = (commptr[1] - '0') << 4;
1608 else
1609 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
1610
1611 if (commptr[2] >= '0' && commptr[2] <= '9')
1612 *commptr |= commptr[2] - '0';
1613 else
1614 *commptr |= tolower(commptr[2]) - 'a' + 10;
1615
1616 strcpy(commptr + 1, commptr + 3);
1617 }
1618 else if (*commptr == '?')
1619 break;
1620
1621 argv[argc] = NULL;
1622
1623 if (argv[0][0] == '\0')
1624 argv[0] = strrchr(command, '/') + 1;
1625
1626 /*
1627 * Setup the environment variables as needed...
1628 */
1629
1630 gethostname(hostname, sizeof(hostname) - 1);
1631
1632 sprintf(lang, "LANG=%s", con->language ? con->language->language : "C");
1633 sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.sin_port));
1634 sprintf(server_port, "SERVER_PORT=%d", ntohs(con->http.hostaddr.sin_port));
1635 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", hostname);
1636 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname);
1637 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
1638 snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
1639 snprintf(datadir, sizeof(datadir), "CUPS_DATADIR=%s", DataDir);
1640 snprintf(root, sizeof(root), "CUPS_SERVERROOT=%s", ServerRoot);
1641
1642 if (getenv("LD_LIBRARY_PATH") != NULL)
1643 snprintf(ldpath, sizeof(ldpath), "LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
1644 else
1645 ldpath[0] = '\0';
1646
1647 envp[0] = "PATH=/bin:/usr/bin";
1648 envp[1] = "SERVER_SOFTWARE=CUPS/1.1";
1649 envp[2] = "GATEWAY_INTERFACE=CGI/1.1";
1650 envp[3] = "SERVER_PROTOCOL=HTTP/1.1";
1651 envp[4] = ipp_port;
1652 envp[5] = server_name;
1653 envp[6] = server_port;
1654 envp[7] = remote_host;
1655 envp[8] = remote_user;
1656 envp[9] = lang;
1657 envp[10] = TZ;
1658 envp[11] = tmpdir;
1659 envp[12] = datadir;
1660 envp[13] = root;
1661
1662 envc = 14;
1663
1664 if (ldpath[0])
1665 envp[envc ++] = ldpath;
1666
1667 if (con->operation == HTTP_GET)
1668 {
1669 envp[envc ++] = "REQUEST_METHOD=GET";
1670
1671 if (*commptr)
1672 {
1673 /*
1674 * Add GET form variables after ?...
1675 */
1676
1677 *commptr++ = '\0';
1678
1679 snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
1680 envp[envc ++] = query_string;
1681 }
1682 }
1683 else
1684 {
1685 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
1686 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
1687 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1688
1689 envp[envc ++] = "REQUEST_METHOD=POST";
1690 envp[envc ++] = content_length;
1691 envp[envc ++] = content_type;
1692 }
1693
1694 envp[envc] = NULL;
1695
1696 /*
1697 * Create a pipe for the output...
1698 */
1699
1700 if (pipe(fds))
1701 {
1702 LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
1703 argv[0], strerror(errno));
1704 return (0);
1705 }
1706
1707 /*
1708 * Then execute the command...
1709 */
1710
1711 if ((pid = fork()) == 0)
1712 {
1713 /*
1714 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
1715 */
1716
1717 setgid(Group);
1718 setuid(User);
1719
1720 if (infile)
1721 {
1722 close(0);
1723 dup(infile);
1724 }
1725
1726 close(1);
1727 dup(fds[1]);
1728
1729 close(fds[0]);
1730 close(fds[1]);
1731
1732 /*
1733 * Execute the pipe program; if an error occurs, exit with status 1...
1734 */
1735
1736 execve(command, argv, envp);
1737 perror("execve failed");
1738 exit(errno);
1739 return (0);
1740 }
1741 else if (pid < 0)
1742 {
1743 /*
1744 * Error - can't fork!
1745 */
1746
1747 LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
1748 strerror(errno));
1749
1750 close(fds[0]);
1751 close(fds[1]);
1752 return (0);
1753 }
1754 else
1755 {
1756 /*
1757 * Fork successful - return the PID...
1758 */
1759
1760 AddCert(pid, con->username);
1761
1762 LogMessage(L_DEBUG, "CGI %s started - PID = %d", argv[0], pid);
1763
1764 *outfile = fds[0];
1765 close(fds[1]);
1766
1767 return (pid);
1768 }
1769 }
1770
1771
1772 /*
1773 * End of "$Id: client.c,v 1.62 2000/07/10 15:32:27 mike Exp $".
1774 */