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