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