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