]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/client.c
Full Apache LogLevel support.
[thirdparty/cups.git] / scheduler / client.c
1 /*
2 * "$Id: client.c,v 1.86 2001/02/21 20:16:46 mike Exp $"
3 *
4 * Client routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-2001 by Easy Software Products, all rights reserved.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * AcceptClient() - Accept a new client.
27 * CloseAllClients() - Close all remote clients immediately.
28 * CloseClient() - Close a remote client.
29 * EncryptClient() - Enable encryption for the client...
30 * ReadClient() - Read data from a client.
31 * SendCommand() - Send output from a command via HTTP.
32 * SendError() - Send an error message via HTTP.
33 * SendFile() - Send a file via HTTP.
34 * SendHeader() - Send an HTTP request.
35 * WriteClient() - Write data to a client as needed.
36 * check_if_modified() - Decode an "If-Modified-Since" line.
37 * decode_auth() - Decode an authorization string.
38 * get_file() - Get a filename and state info.
39 * pipe_command() - Pipe the output of a command to the remote client.
40 */
41
42 /*
43 * Include necessary headers...
44 */
45
46 #include "cupsd.h"
47
48 #ifdef HAVE_LIBSSL
49 # include <openssl/err.h>
50 # include <openssl/ssl.h>
51 # include <openssl/rand.h>
52 #endif /* HAVE_LIBSSL */
53
54
55 /*
56 * Local functions...
57 */
58
59 static int check_if_modified(client_t *con, struct stat *filestats);
60 static void decode_auth(client_t *con);
61 static char *get_file(client_t *con, struct stat *filestats);
62 static int pipe_command(client_t *con, int infile, int *outfile, char *command, char *options);
63
64
65 /*
66 * 'AcceptClient()' - Accept a new client.
67 */
68
69 void
70 AcceptClient(listener_t *lis) /* I - Listener socket */
71 {
72 int i; /* Looping var */
73 int val; /* Parameter value */
74 client_t *con; /* New client pointer */
75 unsigned address;/* Address of client */
76 struct hostent *host; /* Host entry for address */
77
78
79 LogMessage(L_DEBUG2, "AcceptClient(%08x) %d NumClients = %d",
80 lis, lis->fd, NumClients);
81
82 /*
83 * Make sure we don't have a full set of clients already...
84 */
85
86 if (NumClients == MaxClients)
87 return;
88
89 /*
90 * Get a pointer to the next available client...
91 */
92
93 con = Clients + NumClients;
94
95 memset(con, 0, sizeof(client_t));
96 con->http.activity = time(NULL);
97
98 /*
99 * Accept the client and get the remote address...
100 */
101
102 val = sizeof(struct sockaddr_in);
103
104 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr),
105 &val)) < 0)
106 {
107 LogMessage(L_ERROR, "Unable to accept client connection - %s.",
108 strerror(errno));
109 return;
110 }
111
112 con->http.hostaddr.sin_port = lis->address.sin_port;
113
114 /*
115 * Get the hostname or format the IP address as needed...
116 */
117
118 address = ntohl(con->http.hostaddr.sin_addr.s_addr);
119
120 if (HostNameLookups)
121 #ifndef __sgi
122 host = gethostbyaddr((char *)&(con->http.hostaddr.sin_addr),
123 sizeof(struct in_addr), AF_INET);
124 #else
125 host = gethostbyaddr(&(con->http.hostaddr.sin_addr),
126 sizeof(struct in_addr), AF_INET);
127 #endif /* !__sgi */
128 else
129 host = NULL;
130
131 if (con->http.hostaddr.sin_addr.s_addr == ServerAddr.sin_addr.s_addr)
132 {
133 /*
134 * Map accesses from the same host to the server name.
135 */
136
137 strncpy(con->http.hostname, ServerName, sizeof(con->http.hostname) - 1);
138 }
139 else if (host == NULL)
140 {
141 sprintf(con->http.hostname, "%d.%d.%d.%d", (address >> 24) & 255,
142 (address >> 16) & 255, (address >> 8) & 255, address & 255);
143
144 if (HostNameLookups == 2)
145 {
146 /*
147 * Can't have an unresolved IP address with double-lookups enabled...
148 */
149
150 #ifdef WIN32
151 closesocket(con->http.fd);
152 #else
153 close(con->http.fd);
154 #endif /* WIN32 */
155
156 LogMessage(L_WARN, "Name lookup failed - connection from %s closed!",
157 con->http.hostname);
158 return;
159 }
160 }
161 else
162 strncpy(con->http.hostname, host->h_name, sizeof(con->http.hostname) - 1);
163
164 if (HostNameLookups == 2)
165 {
166 /*
167 * Do double lookups as needed...
168 */
169
170 if ((host = gethostbyname(con->http.hostname)) != NULL)
171 {
172 /*
173 * See if the hostname maps to the IP address...
174 */
175
176 if (host->h_length != 4 || host->h_addrtype != AF_INET)
177 {
178 /*
179 * Not an IPv4 address...
180 */
181
182 host = NULL;
183 }
184 else
185 {
186 /*
187 * Compare all of the addresses against this one...
188 */
189
190 for (i = 0; host->h_addr_list[i]; i ++)
191 if (memcmp(&(con->http.hostaddr.sin_addr), host->h_addr_list[i], 4) == 0)
192 break;
193
194 if (!host->h_addr_list[i])
195 host = NULL;
196 }
197 }
198
199 if (host == NULL)
200 {
201 /*
202 * Can't have a hostname that doesn't resolve to the same IP address
203 * with double-lookups enabled...
204 */
205
206 #ifdef WIN32
207 closesocket(con->http.fd);
208 #else
209 close(con->http.fd);
210 #endif /* WIN32 */
211
212 LogMessage(L_WARN, "IP lookup failed - connection from %s closed!",
213 con->http.hostname);
214 return;
215 }
216 }
217
218 LogMessage(L_DEBUG, "AcceptClient() %d from %s:%d.", con->http.fd,
219 con->http.hostname, ntohs(con->http.hostaddr.sin_port));
220
221 /*
222 * Add the socket to the select() input mask.
223 */
224
225 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
226
227 LogMessage(L_DEBUG2, "AcceptClient() Adding fd %d to InputSet...",
228 con->http.fd);
229 FD_SET(con->http.fd, &InputSet);
230
231 NumClients ++;
232
233 /*
234 * Temporarily suspend accept()'s until we lose a client...
235 */
236
237 if (NumClients == MaxClients)
238 PauseListening();
239
240 #ifdef HAVE_LIBSSL
241 /*
242 * See if we are connecting on a secure port...
243 */
244
245 if (lis->encryption == HTTP_ENCRYPT_ALWAYS)
246 {
247 /*
248 * https connection; go secure...
249 */
250
251 EncryptClient(con);
252 }
253 #endif /* HAVE_LIBSSL */
254 }
255
256
257 /*
258 * 'CloseAllClients()' - Close all remote clients immediately.
259 */
260
261 void
262 CloseAllClients(void)
263 {
264 while (NumClients > 0)
265 CloseClient(Clients);
266 }
267
268
269 /*
270 * 'CloseClient()' - Close a remote client.
271 */
272
273 void
274 CloseClient(client_t *con) /* I - Client to close */
275 {
276 int status; /* Exit status of pipe command */
277 #ifdef HAVE_LIBSSL
278 SSL_CTX *context; /* Context for encryption */
279 SSL *conn; /* Connection for encryption */
280 #endif /* HAVE_LIBSSL */
281
282
283 LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd);
284
285 #ifdef HAVE_LIBSSL
286 /*
287 * Shutdown encryption as needed...
288 */
289
290 if (con->http.tls)
291 {
292 conn = (SSL *)(con->http.tls);
293 context = SSL_get_SSL_CTX(conn);
294
295 SSL_shutdown(conn);
296 SSL_CTX_free(context);
297 SSL_free(conn);
298
299 con->http.tls = NULL;
300 }
301 #endif /* HAVE_LIBSSL */
302
303 /*
304 * Close the socket and clear the file from the input set for select()...
305 */
306
307 if (con->http.fd > 0)
308 {
309 LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet...",
310 con->http.fd);
311 close(con->http.fd);
312 FD_CLR(con->http.fd, &InputSet);
313 FD_CLR(con->http.fd, &OutputSet);
314 con->http.fd = 0;
315 }
316
317 if (con->pipe_pid != 0)
318 {
319 LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet...",
320 con->file);
321 FD_CLR(con->file, &InputSet);
322 }
323
324 if (con->file)
325 {
326 /*
327 * Close the open data file...
328 */
329
330 if (con->pipe_pid)
331 {
332 kill(con->pipe_pid, SIGKILL);
333 waitpid(con->pipe_pid, &status, WNOHANG);
334 }
335
336 FD_CLR(con->file, &InputSet);
337 close(con->file);
338 con->file = 0;
339 }
340
341 if (con->request)
342 {
343 ippDelete(con->request);
344 con->request = NULL;
345 }
346
347 if (con->response)
348 {
349 ippDelete(con->response);
350 con->response = NULL;
351 }
352
353 if (con->language)
354 {
355 cupsLangFree(con->language);
356 con->language = NULL;
357 }
358
359 /*
360 * Re-enable new client connections if we are going back under the
361 * limit...
362 */
363
364 if (NumClients == MaxClients)
365 ResumeListening();
366
367 /*
368 * Compact the list of clients as necessary...
369 */
370
371 NumClients --;
372
373 if (con < (Clients + NumClients))
374 memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
375 }
376
377
378 /*
379 * 'EncryptClient()' - Enable encryption for the client...
380 */
381
382 int /* O - 1 on success, 0 on error */
383 EncryptClient(client_t *con) /* I - Client to encrypt */
384 {
385 #ifdef HAVE_LIBSSL
386 SSL_CTX *context; /* Context for encryption */
387 SSL *conn; /* Connection for encryption */
388 int error; /* Error code */
389
390
391 /*
392 * Create the SSL context and accept the connection...
393 */
394
395 context = SSL_CTX_new(SSLv23_method());
396 conn = SSL_new(context);
397
398 SSL_use_PrivateKey_file(conn, ServerKey, SSL_FILETYPE_PEM);
399 SSL_use_certificate_file(conn, ServerCertificate, SSL_FILETYPE_PEM);
400
401 SSL_set_fd(conn, con->http.fd);
402 if (SSL_accept(conn) != 1)
403 {
404 while ((error = ERR_get_error()) != 0)
405 LogMessage(L_ERROR, "EncryptClient: %s", ERR_error_string(error, NULL));
406
407 SSL_CTX_free(context);
408 SSL_free(conn);
409 return (0);
410 }
411
412 con->http.tls = conn;
413 return (1);
414 #else
415 return (0);
416 #endif /* HAVE_LIBSSL */
417 }
418
419
420 /*
421 * 'ReadClient()' - Read data from a client.
422 */
423
424 int /* O - 1 on success, 0 on error */
425 ReadClient(client_t *con) /* I - Client to read from */
426 {
427 char line[8192], /* Line from client... */
428 operation[64], /* Operation code from socket */
429 version[64]; /* HTTP version number string */
430 int major, minor; /* HTTP version numbers */
431 http_status_t status; /* Transfer status */
432 ipp_state_t ipp_state; /* State of IPP transfer */
433 int bytes; /* Number of bytes to POST */
434 char *filename; /* Name of file for GET/HEAD */
435 struct stat filestats; /* File information */
436 mime_type_t *type; /* MIME type of file */
437 printer_t *p; /* Printer */
438 location_t *best; /* Best match for authentication */
439 static unsigned request_id = 0;/* Request ID for temp files */
440
441
442 status = HTTP_CONTINUE;
443
444 switch (con->http.state)
445 {
446 case HTTP_WAITING :
447 /*
448 * See if we've received a request line...
449 */
450
451 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
452 {
453 CloseClient(con);
454 return (0);
455 }
456
457 /*
458 * Ignore blank request lines...
459 */
460
461 if (line[0] == '\0')
462 break;
463
464 /*
465 * Clear other state variables...
466 */
467
468 httpClearFields(HTTP(con));
469
470 con->http.activity = time(NULL);
471 con->http.version = HTTP_1_0;
472 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
473 con->http.data_encoding = HTTP_ENCODE_LENGTH;
474 con->http.data_remaining = 0;
475 con->operation = HTTP_WAITING;
476 con->bytes = 0;
477 con->file = 0;
478 con->pipe_pid = 0;
479 con->command[0] = '\0';
480 con->username[0] = '\0';
481 con->password[0] = '\0';
482 con->uri[0] = '\0';
483
484 if (con->language != NULL)
485 {
486 cupsLangFree(con->language);
487 con->language = NULL;
488 }
489
490 /*
491 * Grab the request line...
492 */
493
494 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
495 {
496 case 1 :
497 SendError(con, HTTP_BAD_REQUEST);
498 CloseClient(con);
499 return (0);
500 case 2 :
501 con->http.version = HTTP_0_9;
502 break;
503 case 3 :
504 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
505 {
506 SendError(con, HTTP_BAD_REQUEST);
507 CloseClient(con);
508 return (0);
509 }
510
511 if (major < 2)
512 {
513 con->http.version = (http_version_t)(major * 100 + minor);
514 if (con->http.version == HTTP_1_1)
515 con->http.keep_alive = HTTP_KEEPALIVE_ON;
516 else
517 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
518 }
519 else
520 {
521 SendError(con, HTTP_NOT_SUPPORTED);
522 CloseClient(con);
523 return (0);
524 }
525 break;
526 }
527
528 /*
529 * Process the request...
530 */
531
532 if (strcmp(operation, "GET") == 0)
533 con->http.state = HTTP_GET;
534 else if (strcmp(operation, "PUT") == 0)
535 con->http.state = HTTP_PUT;
536 else if (strcmp(operation, "POST") == 0)
537 con->http.state = HTTP_POST;
538 else if (strcmp(operation, "DELETE") == 0)
539 con->http.state = HTTP_DELETE;
540 else if (strcmp(operation, "TRACE") == 0)
541 con->http.state = HTTP_TRACE;
542 else if (strcmp(operation, "OPTIONS") == 0)
543 con->http.state = HTTP_OPTIONS;
544 else if (strcmp(operation, "HEAD") == 0)
545 con->http.state = HTTP_HEAD;
546 else
547 {
548 SendError(con, HTTP_BAD_REQUEST);
549 CloseClient(con);
550 return (0);
551 }
552
553 con->start = time(NULL);
554 con->operation = con->http.state;
555
556 LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
557 operation, con->uri,
558 con->http.version / 100, con->http.version % 100);
559
560 con->http.status = HTTP_OK;
561
562 case HTTP_OPTIONS :
563 case HTTP_DELETE :
564 case HTTP_GET :
565 case HTTP_HEAD :
566 case HTTP_POST :
567 case HTTP_PUT :
568 case HTTP_TRACE :
569 /*
570 * Parse incoming parameters until the status changes...
571 */
572
573 status = httpUpdate(HTTP(con));
574
575 if (status != HTTP_OK && status != HTTP_CONTINUE)
576 {
577 SendError(con, HTTP_BAD_REQUEST);
578 CloseClient(con);
579 return (0);
580 }
581 break;
582
583 default :
584 break; /* Anti-compiler-warning-code */
585 }
586
587 /*
588 * Handle new transfers...
589 */
590
591 if (status == HTTP_OK)
592 {
593 con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
594
595 decode_auth(con);
596
597 if (strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) == 0)
598 con->http.keep_alive = HTTP_KEEPALIVE_ON;
599
600 if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
601 con->http.version >= HTTP_1_1)
602 {
603 /*
604 * HTTP/1.1 and higher require the "Host:" field...
605 */
606
607 if (!SendError(con, HTTP_BAD_REQUEST))
608 {
609 CloseClient(con);
610 return (0);
611 }
612 }
613 else if (con->operation == HTTP_OPTIONS)
614 {
615 /*
616 * Do OPTIONS command...
617 */
618
619 if ((best = FindBest(con)) != NULL &&
620 best->type != AUTH_NONE)
621 {
622 if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL))
623 {
624 CloseClient(con);
625 return (0);
626 }
627 }
628
629 if ((strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 ||
630 (best != NULL && best->encryption == HTTP_ENCRYPT_ALWAYS)) &&
631 con->http.tls == NULL)
632 {
633 #ifdef HAVE_LIBSSL
634 /*
635 * Do encryption stuff...
636 */
637
638 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
639 {
640 CloseClient(con);
641 return (0);
642 }
643
644 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
645 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,SSL/2.0,SSL/3.0\r\n");
646 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
647 httpPrintf(HTTP(con), "\r\n");
648
649 EncryptClient(con);
650 #else
651 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
652 {
653 CloseClient(con);
654 return (0);
655 }
656 #endif /* HAVE_LIBSSL */
657 }
658
659 if (!SendHeader(con, HTTP_OK, NULL))
660 {
661 CloseClient(con);
662 return (0);
663 }
664
665 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n");
666 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
667 httpPrintf(HTTP(con), "\r\n");
668 }
669 else if (strstr(con->uri, "..") != NULL)
670 {
671 /*
672 * Protect against malicious users!
673 */
674
675 if (!SendError(con, HTTP_FORBIDDEN))
676 {
677 CloseClient(con);
678 return (0);
679 }
680 }
681 else if (con->uri[0] != '/')
682 {
683 /*
684 * Don't allow proxying (yet)...
685 */
686
687 if (!SendError(con, HTTP_METHOD_NOT_ALLOWED))
688 {
689 CloseClient(con);
690 return (0);
691 }
692 }
693 else
694 {
695 if ((status = IsAuthorized(con)) == HTTP_UPGRADE_REQUIRED)
696 {
697 #ifdef HAVE_LIBSSL
698 /*
699 * Do encryption stuff...
700 */
701
702 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
703 {
704 CloseClient(con);
705 return (0);
706 }
707
708 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
709 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,SSL/2.0,SSL/3.0\r\n");
710 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
711 httpPrintf(HTTP(con), "\r\n");
712
713 EncryptClient(con);
714 #else
715 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
716 {
717 CloseClient(con);
718 return (0);
719 }
720 #endif /* HAVE_LIBSSL */
721 }
722 else if (status != HTTP_OK)
723 {
724 SendError(con, status);
725 CloseClient(con);
726 return (0);
727 }
728
729 switch (con->http.state)
730 {
731 case HTTP_GET_SEND :
732 if (strncmp(con->uri, "/printers/", 10) == 0 &&
733 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
734 {
735 /*
736 * Send PPD file - get the real printer name since printer
737 * names are not case sensitive but filenames can be...
738 */
739
740 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
741
742 if ((p = FindPrinter(con->uri + 10)) != NULL)
743 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
744 else
745 {
746 if (!SendError(con, HTTP_NOT_FOUND))
747 {
748 CloseClient(con);
749 return (0);
750 }
751
752 break;
753 }
754 }
755
756 if (strncmp(con->uri, "/admin", 6) == 0 ||
757 strncmp(con->uri, "/printers", 9) == 0 ||
758 strncmp(con->uri, "/classes", 8) == 0 ||
759 strncmp(con->uri, "/jobs", 5) == 0)
760 {
761 /*
762 * Send CGI output...
763 */
764
765 if (strncmp(con->uri, "/admin", 6) == 0)
766 {
767 snprintf(con->command, sizeof(con->command),
768 "%s/cgi-bin/admin.cgi", ServerBin);
769 con->options = con->uri + 6;
770 }
771 else if (strncmp(con->uri, "/printers", 9) == 0)
772 {
773 snprintf(con->command, sizeof(con->command),
774 "%s/cgi-bin/printers.cgi", ServerBin);
775 con->options = con->uri + 9;
776 }
777 else if (strncmp(con->uri, "/classes", 8) == 0)
778 {
779 snprintf(con->command, sizeof(con->command),
780 "%s/cgi-bin/classes.cgi", ServerBin);
781 con->options = con->uri + 8;
782 }
783 else
784 {
785 snprintf(con->command, sizeof(con->command),
786 "%s/cgi-bin/jobs.cgi", ServerBin);
787 con->options = con->uri + 5;
788 }
789
790 if (con->options[0] == '/')
791 con->options ++;
792
793 if (!SendCommand(con, con->command, con->options))
794 {
795 if (!SendError(con, HTTP_NOT_FOUND))
796 {
797 CloseClient(con);
798 return (0);
799 }
800 }
801 else
802 LogRequest(con, HTTP_OK);
803
804 if (con->http.version <= HTTP_1_0)
805 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
806 }
807 else
808 {
809 /*
810 * Serve a file...
811 */
812
813 if ((filename = get_file(con, &filestats)) == NULL)
814 {
815 if (!SendError(con, HTTP_NOT_FOUND))
816 {
817 CloseClient(con);
818 return (0);
819 }
820 }
821 else if (!check_if_modified(con, &filestats))
822 {
823 if (!SendError(con, HTTP_NOT_MODIFIED))
824 {
825 CloseClient(con);
826 return (0);
827 }
828 }
829 else
830 {
831 type = mimeFileType(MimeDatabase, filename);
832 if (type == NULL)
833 strcpy(line, "text/plain");
834 else
835 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
836
837 if (!SendFile(con, HTTP_OK, filename, line, &filestats))
838 {
839 CloseClient(con);
840 return (0);
841 }
842 }
843 }
844 break;
845
846 case HTTP_POST_RECV :
847 /*
848 * See if the POST request includes a Content-Length field, and if
849 * so check the length against any limits that are set...
850 */
851
852 LogMessage(L_DEBUG2, "POST %s", con->uri);
853 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
854
855 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
856 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
857 MaxRequestSize > 0)
858 {
859 /*
860 * Request too large...
861 */
862
863 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
864 {
865 CloseClient(con);
866 return (0);
867 }
868
869 break;
870 }
871
872 /*
873 * See what kind of POST request this is; for IPP requests the
874 * content-type field will be "application/ipp"...
875 */
876
877 if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
878 con->request = ippNew();
879 else if (strncmp(con->uri, "/admin", 6) == 0 ||
880 strncmp(con->uri, "/printers", 9) == 0 ||
881 strncmp(con->uri, "/classes", 8) == 0 ||
882 strncmp(con->uri, "/jobs", 5) == 0)
883 {
884 /*
885 * CGI request...
886 */
887
888 if (strncmp(con->uri, "/admin", 6) == 0)
889 {
890 snprintf(con->command, sizeof(con->command),
891 "%s/cgi-bin/admin.cgi", ServerBin);
892 con->options = con->uri + 6;
893 }
894 else if (strncmp(con->uri, "/printers", 9) == 0)
895 {
896 snprintf(con->command, sizeof(con->command),
897 "%s/cgi-bin/printers.cgi", ServerBin);
898 con->options = con->uri + 9;
899 }
900 else if (strncmp(con->uri, "/classes", 8) == 0)
901 {
902 snprintf(con->command, sizeof(con->command),
903 "%s/cgi-bin/classes.cgi", ServerBin);
904 con->options = con->uri + 8;
905 }
906 else
907 {
908 snprintf(con->command, sizeof(con->command),
909 "%s/cgi-bin/jobs.cgi", ServerBin);
910 con->options = con->uri + 5;
911 }
912
913 if (con->options[0] == '/')
914 con->options ++;
915
916 LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"",
917 con->http.fd, con->command, con->options);
918
919 if (con->http.version <= HTTP_1_0)
920 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
921 }
922 else if (!SendError(con, HTTP_UNAUTHORIZED))
923 {
924 CloseClient(con);
925 return (0);
926 }
927 break;
928
929 case HTTP_PUT_RECV :
930 case HTTP_DELETE :
931 case HTTP_TRACE :
932 SendError(con, HTTP_NOT_IMPLEMENTED);
933 CloseClient(con);
934 return (0);
935
936 case HTTP_HEAD :
937 if (strncmp(con->uri, "/printers/", 10) == 0 &&
938 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
939 {
940 /*
941 * Send PPD file - get the real printer name since printer
942 * names are not case sensitive but filenames can be...
943 */
944
945 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
946
947 if ((p = FindPrinter(con->uri + 10)) != NULL)
948 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
949 else
950 {
951 if (!SendError(con, HTTP_NOT_FOUND))
952 {
953 CloseClient(con);
954 return (0);
955 }
956
957 break;
958 }
959 }
960
961 if (strncmp(con->uri, "/admin/", 7) == 0 ||
962 strncmp(con->uri, "/printers/", 10) == 0 ||
963 strncmp(con->uri, "/classes/", 9) == 0 ||
964 strncmp(con->uri, "/jobs/", 6) == 0)
965 {
966 /*
967 * CGI output...
968 */
969
970 if (!SendHeader(con, HTTP_OK, "text/html"))
971 {
972 CloseClient(con);
973 return (0);
974 }
975
976 if (httpPrintf(HTTP(con), "\r\n") < 0)
977 {
978 CloseClient(con);
979 return (0);
980 }
981
982 LogRequest(con, HTTP_OK);
983 }
984 else if ((filename = get_file(con, &filestats)) == NULL)
985 {
986 if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
987 {
988 CloseClient(con);
989 return (0);
990 }
991
992 LogRequest(con, HTTP_NOT_FOUND);
993 }
994 else if (!check_if_modified(con, &filestats))
995 {
996 if (!SendError(con, HTTP_NOT_MODIFIED))
997 {
998 CloseClient(con);
999 return (0);
1000 }
1001
1002 LogRequest(con, HTTP_NOT_MODIFIED);
1003 }
1004 else
1005 {
1006 /*
1007 * Serve a file...
1008 */
1009
1010 type = mimeFileType(MimeDatabase, filename);
1011 if (type == NULL)
1012 strcpy(line, "text/plain");
1013 else
1014 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1015
1016 if (!SendHeader(con, HTTP_OK, line))
1017 {
1018 CloseClient(con);
1019 return (0);
1020 }
1021
1022 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1023 httpGetDateString(filestats.st_mtime)) < 0)
1024 {
1025 CloseClient(con);
1026 return (0);
1027 }
1028
1029 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
1030 filestats.st_size) < 0)
1031 {
1032 CloseClient(con);
1033 return (0);
1034 }
1035
1036 LogRequest(con, HTTP_OK);
1037 }
1038
1039 if (httpPrintf(HTTP(con), "\r\n") < 0)
1040 {
1041 CloseClient(con);
1042 return (0);
1043 }
1044
1045 con->http.state = HTTP_WAITING;
1046 break;
1047
1048 default :
1049 break; /* Anti-compiler-warning-code */
1050 }
1051 }
1052 }
1053
1054 /*
1055 * Handle any incoming data...
1056 */
1057
1058 switch (con->http.state)
1059 {
1060 case HTTP_PUT_RECV :
1061 break;
1062
1063 case HTTP_POST_RECV :
1064 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
1065 con->http.fd,
1066 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
1067 con->http.data_remaining, con->file);
1068
1069 if (con->request != NULL)
1070 {
1071 /*
1072 * Grab any request data from the connection...
1073 */
1074
1075 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
1076 {
1077 LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!",
1078 con->http.fd);
1079 CloseClient(con);
1080 return (0);
1081 }
1082 else if (ipp_state != IPP_DATA)
1083 break;
1084 else
1085 con->bytes += ippLength(con->request);
1086 }
1087
1088 if (con->file == 0 && con->http.state != HTTP_POST_SEND)
1089 {
1090 /*
1091 * Create a file as needed for the request data...
1092 */
1093
1094 snprintf(con->filename, sizeof(con->filename), "%s/%08x",
1095 RequestRoot, request_id ++);
1096 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1097 fchmod(con->file, 0640);
1098 fchown(con->file, User, Group);
1099
1100 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s", con->http.fd,
1101 con->filename);
1102
1103 if (con->file < 0)
1104 {
1105 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1106 {
1107 CloseClient(con);
1108 return (0);
1109 }
1110 }
1111 }
1112
1113 if (con->http.state != HTTP_POST_SEND)
1114 {
1115 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
1116 {
1117 CloseClient(con);
1118 return (0);
1119 }
1120 else if (bytes > 0)
1121 {
1122 con->bytes += bytes;
1123
1124 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes",
1125 con->http.fd, bytes);
1126
1127 if (write(con->file, line, bytes) < bytes)
1128 {
1129 close(con->file);
1130 con->file = 0;
1131 unlink(con->filename);
1132
1133 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1134 {
1135 CloseClient(con);
1136 return (0);
1137 }
1138 }
1139 }
1140 else if (con->http.state != HTTP_POST_SEND)
1141 {
1142 CloseClient(con);
1143 return (0);
1144 }
1145 }
1146
1147 if (con->http.state == HTTP_POST_SEND)
1148 {
1149 if (con->file)
1150 {
1151 fstat(con->file, &filestats);
1152 close(con->file);
1153 con->file = 0;
1154
1155 if (filestats.st_size > MaxRequestSize &&
1156 MaxRequestSize > 0)
1157 {
1158 /*
1159 * Request is too big; remove it and send an error...
1160 */
1161
1162 unlink(con->filename);
1163
1164 if (con->request)
1165 {
1166 /*
1167 * Delete any IPP request data...
1168 */
1169
1170 ippDelete(con->request);
1171 con->request = NULL;
1172 }
1173
1174 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1175 {
1176 CloseClient(con);
1177 return (0);
1178 }
1179 }
1180
1181 if (con->command[0])
1182 {
1183 if (!SendCommand(con, con->command, con->options))
1184 {
1185 if (!SendError(con, HTTP_NOT_FOUND))
1186 {
1187 CloseClient(con);
1188 return (0);
1189 }
1190 }
1191 else
1192 LogRequest(con, HTTP_OK);
1193 }
1194 }
1195
1196 if (con->request)
1197 ProcessIPPRequest(con);
1198 }
1199 break;
1200
1201 default :
1202 break; /* Anti-compiler-warning-code */
1203 }
1204
1205 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
1206 {
1207 CloseClient(con);
1208 return (0);
1209 }
1210 else
1211 return (1);
1212 }
1213
1214
1215 /*
1216 * 'SendCommand()' - Send output from a command via HTTP.
1217 */
1218
1219 int
1220 SendCommand(client_t *con,
1221 char *command,
1222 char *options)
1223 {
1224 int fd;
1225
1226
1227 if (con->filename[0])
1228 fd = open(con->filename, O_RDONLY);
1229 else
1230 fd = open("/dev/null", O_RDONLY);
1231
1232 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
1233
1234 close(fd);
1235
1236 LogMessage(L_DEBUG, "SendCommand() %d command=\"%s\" file=%d pipe_pid=%d",
1237 con->http.fd, command, con->file, con->pipe_pid);
1238
1239 if (con->pipe_pid == 0)
1240 return (0);
1241
1242 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1243
1244 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to InputSet...", con->file);
1245 FD_SET(con->file, &InputSet);
1246 FD_SET(con->http.fd, &OutputSet);
1247
1248 if (!SendHeader(con, HTTP_OK, NULL))
1249 return (0);
1250
1251 if (con->http.version == HTTP_1_1)
1252 {
1253 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
1254
1255 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
1256 return (0);
1257 }
1258
1259 con->got_fields = 0;
1260 con->field_col = 0;
1261
1262 return (1);
1263 }
1264
1265
1266 /*
1267 * 'SendError()' - Send an error message via HTTP.
1268 */
1269
1270 int /* O - 1 if successful, 0 otherwise */
1271 SendError(client_t *con, /* I - Connection */
1272 http_status_t code) /* I - Error code */
1273 {
1274 char message[1024]; /* Message for user */
1275
1276
1277 /*
1278 * Put the request in the access_log file...
1279 */
1280
1281 if (con->operation > HTTP_WAITING)
1282 LogRequest(con, code);
1283
1284 /*
1285 * To work around bugs in some proxies, don't use Keep-Alive for some
1286 * error messages...
1287 */
1288
1289 if (code >= HTTP_BAD_REQUEST)
1290 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1291
1292 /*
1293 * Send an error message back to the client. If the error code is a
1294 * 400 or 500 series, make sure the message contains some text, too!
1295 */
1296
1297 if (!SendHeader(con, code, NULL))
1298 return (0);
1299
1300 #ifdef HAVE_LIBSSL
1301 if (code == HTTP_UPGRADE_REQUIRED)
1302 {
1303 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
1304 return (0);
1305
1306 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,SSL/2.0,SSL/3.0\r\n") < 0)
1307 return (0);
1308 }
1309 #endif /* HAVE_LIBSSL */
1310
1311 if (con->http.version >= HTTP_1_1 && !con->http.keep_alive)
1312 {
1313 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
1314 return (0);
1315 }
1316
1317 if (code >= HTTP_BAD_REQUEST)
1318 {
1319 /*
1320 * Send a human-readable error message.
1321 */
1322
1323 snprintf(message, sizeof(message),
1324 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
1325 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
1326 code, httpStatus(code), httpStatus(code),
1327 con->language ? con->language->messages[code] :
1328 httpStatus(code));
1329
1330 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
1331 return (0);
1332 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", strlen(message)) < 0)
1333 return (0);
1334 if (httpPrintf(HTTP(con), "\r\n") < 0)
1335 return (0);
1336 if (httpPrintf(HTTP(con), "%s", message) < 0)
1337 return (0);
1338 }
1339 else if (httpPrintf(HTTP(con), "\r\n") < 0)
1340 return (0);
1341
1342 con->http.state = HTTP_WAITING;
1343
1344 return (1);
1345 }
1346
1347
1348 /*
1349 * 'SendFile()' - Send a file via HTTP.
1350 */
1351
1352 int
1353 SendFile(client_t *con,
1354 http_status_t code,
1355 char *filename,
1356 char *type,
1357 struct stat *filestats)
1358 {
1359 con->file = open(filename, O_RDONLY);
1360
1361 LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
1362
1363 if (con->file < 0)
1364 return (0);
1365
1366 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1367
1368 con->pipe_pid = 0;
1369
1370 if (!SendHeader(con, code, type))
1371 return (0);
1372
1373 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
1374 return (0);
1375 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", filestats->st_size) < 0)
1376 return (0);
1377 if (httpPrintf(HTTP(con), "\r\n") < 0)
1378 return (0);
1379
1380 FD_SET(con->http.fd, &OutputSet);
1381
1382 return (1);
1383 }
1384
1385
1386 /*
1387 * 'SendHeader()' - Send an HTTP request.
1388 */
1389
1390 int /* O - 1 on success, 0 on failure */
1391 SendHeader(client_t *con, /* I - Client to send to */
1392 http_status_t code, /* I - HTTP status code */
1393 char *type) /* I - MIME type of document */
1394 {
1395 location_t *loc; /* Authentication location */
1396
1397
1398 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
1399 con->http.version % 100, code, httpStatus(code)) < 0)
1400 return (0);
1401 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
1402 return (0);
1403 if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
1404 return (0);
1405 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
1406 {
1407 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
1408 return (0);
1409 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
1410 return (0);
1411 }
1412 if (code == HTTP_METHOD_NOT_ALLOWED)
1413 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
1414 return (0);
1415
1416 if (code == HTTP_UNAUTHORIZED)
1417 {
1418 loc = FindBest(con); /* This already succeeded in IsAuthorized */
1419
1420 if (loc->type == AUTH_BASIC)
1421 {
1422 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
1423 return (0);
1424 }
1425 else
1426 {
1427 if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" "
1428 "nonce=\"%s\"\r\n", con->http.hostname) < 0)
1429 return (0);
1430 }
1431 }
1432 if (con->language != NULL)
1433 {
1434 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
1435 con->language->language) < 0)
1436 return (0);
1437
1438 if (type != NULL)
1439 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
1440 cupsLangEncoding(con->language)) < 0)
1441 return (0);
1442 }
1443 else if (type != NULL)
1444 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
1445 return (0);
1446
1447 return (1);
1448 }
1449
1450
1451 /*
1452 * 'WriteClient()' - Write data to a client as needed.
1453 */
1454
1455 int /* O - 1 if success, 0 if fail */
1456 WriteClient(client_t *con) /* I - Client connection */
1457 {
1458 int bytes; /* Number of bytes written */
1459 char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */
1460 char *bufptr; /* Pointer into buffer */
1461 ipp_state_t ipp_state; /* IPP state value */
1462
1463
1464 if (con->http.state != HTTP_GET_SEND &&
1465 con->http.state != HTTP_POST_SEND)
1466 return (1);
1467
1468 if (con->response != NULL)
1469 {
1470 ipp_state = ippWrite(&(con->http), con->response);
1471 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
1472 }
1473 else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
1474 {
1475 if (con->pipe_pid && !con->got_fields)
1476 {
1477 /*
1478 * Inspect the data for Content-Type and other fields.
1479 */
1480
1481 buf[bytes] = '\0';
1482
1483 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
1484 if (*bufptr == '\n')
1485 {
1486 /*
1487 * Send line to client...
1488 */
1489
1490 if (bufptr > buf && bufptr[-1] == '\r')
1491 bufptr[-1] = '\0';
1492 *bufptr++ = '\0';
1493
1494 httpPrintf(HTTP(con), "%s\r\n", buf);
1495 LogMessage(L_DEBUG2, "WriteClient() %d %s", con->http.fd, buf);
1496
1497 /*
1498 * Update buffer...
1499 */
1500
1501 bytes -= (bufptr - buf);
1502 memcpy(buf, bufptr, bytes + 1);
1503 bufptr = buf - 1;
1504
1505 /*
1506 * See if the line was empty...
1507 */
1508
1509 if (con->field_col == 0)
1510 con->got_fields = 1;
1511 else
1512 con->field_col = 0;
1513 }
1514 else if (*bufptr != '\r')
1515 con->field_col ++;
1516
1517 if (bytes > 0 && !con->got_fields)
1518 {
1519 /*
1520 * Remaining text needs to go out...
1521 */
1522
1523 httpPrintf(HTTP(con), "%s", buf);
1524
1525 con->http.activity = time(NULL);
1526 return (1);
1527 }
1528 else if (bytes == 0)
1529 {
1530 con->http.activity = time(NULL);
1531 return (1);
1532 }
1533 }
1534
1535 if (httpWrite(HTTP(con), buf, bytes) < 0)
1536 {
1537 CloseClient(con);
1538 return (0);
1539 }
1540
1541 con->bytes += bytes;
1542 }
1543
1544 if (bytes <= 0)
1545 {
1546 LogRequest(con, HTTP_OK);
1547
1548 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
1549 {
1550 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
1551 {
1552 CloseClient(con);
1553 return (0);
1554 }
1555 }
1556
1557 con->http.state = HTTP_WAITING;
1558
1559 FD_CLR(con->http.fd, &OutputSet);
1560
1561 if (con->file)
1562 {
1563 LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from InputSet...",
1564 con->file);
1565 FD_CLR(con->file, &InputSet);
1566
1567 if (con->pipe_pid)
1568 kill(con->pipe_pid, SIGTERM);
1569
1570 close(con->file);
1571 con->file = 0;
1572 con->pipe_pid = 0;
1573 }
1574
1575 if (con->filename[0])
1576 unlink(con->filename);
1577
1578 if (con->request != NULL)
1579 {
1580 ippDelete(con->request);
1581 con->request = NULL;
1582 }
1583
1584 if (con->response != NULL)
1585 {
1586 ippDelete(con->response);
1587 con->response = NULL;
1588 }
1589
1590 if (!con->http.keep_alive)
1591 {
1592 CloseClient(con);
1593 return (0);
1594 }
1595 }
1596
1597 if (bytes >= 1024)
1598 LogMessage(L_DEBUG2, "WriteClient() %d %d bytes", con->http.fd, bytes);
1599
1600 con->http.activity = time(NULL);
1601
1602 return (1);
1603 }
1604
1605
1606 /*
1607 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
1608 */
1609
1610 static int /* O - 1 if modified since */
1611 check_if_modified(client_t *con, /* I - Client connection */
1612 struct stat *filestats) /* I - File information */
1613 {
1614 char *ptr; /* Pointer into field */
1615 time_t date; /* Time/date value */
1616 int size; /* Size/length value */
1617
1618
1619 size = 0;
1620 date = 0;
1621 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
1622
1623 if (*ptr == '\0')
1624 return (1);
1625
1626 LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"",
1627 con->http.fd, ptr);
1628
1629 while (*ptr != '\0')
1630 {
1631 while (isspace(*ptr) || *ptr == ';')
1632 ptr ++;
1633
1634 if (strncasecmp(ptr, "length=", 7) == 0)
1635 {
1636 ptr += 7;
1637 size = atoi(ptr);
1638
1639 while (isdigit(*ptr))
1640 ptr ++;
1641 }
1642 else if (isalpha(*ptr))
1643 {
1644 date = httpGetDateTime(ptr);
1645 while (*ptr != '\0' && *ptr != ';')
1646 ptr ++;
1647 }
1648 }
1649
1650 LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
1651 con->http.fd, size, filestats->st_size, date, filestats->st_mtime);
1652
1653 return ((size != filestats->st_size && size != 0) ||
1654 (date < filestats->st_mtime && date != 0) ||
1655 (size == 0 && date == 0));
1656 }
1657
1658
1659 /*
1660 * 'decode_auth()' - Decode an authorization string.
1661 */
1662
1663 static void
1664 decode_auth(client_t *con) /* I - Client to decode to */
1665 {
1666 char *s, /* Authorization string */
1667 value[1024]; /* Value string */
1668 const char *username; /* Certificate username */
1669
1670
1671 /*
1672 * Decode the string...
1673 */
1674
1675 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
1676
1677 LogMessage(L_DEBUG2, "decode_auth(%08x): Authorization string = \"%s\"",
1678 con, s);
1679
1680 if (strncmp(s, "Basic", 5) == 0)
1681 {
1682 s += 5;
1683 while (isspace(*s))
1684 s ++;
1685
1686 httpDecode64(value, s);
1687
1688 /*
1689 * Pull the username and password out...
1690 */
1691
1692 if ((s = strchr(value, ':')) == NULL)
1693 {
1694 LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
1695 con->http.fd, value);
1696 return;
1697 }
1698
1699 *s++ = '\0';
1700
1701 strncpy(con->username, value, sizeof(con->username) - 1);
1702 con->username[sizeof(con->username) - 1] = '\0';
1703
1704 strncpy(con->password, s, sizeof(con->password) - 1);
1705 con->password[sizeof(con->password) - 1] = '\0';
1706 }
1707 else if (strncmp(s, "Local", 5) == 0)
1708 {
1709 s += 5;
1710 while (isspace(*s))
1711 s ++;
1712
1713 if ((username = FindCert(s)) != NULL)
1714 {
1715 strncpy(con->username, username, sizeof(con->username) - 1);
1716 con->username[sizeof(con->username) - 1] = '\0';
1717 }
1718 }
1719 else if (strncmp(s, "Digest", 5) == 0)
1720 {
1721 /*
1722 * Get the username and password from the Digest attributes...
1723 */
1724
1725 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username",
1726 value))
1727 {
1728 strncpy(con->username, value, sizeof(con->username) - 1);
1729 con->username[sizeof(con->username) - 1] = '\0';
1730 }
1731
1732 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response",
1733 value))
1734 {
1735 strncpy(con->password, value, sizeof(con->password) - 1);
1736 con->password[sizeof(con->password) - 1] = '\0';
1737 }
1738 }
1739
1740 LogMessage(L_DEBUG2, "decode_auth() %d username=\"%s\"",
1741 con->http.fd, con->username);
1742 }
1743
1744
1745 /*
1746 * 'get_file()' - Get a filename and state info.
1747 */
1748
1749 static char * /* O - Real filename */
1750 get_file(client_t *con, /* I - Client connection */
1751 struct stat *filestats)/* O - File information */
1752 {
1753 int status; /* Status of filesystem calls */
1754 char *params; /* Pointer to parameters in URI */
1755 static char filename[1024]; /* Filename buffer */
1756
1757
1758 /*
1759 * Need to add DocumentRoot global...
1760 */
1761
1762 if (strncmp(con->uri, "/ppd/", 5) == 0)
1763 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri);
1764 else if (con->language != NULL)
1765 snprintf(filename, sizeof(filename), "%s/%s%s", DocumentRoot, con->language->language,
1766 con->uri);
1767 else
1768 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
1769
1770 if ((params = strchr(filename, '?')) != NULL)
1771 *params = '\0';
1772
1773 /*
1774 * Grab the status for this language; if there isn't a language-specific file
1775 * then fallback to the default one...
1776 */
1777
1778 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
1779 {
1780 /*
1781 * Drop the language prefix and try the current directory...
1782 */
1783
1784 if (strncmp(con->uri, "/ppd/", 5) != 0)
1785 {
1786 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
1787
1788 status = stat(filename, filestats);
1789 }
1790 }
1791
1792 /*
1793 * If we're found a directory, get the index.html file instead...
1794 */
1795
1796 if (!status && S_ISDIR(filestats->st_mode))
1797 {
1798 if (filename[strlen(filename) - 1] == '/')
1799 strncat(filename, "index.html", sizeof(filename));
1800 else
1801 strncat(filename, "/index.html", sizeof(filename));
1802
1803 filename[sizeof(filename) - 1] = '\0';
1804
1805 status = stat(filename, filestats);
1806 }
1807
1808 LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d",
1809 con->http.fd, filename, status ? -1 : filestats->st_size);
1810
1811 if (status)
1812 return (NULL);
1813 else
1814 return (filename);
1815 }
1816
1817
1818 /*
1819 * 'pipe_command()' - Pipe the output of a command to the remote client.
1820 */
1821
1822 static int /* O - Process ID */
1823 pipe_command(client_t *con, /* I - Client connection */
1824 int infile, /* I - Standard input for command */
1825 int *outfile, /* O - Standard output for command */
1826 char *command, /* I - Command to run */
1827 char *options) /* I - Options for command */
1828 {
1829 int pid; /* Process ID */
1830 char *commptr; /* Command string pointer */
1831 int fd; /* Looping var */
1832 int fds[2]; /* Pipe FDs */
1833 int argc; /* Number of arguments */
1834 int envc; /* Number of environment variables */
1835 char argbuf[10240], /* Argument buffer */
1836 *argv[100], /* Argument strings */
1837 *envp[100]; /* Environment variables */
1838 char lang[1024], /* LANG env variable */
1839 content_length[1024], /* CONTENT_LENGTH env variable */
1840 content_type[1024], /* CONTENT_TYPE env variable */
1841 ipp_port[1024], /* Default listen port */
1842 server_port[1024], /* Default server port */
1843 server_name[1024], /* Default listen hostname */
1844 remote_host[1024], /* REMOTE_HOST env variable */
1845 remote_user[1024], /* REMOTE_USER env variable */
1846 tmpdir[1024], /* TMPDIR environment variable */
1847 ldpath[1024], /* LD_LIBRARY_PATH environment variable */
1848 datadir[1024], /* CUPS_DATADIR environment variable */
1849 root[1024], /* CUPS_SERVERROOT environment variable */
1850 query_string[10240]; /* QUERY_STRING env variable */
1851
1852
1853 /*
1854 * Copy the command string...
1855 */
1856
1857 strncpy(argbuf, options, sizeof(argbuf) - 1);
1858 argbuf[sizeof(argbuf) - 1] = '\0';
1859
1860 /*
1861 * Parse the string; arguments can be separated by + and are terminated
1862 * by ?...
1863 */
1864
1865 argv[0] = argbuf;
1866
1867 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
1868 if (*commptr == ' ' || *commptr == '+')
1869 {
1870 *commptr++ = '\0';
1871
1872 while (*commptr == ' ')
1873 commptr ++;
1874
1875 if (*commptr != '\0')
1876 {
1877 argv[argc] = commptr;
1878 argc ++;
1879 }
1880
1881 commptr --;
1882 }
1883 else if (*commptr == '%')
1884 {
1885 if (commptr[1] >= '0' && commptr[1] <= '9')
1886 *commptr = (commptr[1] - '0') << 4;
1887 else
1888 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
1889
1890 if (commptr[2] >= '0' && commptr[2] <= '9')
1891 *commptr |= commptr[2] - '0';
1892 else
1893 *commptr |= tolower(commptr[2]) - 'a' + 10;
1894
1895 strcpy(commptr + 1, commptr + 3);
1896 }
1897 else if (*commptr == '?')
1898 break;
1899
1900 argv[argc] = NULL;
1901
1902 if (argv[0][0] == '\0')
1903 argv[0] = strrchr(command, '/') + 1;
1904
1905 /*
1906 * Setup the environment variables as needed...
1907 */
1908
1909 snprintf(lang, sizeof(lang), "LANG=%s",
1910 con->language ? con->language->language : "C");
1911 sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.sin_port));
1912 sprintf(server_port, "SERVER_PORT=%d", ntohs(con->http.hostaddr.sin_port));
1913 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName);
1914 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname);
1915 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
1916 snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
1917 snprintf(datadir, sizeof(datadir), "CUPS_DATADIR=%s", DataDir);
1918 snprintf(root, sizeof(root), "CUPS_SERVERROOT=%s", ServerRoot);
1919
1920 if (getenv("LD_LIBRARY_PATH") != NULL)
1921 snprintf(ldpath, sizeof(ldpath), "LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
1922 else
1923 ldpath[0] = '\0';
1924
1925 envp[0] = "PATH=/bin:/usr/bin";
1926 envp[1] = "SERVER_SOFTWARE=CUPS/1.1";
1927 envp[2] = "GATEWAY_INTERFACE=CGI/1.1";
1928 envp[3] = "SERVER_PROTOCOL=HTTP/1.1";
1929 envp[4] = ipp_port;
1930 envp[5] = server_name;
1931 envp[6] = server_port;
1932 envp[7] = remote_host;
1933 envp[8] = remote_user;
1934 envp[9] = lang;
1935 envp[10] = TZ;
1936 envp[11] = tmpdir;
1937 envp[12] = datadir;
1938 envp[13] = root;
1939
1940 envc = 14;
1941
1942 if (ldpath[0])
1943 envp[envc ++] = ldpath;
1944
1945 if (con->operation == HTTP_GET)
1946 {
1947 envp[envc ++] = "REQUEST_METHOD=GET";
1948
1949 if (*commptr)
1950 {
1951 /*
1952 * Add GET form variables after ?...
1953 */
1954
1955 *commptr++ = '\0';
1956
1957 snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
1958 envp[envc ++] = query_string;
1959 }
1960 }
1961 else
1962 {
1963 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
1964 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
1965 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1966
1967 envp[envc ++] = "REQUEST_METHOD=POST";
1968 envp[envc ++] = content_length;
1969 envp[envc ++] = content_type;
1970 }
1971
1972 envp[envc] = NULL;
1973
1974 /*
1975 * Create a pipe for the output...
1976 */
1977
1978 if (pipe(fds))
1979 {
1980 LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
1981 argv[0], strerror(errno));
1982 return (0);
1983 }
1984
1985 /*
1986 * Then execute the command...
1987 */
1988
1989 if ((pid = fork()) == 0)
1990 {
1991 /*
1992 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
1993 */
1994
1995 if (getuid() == 0)
1996 {
1997 /*
1998 * Running as root, so change to non-priviledged user...
1999 */
2000
2001 if (setgid(Group))
2002 exit(errno);
2003
2004 if (setuid(User))
2005 exit(errno);
2006 }
2007
2008 /*
2009 * Reset group membership to just the main one we belong to.
2010 */
2011
2012 setgroups(0, NULL);
2013
2014 /*
2015 * Update stdin/stdout...
2016 */
2017
2018 if (infile)
2019 {
2020 close(0);
2021 dup(infile);
2022 }
2023
2024 close(1);
2025 dup(fds[1]);
2026
2027 /*
2028 * Close extra file descriptors...
2029 */
2030
2031 for (fd = 3; fd < MaxFDs; fd ++)
2032 close(fd);
2033
2034 /*
2035 * Change umask to restrict permissions on created files...
2036 */
2037
2038 umask(077);
2039
2040 /*
2041 * Execute the pipe program; if an error occurs, exit with status 1...
2042 */
2043
2044 execve(command, argv, envp);
2045 perror("execve failed");
2046 exit(errno);
2047 return (0);
2048 }
2049 else if (pid < 0)
2050 {
2051 /*
2052 * Error - can't fork!
2053 */
2054
2055 LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
2056 strerror(errno));
2057
2058 close(fds[0]);
2059 close(fds[1]);
2060 return (0);
2061 }
2062 else
2063 {
2064 /*
2065 * Fork successful - return the PID...
2066 */
2067
2068 AddCert(pid, con->username);
2069
2070 LogMessage(L_DEBUG, "CGI %s started - PID = %d", argv[0], pid);
2071
2072 *outfile = fds[0];
2073 close(fds[1]);
2074
2075 return (pid);
2076 }
2077 }
2078
2079
2080 /*
2081 * End of "$Id: client.c,v 1.86 2001/02/21 20:16:46 mike Exp $".
2082 */