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