]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/client.c
Mirror 1.1.x changes.
[thirdparty/cups.git] / scheduler / client.c
1 /*
2 * "$Id: client.c,v 1.91.2.76 2004/02/25 16:22:01 mike Exp $"
3 *
4 * Client routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-2003 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 * IsCGI() - Is the resource a CGI script/program?
31 * ReadClient() - Read data from a client.
32 * SendCommand() - Send output from a command via HTTP.
33 * SendError() - Send an error message via HTTP.
34 * SendFile() - Send a file via HTTP.
35 * SendHeader() - Send an HTTP request.
36 * UpdateCGI() - Read status messages from CGI scripts and programs.
37 * WriteClient() - Write data to a client as needed.
38 * check_if_modified() - Decode an "If-Modified-Since" line.
39 * decode_auth() - Decode an authorization string.
40 * get_file() - Get a filename and state info.
41 * install_conf_file() - Install a configuration file.
42 * pipe_command() - Pipe the output of a command to the remote client.
43 * CDSAReadFunc() - Read function for CDSA decryption code.
44 * CDSAWriteFunc() - Write function for CDSA encryption code.
45 */
46
47 /*
48 * Include necessary headers...
49 */
50
51 #include <cups/http-private.h>
52 #include "cupsd.h"
53 #include <grp.h>
54
55
56 /*
57 * Local functions...
58 */
59
60 static int check_if_modified(client_t *con,
61 struct stat *filestats);
62 static void decode_auth(client_t *con);
63 static char *get_file(client_t *con, struct stat *filestats,
64 char *filename, int len);
65 static http_status_t install_conf_file(client_t *con);
66 static int pipe_command(client_t *con, int infile, int *outfile,
67 char *command, char *options);
68
69 #ifdef HAVE_CDSASSL
70 static OSStatus CDSAReadFunc(SSLConnectionRef connection, void *data,
71 size_t *dataLength);
72 static OSStatus CDSAWriteFunc(SSLConnectionRef connection,
73 const void *data, size_t *dataLength);
74 #endif /* HAVE_CDSASSL */
75
76
77 /*
78 * 'AcceptClient()' - Accept a new client.
79 */
80
81 void
82 AcceptClient(listener_t *lis) /* I - Listener socket */
83 {
84 int i; /* Looping var */
85 int count; /* Count of connections on a host */
86 int val; /* Parameter value */
87 client_t *con; /* New client pointer */
88 const struct hostent *host; /* Host entry for address */
89 char *hostname;/* Hostname for address */
90 http_addr_t temp; /* Temporary address variable */
91 static time_t last_dos = 0;
92 /* Time of last DoS attack */
93
94
95 LogMessage(L_DEBUG2, "AcceptClient(lis=%p) %d NumClients = %d",
96 lis, lis->fd, NumClients);
97
98 /*
99 * Make sure we don't have a full set of clients already...
100 */
101
102 if (NumClients == MaxClients)
103 return;
104
105 /*
106 * Get a pointer to the next available client...
107 */
108
109 con = Clients + NumClients;
110
111 memset(con, 0, sizeof(client_t));
112 con->http.activity = time(NULL);
113
114 /*
115 * Accept the client and get the remote address...
116 */
117
118 val = sizeof(struct sockaddr_in);
119
120 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr),
121 &val)) < 0)
122 {
123 LogMessage(L_ERROR, "Unable to accept client connection - %s.",
124 strerror(errno));
125 return;
126 }
127
128 #ifdef AF_INET6
129 if (lis->address.addr.sa_family == AF_INET6)
130 con->http.hostaddr.ipv6.sin6_port = lis->address.ipv6.sin6_port;
131 else
132 #endif /* AF_INET6 */
133 con->http.hostaddr.ipv4.sin_port = lis->address.ipv4.sin_port;
134
135 /*
136 * Check the number of clients on the same address...
137 */
138
139 for (i = 0, count = 0; i < NumClients; i ++)
140 if (memcmp(&(Clients[i].http.hostaddr), &(con->http.hostaddr),
141 sizeof(con->http.hostaddr)) == 0)
142 {
143 count ++;
144 if (count >= MaxClientsPerHost)
145 break;
146 }
147
148 if (count >= MaxClientsPerHost)
149 {
150 if ((time(NULL) - last_dos) >= 60)
151 {
152 last_dos = time(NULL);
153 LogMessage(L_WARN, "Possible DoS attack - more than %d clients connecting from %s!",
154 MaxClientsPerHost, Clients[i].http.hostname);
155 }
156
157 #ifdef WIN32
158 closesocket(con->http.fd);
159 #else
160 close(con->http.fd);
161 #endif /* WIN32 */
162
163 return;
164 }
165
166 /*
167 * Get the hostname or format the IP address as needed...
168 */
169
170 if (HostNameLookups)
171 hostname = httpAddrLookup(&(con->http.hostaddr), con->http.hostname,
172 sizeof(con->http.hostname));
173 else
174 {
175 hostname = NULL;
176 httpAddrString(&(con->http.hostaddr), con->http.hostname,
177 sizeof(con->http.hostname));
178 }
179
180 if (httpAddrLocalhost(&(con->http.hostaddr)))
181 {
182 /*
183 * Map accesses from the loopback interface to "localhost"...
184 */
185
186 strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname));
187 }
188 else if (httpAddrEqual(&(con->http.hostaddr), &ServerAddr))
189 {
190 /*
191 * Map accesses from the same host to the server name.
192 */
193
194 strlcpy(con->http.hostname, ServerName, sizeof(con->http.hostname));
195 }
196
197 if (hostname == NULL && HostNameLookups == 2)
198 {
199 /*
200 * Can't have an unresolved IP address with double-lookups enabled...
201 */
202
203 LogMessage(L_DEBUG2, "AcceptClient: Closing connection %d...",
204 con->http.fd);
205
206 #ifdef WIN32
207 closesocket(con->http.fd);
208 #else
209 close(con->http.fd);
210 #endif /* WIN32 */
211
212 LogMessage(L_WARN, "Name lookup failed - connection from %s closed!",
213 con->http.hostname);
214 return;
215 }
216
217 if (HostNameLookups == 2)
218 {
219 /*
220 * Do double lookups as needed...
221 */
222
223 if ((host = httpGetHostByName(con->http.hostname)) != NULL)
224 {
225 /*
226 * See if the hostname maps to the same IP address...
227 */
228
229 if (host->h_addrtype != con->http.hostaddr.addr.sa_family)
230 {
231 /*
232 * Not the right type of address...
233 */
234
235 host = NULL;
236 }
237 else
238 {
239 /*
240 * Compare all of the addresses against this one...
241 */
242
243 for (i = 0; host->h_addr_list[i]; i ++)
244 {
245 httpAddrLoad(host, 0, i, &temp);
246
247 if (httpAddrEqual(&(con->http.hostaddr), &temp))
248 break;
249 }
250
251 if (!host->h_addr_list[i])
252 host = NULL;
253 }
254 }
255
256 if (host == NULL)
257 {
258 /*
259 * Can't have a hostname that doesn't resolve to the same IP address
260 * with double-lookups enabled...
261 */
262
263 LogMessage(L_DEBUG2, "AcceptClient: Closing connection %d...",
264 con->http.fd);
265
266 #ifdef WIN32
267 closesocket(con->http.fd);
268 #else
269 close(con->http.fd);
270 #endif /* WIN32 */
271
272 LogMessage(L_WARN, "IP lookup failed - connection from %s closed!",
273 con->http.hostname);
274 return;
275 }
276 }
277
278 #ifdef AF_INET6
279 if (con->http.hostaddr.addr.sa_family == AF_INET6)
280 LogMessage(L_DEBUG, "AcceptClient: %d from %s:%d.", con->http.fd,
281 con->http.hostname, ntohs(con->http.hostaddr.ipv6.sin6_port));
282 else
283 #endif /* AF_INET6 */
284 LogMessage(L_DEBUG, "AcceptClient: %d from %s:%d.", con->http.fd,
285 con->http.hostname, ntohs(con->http.hostaddr.ipv4.sin_port));
286
287 /*
288 * Using TCP_NODELAY improves responsiveness, especially on systems
289 * with a slow loopback interface... Since we write large buffers
290 * when sending print files and requests, there shouldn't be any
291 * performance penalty for this...
292 */
293
294 val = 1;
295 setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
296
297 /*
298 * Close this file on all execs...
299 */
300
301 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
302
303 /*
304 * Add the socket to the select() input mask.
305 */
306
307 LogMessage(L_DEBUG2, "AcceptClient: Adding fd %d to InputSet...",
308 con->http.fd);
309 FD_SET(con->http.fd, InputSet);
310
311 NumClients ++;
312
313 /*
314 * Temporarily suspend accept()'s until we lose a client...
315 */
316
317 if (NumClients == MaxClients)
318 PauseListening();
319
320 #ifdef HAVE_SSL
321 /*
322 * See if we are connecting on a secure port...
323 */
324
325 if (lis->encryption == HTTP_ENCRYPT_ALWAYS)
326 {
327 /*
328 * https connection; go secure...
329 */
330
331 con->http.encryption = HTTP_ENCRYPT_ALWAYS;
332
333 EncryptClient(con);
334 }
335 #endif /* HAVE_SSL */
336 }
337
338
339 /*
340 * 'CloseAllClients()' - Close all remote clients immediately.
341 */
342
343 void
344 CloseAllClients(void)
345 {
346 while (NumClients > 0)
347 CloseClient(Clients);
348 }
349
350
351 /*
352 * 'CloseClient()' - Close a remote client.
353 */
354
355 void
356 CloseClient(client_t *con) /* I - Client to close */
357 {
358 int partial; /* Do partial close for SSL? */
359 #if defined(HAVE_LIBSSL)
360 SSL_CTX *context; /* Context for encryption */
361 SSL *conn; /* Connection for encryption */
362 unsigned long error; /* Error code */
363 #elif defined(HAVE_GNUTLS)
364 http_tls_t *conn; /* TLS connection information */
365 int error; /* Error code */
366 gnutls_certificate_server_credentials *credentials;
367 /* TLS credentials */
368 #endif /* HAVE_GNUTLS */
369
370
371 LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd);
372
373 partial = 0;
374
375 #ifdef HAVE_SSL
376 /*
377 * Shutdown encryption as needed...
378 */
379
380 if (con->http.tls)
381 {
382 partial = 1;
383
384 # ifdef HAVE_LIBSSL
385 conn = (SSL *)(con->http.tls);
386 context = SSL_get_SSL_CTX(conn);
387
388 switch (SSL_shutdown(conn))
389 {
390 case 1 :
391 LogMessage(L_INFO, "CloseClient: SSL shutdown successful!");
392 break;
393 case -1 :
394 LogMessage(L_ERROR, "CloseClient: Fatal error during SSL shutdown!");
395 default :
396 while ((error = ERR_get_error()) != 0)
397 LogMessage(L_ERROR, "CloseClient: %s", ERR_error_string(error, NULL));
398 break;
399 }
400
401 SSL_CTX_free(context);
402 SSL_free(conn);
403
404 # elif defined(HAVE_GNUTLS)
405 conn = (http_tls_t *)(con->http.tls);
406 credentials = (gnutls_certificate_server_credentials *)(conn->credentials);
407
408 error = gnutls_bye(conn->session, GNUTLS_SHUT_WR);
409 switch (error)
410 {
411 case GNUTLS_E_SUCCESS:
412 LogMessage(L_INFO, "CloseClient: SSL shutdown successful!");
413 break;
414 default:
415 LogMessage(L_ERROR, "CloseClient: %s", gnutls_strerror(error));
416 break;
417 }
418
419 gnutls_deinit(conn->session);
420 gnutls_certificate_free_credentials(*credentials);
421 free(credentials);
422 free(conn);
423
424 # elif defined(HAVE_CDSASSL)
425 status = SSLClose((SSLContextRef)con->http.tls);
426 SSLDisposeContext((SSLContextRef)con->http.tls);
427 # endif /* HAVE_LIBSSL */
428
429 con->http.tls = NULL;
430 }
431 #endif /* HAVE_SSL */
432
433 /*
434 * Close the socket and clear the file from the input set for select()...
435 */
436
437 if (con->http.fd > 0)
438 {
439 if (partial)
440 {
441 /*
442 * Only do a partial close so that the encrypted client gets everything.
443 */
444
445 LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from OutputSet...",
446 con->http.fd);
447 shutdown(con->http.fd, 0);
448 FD_CLR(con->http.fd, OutputSet);
449 }
450 else
451 {
452 /*
453 * Shut the socket down fully...
454 */
455
456 LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...",
457 con->http.fd);
458 close(con->http.fd);
459 FD_CLR(con->http.fd, InputSet);
460 FD_CLR(con->http.fd, OutputSet);
461 con->http.fd = 0;
462 }
463 }
464
465 if (con->pipe_pid != 0)
466 {
467 /*
468 * Stop any CGI process...
469 */
470
471 LogMessage(L_DEBUG2, "CloseClient: %d Killing process ID %d...",
472 con->http.fd, con->pipe_pid);
473 kill(con->pipe_pid, SIGKILL);
474 }
475
476 if (con->file >= 0)
477 {
478 if (FD_ISSET(con->file, InputSet))
479 {
480 LogMessage(L_DEBUG2, "CloseClient: %d Removing fd %d from InputSet...",
481 con->http.fd, con->file);
482 FD_CLR(con->file, InputSet);
483 }
484
485 LogMessage(L_DEBUG2, "CloseClient: %d Closing data file %d.",
486 con->http.fd, con->file);
487
488 close(con->file);
489 con->file = -1;
490 }
491
492 if (!partial)
493 {
494 /*
495 * Free memory...
496 */
497
498 if (con->http.input_set)
499 free(con->http.input_set);
500
501 httpClearCookie(HTTP(con));
502
503 ClearString(&con->filename);
504 ClearString(&con->command);
505 ClearString(&con->options);
506
507 if (con->request)
508 {
509 ippDelete(con->request);
510 con->request = NULL;
511 }
512
513 if (con->response)
514 {
515 ippDelete(con->response);
516 con->response = NULL;
517 }
518
519 if (con->language)
520 {
521 cupsLangFree(con->language);
522 con->language = NULL;
523 }
524
525 /*
526 * Re-enable new client connections if we are going back under the
527 * limit...
528 */
529
530 if (NumClients == MaxClients)
531 ResumeListening();
532
533 /*
534 * Compact the list of clients as necessary...
535 */
536
537 NumClients --;
538
539 if (con < (Clients + NumClients))
540 memmove(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
541 }
542 }
543
544
545 /*
546 * 'EncryptClient()' - Enable encryption for the client...
547 */
548
549 int /* O - 1 on success, 0 on error */
550 EncryptClient(client_t *con) /* I - Client to encrypt */
551 {
552 #if defined HAVE_LIBSSL
553 SSL_CTX *context; /* Context for encryption */
554 SSL *conn; /* Connection for encryption */
555 unsigned long error; /* Error code */
556
557
558 /*
559 * Create the SSL context and accept the connection...
560 */
561
562 context = SSL_CTX_new(SSLv23_server_method());
563
564 SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
565 SSL_CTX_use_certificate_file(context, ServerCertificate, SSL_FILETYPE_PEM);
566
567 conn = SSL_new(context);
568
569 SSL_set_fd(conn, con->http.fd);
570 if (SSL_accept(conn) != 1)
571 {
572 while ((error = ERR_get_error()) != 0)
573 LogMessage(L_ERROR, "EncryptClient: %s", ERR_error_string(error, NULL));
574
575 SSL_CTX_free(context);
576 SSL_free(conn);
577 return (0);
578 }
579
580 con->http.tls = conn;
581 return (1);
582
583 #elif defined(HAVE_GNUTLS)
584 http_tls_t *conn; /* TLS session object */
585 int error; /* Error code */
586 gnutls_certificate_server_credentials *credentials;
587 /* TLS credentials */
588
589 /*
590 * Create the SSL object and perform the SSL handshake...
591 */
592
593 conn = (http_tls_t *)malloc(sizeof(gnutls_session));
594
595 if (conn == NULL)
596 return (0);
597
598 credentials = (gnutls_certificate_server_credentials *)
599 malloc(sizeof(gnutls_certificate_server_credentials));
600 if (credentials == NULL)
601 {
602 free(conn);
603 return (0);
604 }
605
606 gnutls_certificate_allocate_credentials(credentials);
607 gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
608 ServerKey, GNUTLS_X509_FMT_PEM);
609
610 gnutls_init(&(conn->session), GNUTLS_SERVER);
611 gnutls_set_default_priority(conn->session);
612 gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials);
613 gnutls_transport_set_ptr(conn->session, con->http.fd);
614
615 error = gnutls_handshake(conn->session);
616
617 if (error != GNUTLS_E_SUCCESS)
618 {
619 LogMessage(L_ERROR, "EncryptClient: %s", gnutls_strerror(error));
620 gnutls_deinit(conn->session);
621 gnutls_certificate_free_credentials(*credentials);
622 free(conn);
623 free(credentials);
624 return (0);
625 }
626
627 LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.",
628 con->http.fd);
629
630 conn->credentials = credentials;
631 con->http.tls = conn;
632 return (1);
633
634 #elif defined(HAVE_CDSASSL)
635 OSStatus error; /* Error info */
636 SSLContextRef conn; /* New connection */
637 SSLProtocol tryVersion; /* Protocol version */
638 const char *hostName; /* Local hostname */
639 int allowExpired; /* Allow expired certificates? */
640 int allowAnyRoot; /* Allow any root certificate? */
641 SSLProtocol *negVersion; /* Negotiated protocol version */
642 SSLCipherSuite *negCipher; /* Negotiated cypher */
643 CFArrayRef *peerCerts; /* Certificates */
644
645
646 conn = NULL;
647 error = SSLNewContext(true, &conn);
648 allowExpired = 1;
649 allowAnyRoot = 1;
650
651 if (!error)
652 error = SSLSetIOFuncs(conn, CDSAReadFunc, CDSAWriteFunc);
653
654 if (!error)
655 error = SSLSetProtocolVersion(conn, kSSLProtocol3);
656
657 if (!error)
658 error = SSLSetConnection(conn, (SSLConnectionRef)con->http.fd);
659
660 if (!error)
661 {
662 hostName = ServerName; /* MRS: ??? */
663 error = SSLSetPeerDomainName(conn, hostName, strlen(hostName) + 1);
664 }
665
666 /* have to do these options befor setting server certs */
667 if (!error && allowExpired)
668 error = SSLSetAllowsExpiredCerts(conn, true);
669
670 if (!error && allowAnyRoot)
671 error = SSLSetAllowsAnyRoot(conn, true);
672
673 if (!error && ServerCertificatesArray != NULL)
674 error = SSLSetCertificate(conn, ServerCertificatesArray);
675
676 /*
677 * Perform SSL/TLS handshake
678 */
679
680 do
681 {
682 error = SSLHandshake(conn);
683 }
684 while (error == errSSLWouldBlock);
685
686 if (error)
687 {
688 LogMessage(L_ERROR, "EncryptClient: %d", error);
689
690 con->http.error = error;
691 con->http.status = HTTP_ERROR;
692
693 if (conn != NULL)
694 SSLDisposeContext(conn);
695
696 return (0);
697 }
698
699 LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.",
700 con->http.fd);
701 con->http.tls = conn;
702 return (1);
703
704 #else
705 return (0);
706 #endif /* HAVE_GNUTLS */
707 }
708
709
710 /*
711 * 'IsCGI()' - Is the resource a CGI script/program?
712 */
713
714 int /* O - 1 = CGI, 0 = file */
715 IsCGI(client_t *con, /* I - Client connection */
716 const char *filename, /* I - Real filename */
717 struct stat *filestats, /* I - File information */
718 mime_type_t *type) /* I - MIME type */
719 {
720 const char *options; /* Options on URL */
721
722
723 LogMessage(L_DEBUG2, "IsCGI(con=%p, filename=\"%s\", filestats=%p, type=%s/%s)\n",
724 con, filename, filestats, type ? type->super : "unknown",
725 type ? type->type : "unknown");
726
727 /*
728 * Get the options, if any...
729 */
730
731 if ((options = strchr(con->uri, '?')) != NULL)
732 options ++;
733
734 /*
735 * Check for known types...
736 */
737
738 if (strcasecmp(type->super, "application"))
739 {
740 LogMessage(L_DEBUG2, "IsCGI: Returning 0...");
741 return (0);
742 }
743
744 if (!strcasecmp(type->type, "x-httpd-cgi") &&
745 (filestats->st_mode & 0111))
746 {
747 /*
748 * "application/x-httpd-cgi" is a CGI script.
749 */
750
751 SetString(&con->command, filename);
752
753 filename = strrchr(filename, '/') + 1; /* Filename always absolute */
754
755 if (options)
756 SetStringf(&con->options, "%s %s", filename, options);
757 else
758 SetStringf(&con->options, "%s", filename);
759
760 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
761 con->command, con->options);
762
763 return (1);
764 }
765 #ifdef HAVE_JAVA
766 else if (!strcasecmp(type->type, "x-httpd-java"))
767 {
768 /*
769 * "application/x-httpd-java" is a Java servlet.
770 */
771
772 SetString(&con->command, CUPS_JAVA);
773
774 if (options)
775 SetStringf(&con->options, "java %s %s", filename, options);
776 else
777 SetStringf(&con->options, "java %s", filename);
778
779 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
780 con->command, con->options);
781
782 return (1);
783 }
784 #endif /* HAVE_JAVA */
785 #ifdef HAVE_PERL
786 else if (!strcasecmp(type->type, "x-httpd-perl"))
787 {
788 /*
789 * "application/x-httpd-perl" is a Perl page.
790 */
791
792 SetString(&con->command, CUPS_PERL);
793
794 if (options)
795 SetStringf(&con->options, "perl %s %s", filename, options);
796 else
797 SetStringf(&con->options, "perl %s", filename);
798
799 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
800 con->command, con->options);
801
802 return (1);
803 }
804 #endif /* HAVE_PERL */
805 #ifdef HAVE_PHP
806 else if (!strcasecmp(type->type, "x-httpd-php"))
807 {
808 /*
809 * "application/x-httpd-php" is a PHP page.
810 */
811
812 SetString(&con->command, CUPS_PHP);
813
814 if (options)
815 SetStringf(&con->options, "php %s %s", filename, options);
816 else
817 SetStringf(&con->options, "php %s", filename);
818
819 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
820 con->command, con->options);
821
822 return (1);
823 }
824 #endif /* HAVE_PHP */
825 #ifdef HAVE_PYTHON
826 else if (!strcasecmp(type->type, "x-httpd-python"))
827 {
828 /*
829 * "application/x-httpd-python" is a Python page.
830 */
831
832 SetString(&con->command, CUPS_PYTHON);
833
834 if (options)
835 SetStringf(&con->options, "python %s %s", filename, options);
836 else
837 SetStringf(&con->options, "python %s", filename);
838
839 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
840 con->command, con->options);
841
842 return (1);
843 }
844 #endif /* HAVE_PYTHON */
845
846 LogMessage(L_DEBUG2, "IsCGI: Returning 0...");
847
848 return (0);
849 }
850
851
852 /*
853 * 'ReadClient()' - Read data from a client.
854 */
855
856 int /* O - 1 on success, 0 on error */
857 ReadClient(client_t *con) /* I - Client to read from */
858 {
859 char line[32768], /* Line from client... */
860 operation[64], /* Operation code from socket */
861 version[64], /* HTTP version number string */
862 locale[64], /* Locale */
863 *ptr; /* Pointer into strings */
864 int major, minor; /* HTTP version numbers */
865 http_status_t status; /* Transfer status */
866 ipp_state_t ipp_state; /* State of IPP transfer */
867 int bytes; /* Number of bytes to POST */
868 char *filename; /* Name of file for GET/HEAD */
869 char buf[1024]; /* Buffer for real filename */
870 struct stat filestats; /* File information */
871 mime_type_t *type; /* MIME type of file */
872 printer_t *p; /* Printer */
873 location_t *best; /* Best match for authentication */
874 static unsigned request_id = 0; /* Request ID for temp files */
875
876
877 status = HTTP_CONTINUE;
878
879 LogMessage(L_DEBUG2, "ReadClient() %d, used=%d", con->http.fd,
880 con->http.used);
881
882 if (con->http.error)
883 {
884 CloseClient(con);
885 return (0);
886 }
887
888 switch (con->http.state)
889 {
890 case HTTP_WAITING :
891 /*
892 * See if we've received a request line...
893 */
894
895 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
896 {
897 CloseClient(con);
898 return (0);
899 }
900
901 /*
902 * Ignore blank request lines...
903 */
904
905 if (line[0] == '\0')
906 break;
907
908 /*
909 * Clear other state variables...
910 */
911
912 httpClearFields(HTTP(con));
913
914 con->http.activity = time(NULL);
915 con->http.version = HTTP_1_0;
916 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
917 con->http.data_encoding = HTTP_ENCODE_LENGTH;
918 con->http.data_remaining = 0;
919 con->operation = HTTP_WAITING;
920 con->bytes = 0;
921 con->file = -1;
922 con->file_ready = 0;
923 con->pipe_pid = 0;
924 con->username[0] = '\0';
925 con->password[0] = '\0';
926 con->uri[0] = '\0';
927
928 ClearString(&con->command);
929 ClearString(&con->options);
930
931 if (con->language != NULL)
932 {
933 cupsLangFree(con->language);
934 con->language = NULL;
935 }
936
937 /*
938 * Grab the request line...
939 */
940
941 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
942 {
943 case 1 :
944 LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
945 SendError(con, HTTP_BAD_REQUEST);
946 CloseClient(con);
947 return (0);
948 case 2 :
949 con->http.version = HTTP_0_9;
950 break;
951 case 3 :
952 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
953 {
954 LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
955 SendError(con, HTTP_BAD_REQUEST);
956 CloseClient(con);
957 return (0);
958 }
959
960 if (major < 2)
961 {
962 con->http.version = (http_version_t)(major * 100 + minor);
963 if (con->http.version == HTTP_1_1 && KeepAlive)
964 con->http.keep_alive = HTTP_KEEPALIVE_ON;
965 else
966 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
967 }
968 else
969 {
970 SendError(con, HTTP_NOT_SUPPORTED);
971 CloseClient(con);
972 return (0);
973 }
974 break;
975 }
976
977 /*
978 * Handle full URLs in the request line...
979 */
980
981 if (con->uri[0] != '/' && strcmp(con->uri, "*"))
982 {
983 char method[HTTP_MAX_URI], /* Method/scheme */
984 userpass[HTTP_MAX_URI], /* Username:password */
985 hostname[HTTP_MAX_URI], /* Hostname */
986 resource[HTTP_MAX_URI]; /* Resource path */
987 int port; /* Port number */
988
989
990 /*
991 * Separate the URI into its components...
992 */
993
994 httpSeparate(con->uri, method, userpass, hostname, &port, resource);
995
996 /*
997 * Only allow URIs with the servername, localhost, or an IP
998 * address...
999 */
1000
1001 if (strcasecmp(hostname, ServerName) &&
1002 strcasecmp(hostname, "localhost") &&
1003 !isdigit(hostname[0]))
1004 {
1005 /*
1006 * Nope, we don't do proxies...
1007 */
1008
1009 LogMessage(L_ERROR, "Bad URI \"%s\" in request!", con->uri);
1010 SendError(con, HTTP_METHOD_NOT_ALLOWED);
1011 CloseClient(con);
1012 return (0);
1013 }
1014
1015 /*
1016 * Copy the resource portion back into the URI; both resource and
1017 * con->uri are HTTP_MAX_URI bytes in size...
1018 */
1019
1020 strcpy(con->uri, resource);
1021 }
1022
1023 /*
1024 * Process the request...
1025 */
1026
1027 if (strcmp(operation, "GET") == 0)
1028 con->http.state = HTTP_GET;
1029 else if (strcmp(operation, "PUT") == 0)
1030 con->http.state = HTTP_PUT;
1031 else if (strcmp(operation, "POST") == 0)
1032 con->http.state = HTTP_POST;
1033 else if (strcmp(operation, "DELETE") == 0)
1034 con->http.state = HTTP_DELETE;
1035 else if (strcmp(operation, "TRACE") == 0)
1036 con->http.state = HTTP_TRACE;
1037 else if (strcmp(operation, "OPTIONS") == 0)
1038 con->http.state = HTTP_OPTIONS;
1039 else if (strcmp(operation, "HEAD") == 0)
1040 con->http.state = HTTP_HEAD;
1041 else
1042 {
1043 LogMessage(L_ERROR, "Bad operation \"%s\"!", operation);
1044 SendError(con, HTTP_BAD_REQUEST);
1045 CloseClient(con);
1046 return (0);
1047 }
1048
1049 con->start = time(NULL);
1050 con->operation = con->http.state;
1051
1052 LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
1053 operation, con->uri,
1054 con->http.version / 100, con->http.version % 100);
1055
1056 con->http.status = HTTP_OK;
1057
1058 case HTTP_OPTIONS :
1059 case HTTP_DELETE :
1060 case HTTP_GET :
1061 case HTTP_HEAD :
1062 case HTTP_POST :
1063 case HTTP_PUT :
1064 case HTTP_TRACE :
1065 /*
1066 * Parse incoming parameters until the status changes...
1067 */
1068
1069 status = httpUpdate(HTTP(con));
1070
1071 if (status != HTTP_OK && status != HTTP_CONTINUE)
1072 {
1073 SendError(con, HTTP_BAD_REQUEST);
1074 CloseClient(con);
1075 return (0);
1076 }
1077 break;
1078
1079 default :
1080 break; /* Anti-compiler-warning-code */
1081 }
1082
1083 /*
1084 * Handle new transfers...
1085 */
1086
1087 if (status == HTTP_OK)
1088 {
1089 if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
1090 {
1091 /*
1092 * Figure out the locale from the Accept-Language and Content-Type
1093 * fields...
1094 */
1095
1096 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ',')) != NULL)
1097 *ptr = '\0';
1098
1099 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ';')) != NULL)
1100 *ptr = '\0';
1101
1102 if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "charset=")) != NULL)
1103 {
1104 /*
1105 * Combine language and charset, and trim any extra params in the
1106 * content-type.
1107 */
1108
1109 snprintf(locale, sizeof(locale), "%s.%s",
1110 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8);
1111
1112 if ((ptr = strchr(locale, ',')) != NULL)
1113 *ptr = '\0';
1114 }
1115 else
1116 snprintf(locale, sizeof(locale), "%s.%s",
1117 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], DefaultCharset);
1118
1119 con->language = cupsLangGet(locale);
1120 }
1121 else
1122 con->language = cupsLangGet(DefaultLocale);
1123
1124 decode_auth(con);
1125
1126 if (strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) == 0 &&
1127 KeepAlive)
1128 con->http.keep_alive = HTTP_KEEPALIVE_ON;
1129
1130 if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
1131 con->http.version >= HTTP_1_1)
1132 {
1133 /*
1134 * HTTP/1.1 and higher require the "Host:" field...
1135 */
1136
1137 if (!SendError(con, HTTP_BAD_REQUEST))
1138 {
1139 CloseClient(con);
1140 return (0);
1141 }
1142 }
1143 else if (con->operation == HTTP_OPTIONS)
1144 {
1145 /*
1146 * Do OPTIONS command...
1147 */
1148
1149 if ((best = FindBest(con->uri, con->http.state)) != NULL &&
1150 best->type != AUTH_NONE)
1151 {
1152 if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL))
1153 {
1154 CloseClient(con);
1155 return (0);
1156 }
1157 }
1158
1159 if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
1160 con->http.tls == NULL)
1161 {
1162 #ifdef HAVE_SSL
1163 /*
1164 * Do encryption stuff...
1165 */
1166
1167 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
1168 {
1169 CloseClient(con);
1170 return (0);
1171 }
1172
1173 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1174 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1175 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1176 httpPrintf(HTTP(con), "\r\n");
1177
1178 EncryptClient(con);
1179 #else
1180 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
1181 {
1182 CloseClient(con);
1183 return (0);
1184 }
1185 #endif /* HAVE_SSL */
1186 }
1187
1188 if (con->http.expect)
1189 {
1190 /**** TODO: send expected header ****/
1191 }
1192
1193 if (!SendHeader(con, HTTP_OK, NULL))
1194 {
1195 CloseClient(con);
1196 return (0);
1197 }
1198
1199 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
1200 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1201 httpPrintf(HTTP(con), "\r\n");
1202 }
1203 else if (strstr(con->uri, "..") != NULL)
1204 {
1205 /*
1206 * Protect against malicious users!
1207 */
1208
1209 if (!SendError(con, HTTP_FORBIDDEN))
1210 {
1211 CloseClient(con);
1212 return (0);
1213 }
1214 }
1215 else
1216 {
1217 if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
1218 con->http.tls == NULL)
1219 {
1220 #ifdef HAVE_SSL
1221 /*
1222 * Do encryption stuff...
1223 */
1224
1225 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
1226 {
1227 CloseClient(con);
1228 return (0);
1229 }
1230
1231 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1232 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1233 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1234 httpPrintf(HTTP(con), "\r\n");
1235
1236 EncryptClient(con);
1237
1238 status = IsAuthorized(con);
1239 #else
1240 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
1241 {
1242 CloseClient(con);
1243 return (0);
1244 }
1245 #endif /* HAVE_SSL */
1246 }
1247
1248 if (status != HTTP_OK)
1249 {
1250 LogMessage(L_DEBUG2, "ReadClient: Unauthorized request for %s...\n",
1251 con->uri);
1252 SendError(con, status);
1253 CloseClient(con);
1254 return (0);
1255 }
1256
1257 if (con->http.expect)
1258 {
1259 /**** TODO: send expected header ****/
1260 }
1261
1262 switch (con->http.state)
1263 {
1264 case HTTP_GET_SEND :
1265 if (strncmp(con->uri, "/printers/", 10) == 0 &&
1266 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
1267 {
1268 /*
1269 * Send PPD file - get the real printer name since printer
1270 * names are not case sensitive but filenames can be...
1271 */
1272
1273 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1274
1275 if ((p = FindPrinter(con->uri + 10)) != NULL)
1276 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1277 else
1278 {
1279 if (!SendError(con, HTTP_NOT_FOUND))
1280 {
1281 CloseClient(con);
1282 return (0);
1283 }
1284
1285 break;
1286 }
1287 }
1288
1289 if ((strncmp(con->uri, "/admin", 6) == 0 &&
1290 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
1291 strncmp(con->uri, "/printers", 9) == 0 ||
1292 strncmp(con->uri, "/classes", 8) == 0 ||
1293 strncmp(con->uri, "/jobs", 5) == 0)
1294 {
1295 /*
1296 * Send CGI output...
1297 */
1298
1299 if (strncmp(con->uri, "/admin", 6) == 0)
1300 {
1301 SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
1302 SetString(&con->options, con->uri + 6);
1303 }
1304 else if (strncmp(con->uri, "/printers", 9) == 0)
1305 {
1306 SetStringf(&con->command, "%s/cgi-bin/printers.cgi", ServerBin);
1307 SetString(&con->options, con->uri + 9);
1308 }
1309 else if (strncmp(con->uri, "/classes", 8) == 0)
1310 {
1311 SetStringf(&con->command, "%s/cgi-bin/classes.cgi", ServerBin);
1312 SetString(&con->options, con->uri + 8);
1313 }
1314 else
1315 {
1316 SetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin);
1317 SetString(&con->options, con->uri + 5);
1318 }
1319
1320 if (con->options[0] == '/')
1321 cups_strcpy(con->options, con->options + 1);
1322
1323 if (!SendCommand(con, con->command, con->options))
1324 {
1325 if (!SendError(con, HTTP_NOT_FOUND))
1326 {
1327 CloseClient(con);
1328 return (0);
1329 }
1330 }
1331 else
1332 LogRequest(con, HTTP_OK);
1333
1334 if (con->http.version <= HTTP_1_0)
1335 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1336 }
1337 else if (strncmp(con->uri, "/admin/conf/", 12) == 0 &&
1338 (strchr(con->uri + 12, '/') != NULL ||
1339 strlen(con->uri) == 12))
1340 {
1341 /*
1342 * GET can only be done to configuration files under
1343 * /admin/conf...
1344 */
1345
1346 if (!SendError(con, HTTP_FORBIDDEN))
1347 {
1348 CloseClient(con);
1349 return (0);
1350 }
1351
1352 break;
1353 }
1354 else
1355 {
1356 /*
1357 * Serve a file...
1358 */
1359
1360 if ((filename = get_file(con, &filestats, buf,
1361 sizeof(buf))) == NULL)
1362 {
1363 if (!SendError(con, HTTP_NOT_FOUND))
1364 {
1365 CloseClient(con);
1366 return (0);
1367 }
1368
1369 break;
1370 }
1371
1372 type = mimeFileType(MimeDatabase, filename, NULL);
1373
1374 if (IsCGI(con, filename, &filestats, type))
1375 {
1376 if (!SendCommand(con, con->command, con->options))
1377 {
1378 if (!SendError(con, HTTP_NOT_FOUND))
1379 {
1380 CloseClient(con);
1381 return (0);
1382 }
1383 }
1384 else
1385 LogRequest(con, HTTP_OK);
1386
1387 if (con->http.version <= HTTP_1_0)
1388 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1389 break;
1390 }
1391
1392 if (!check_if_modified(con, &filestats))
1393 {
1394 if (!SendError(con, HTTP_NOT_MODIFIED))
1395 {
1396 CloseClient(con);
1397 return (0);
1398 }
1399 }
1400 else
1401 {
1402 if (type == NULL)
1403 strcpy(line, "text/plain");
1404 else
1405 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1406
1407 if (!SendFile(con, HTTP_OK, filename, line, &filestats))
1408 {
1409 CloseClient(con);
1410 return (0);
1411 }
1412 }
1413 }
1414 break;
1415
1416 case HTTP_POST_RECV :
1417 /*
1418 * See if the POST request includes a Content-Length field, and if
1419 * so check the length against any limits that are set...
1420 */
1421
1422 LogMessage(L_DEBUG2, "POST %s", con->uri);
1423 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1424
1425 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1426 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
1427 MaxRequestSize > 0)
1428 {
1429 /*
1430 * Request too large...
1431 */
1432
1433 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1434 {
1435 CloseClient(con);
1436 return (0);
1437 }
1438
1439 break;
1440 }
1441 else if (atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) < 0)
1442 {
1443 /*
1444 * Negative content lengths are invalid!
1445 */
1446
1447 if (!SendError(con, HTTP_BAD_REQUEST))
1448 {
1449 CloseClient(con);
1450 return (0);
1451 }
1452
1453 break;
1454 }
1455
1456 /*
1457 * See what kind of POST request this is; for IPP requests the
1458 * content-type field will be "application/ipp"...
1459 */
1460
1461 if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
1462 con->request = ippNew();
1463 else if ((strncmp(con->uri, "/admin", 6) == 0 &&
1464 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
1465 strncmp(con->uri, "/printers", 9) == 0 ||
1466 strncmp(con->uri, "/classes", 8) == 0 ||
1467 strncmp(con->uri, "/jobs", 5) == 0)
1468 {
1469 /*
1470 * CGI request...
1471 */
1472
1473 if (strncmp(con->uri, "/admin", 6) == 0)
1474 {
1475 SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
1476 SetString(&con->options, con->uri + 6);
1477 }
1478 else if (strncmp(con->uri, "/printers", 9) == 0)
1479 {
1480 SetStringf(&con->command, "%s/cgi-bin/printers.cgi", ServerBin);
1481 SetString(&con->options, con->uri + 9);
1482 }
1483 else if (strncmp(con->uri, "/classes", 8) == 0)
1484 {
1485 SetStringf(&con->command, "%s/cgi-bin/classes.cgi", ServerBin);
1486 SetString(&con->options, con->uri + 8);
1487 }
1488 else
1489 {
1490 SetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin);
1491 SetString(&con->options, con->uri + 5);
1492 }
1493
1494 if (con->options[0] == '/')
1495 cups_strcpy(con->options, con->options + 1);
1496
1497 LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"",
1498 con->http.fd, con->command, con->options);
1499
1500 if (con->http.version <= HTTP_1_0)
1501 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1502 }
1503 else
1504 {
1505 /*
1506 * POST to a file...
1507 */
1508
1509 if ((filename = get_file(con, &filestats, buf,
1510 sizeof(buf))) == NULL)
1511 {
1512 if (!SendError(con, HTTP_NOT_FOUND))
1513 {
1514 CloseClient(con);
1515 return (0);
1516 }
1517
1518 break;
1519 }
1520
1521 type = mimeFileType(MimeDatabase, filename, NULL);
1522
1523 if (!IsCGI(con, filename, &filestats, type))
1524 {
1525 /*
1526 * Only POST to CGI's...
1527 */
1528
1529 if (!SendError(con, HTTP_UNAUTHORIZED))
1530 {
1531 CloseClient(con);
1532 return (0);
1533 }
1534 }
1535 }
1536 break;
1537
1538 case HTTP_PUT_RECV :
1539 /*
1540 * Validate the resource name...
1541 */
1542
1543 if (strncmp(con->uri, "/admin/conf/", 12) != 0 ||
1544 strchr(con->uri + 12, '/') != NULL ||
1545 strlen(con->uri) == 12)
1546 {
1547 /*
1548 * PUT can only be done to configuration files under
1549 * /admin/conf...
1550 */
1551
1552 if (!SendError(con, HTTP_FORBIDDEN))
1553 {
1554 CloseClient(con);
1555 return (0);
1556 }
1557
1558 break;
1559 }
1560
1561 /*
1562 * See if the PUT request includes a Content-Length field, and if
1563 * so check the length against any limits that are set...
1564 */
1565
1566 LogMessage(L_DEBUG2, "PUT %s", con->uri);
1567 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1568
1569 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1570 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
1571 MaxRequestSize > 0)
1572 {
1573 /*
1574 * Request too large...
1575 */
1576
1577 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1578 {
1579 CloseClient(con);
1580 return (0);
1581 }
1582
1583 break;
1584 }
1585 else if (atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) < 0)
1586 {
1587 /*
1588 * Negative content lengths are invalid!
1589 */
1590
1591 if (!SendError(con, HTTP_BAD_REQUEST))
1592 {
1593 CloseClient(con);
1594 return (0);
1595 }
1596
1597 break;
1598 }
1599
1600 /*
1601 * Open a temporary file to hold the request...
1602 */
1603
1604 SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
1605 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1606 fchmod(con->file, 0640);
1607 fchown(con->file, getuid(), Group);
1608
1609 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
1610 con->filename, con->file);
1611
1612 if (con->file < 0)
1613 {
1614 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1615 {
1616 CloseClient(con);
1617 return (0);
1618 }
1619 }
1620 break;
1621
1622 case HTTP_DELETE :
1623 case HTTP_TRACE :
1624 SendError(con, HTTP_NOT_IMPLEMENTED);
1625 CloseClient(con);
1626 return (0);
1627
1628 case HTTP_HEAD :
1629 if (strncmp(con->uri, "/printers/", 10) == 0 &&
1630 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
1631 {
1632 /*
1633 * Send PPD file - get the real printer name since printer
1634 * names are not case sensitive but filenames can be...
1635 */
1636
1637 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1638
1639 if ((p = FindPrinter(con->uri + 10)) != NULL)
1640 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1641 else
1642 {
1643 if (!SendError(con, HTTP_NOT_FOUND))
1644 {
1645 CloseClient(con);
1646 return (0);
1647 }
1648
1649 break;
1650 }
1651 }
1652
1653 if ((strncmp(con->uri, "/admin/", 7) == 0 &&
1654 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
1655 strncmp(con->uri, "/printers/", 10) == 0 ||
1656 strncmp(con->uri, "/classes/", 9) == 0 ||
1657 strncmp(con->uri, "/jobs/", 6) == 0)
1658 {
1659 /*
1660 * CGI output...
1661 */
1662
1663 if (!SendHeader(con, HTTP_OK, "text/html"))
1664 {
1665 CloseClient(con);
1666 return (0);
1667 }
1668
1669 if (httpPrintf(HTTP(con), "\r\n") < 0)
1670 {
1671 CloseClient(con);
1672 return (0);
1673 }
1674
1675 LogRequest(con, HTTP_OK);
1676 }
1677 else if (strncmp(con->uri, "/admin/conf/", 12) == 0 &&
1678 (strchr(con->uri + 12, '/') != NULL ||
1679 strlen(con->uri) == 12))
1680 {
1681 /*
1682 * HEAD can only be done to configuration files under
1683 * /admin/conf...
1684 */
1685
1686 if (!SendError(con, HTTP_FORBIDDEN))
1687 {
1688 CloseClient(con);
1689 return (0);
1690 }
1691
1692 break;
1693 }
1694 else if ((filename = get_file(con, &filestats, buf,
1695 sizeof(buf))) == NULL)
1696 {
1697 if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
1698 {
1699 CloseClient(con);
1700 return (0);
1701 }
1702
1703 LogRequest(con, HTTP_NOT_FOUND);
1704 }
1705 else if (!check_if_modified(con, &filestats))
1706 {
1707 if (!SendError(con, HTTP_NOT_MODIFIED))
1708 {
1709 CloseClient(con);
1710 return (0);
1711 }
1712
1713 LogRequest(con, HTTP_NOT_MODIFIED);
1714 }
1715 else
1716 {
1717 /*
1718 * Serve a file...
1719 */
1720
1721 type = mimeFileType(MimeDatabase, filename, NULL);
1722 if (type == NULL)
1723 strcpy(line, "text/plain");
1724 else
1725 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1726
1727 if (!SendHeader(con, HTTP_OK, line))
1728 {
1729 CloseClient(con);
1730 return (0);
1731 }
1732
1733 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1734 httpGetDateString(filestats.st_mtime)) < 0)
1735 {
1736 CloseClient(con);
1737 return (0);
1738 }
1739
1740 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1741 (unsigned long)filestats.st_size) < 0)
1742 {
1743 CloseClient(con);
1744 return (0);
1745 }
1746
1747 LogRequest(con, HTTP_OK);
1748 }
1749
1750 if (httpPrintf(HTTP(con), "\r\n") < 0)
1751 {
1752 CloseClient(con);
1753 return (0);
1754 }
1755
1756 con->http.state = HTTP_WAITING;
1757 break;
1758
1759 default :
1760 break; /* Anti-compiler-warning-code */
1761 }
1762 }
1763 }
1764
1765 /*
1766 * Handle any incoming data...
1767 */
1768
1769 switch (con->http.state)
1770 {
1771 case HTTP_PUT_RECV :
1772 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
1773 con->http.fd,
1774 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
1775 con->http.data_remaining, con->file);
1776
1777 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
1778 {
1779 CloseClient(con);
1780 return (0);
1781 }
1782 else if (bytes > 0)
1783 {
1784 con->bytes += bytes;
1785
1786 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
1787 con->http.fd, bytes, con->file);
1788
1789 if (write(con->file, line, bytes) < bytes)
1790 {
1791 LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s",
1792 bytes, con->filename, strerror(errno));
1793
1794 LogMessage(L_DEBUG2, "ReadClient: Closing data file %d...",
1795 con->file);
1796
1797 close(con->file);
1798 con->file = -1;
1799 unlink(con->filename);
1800 ClearString(&con->filename);
1801
1802 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1803 {
1804 CloseClient(con);
1805 return (0);
1806 }
1807 }
1808 }
1809
1810 if (con->http.state == HTTP_WAITING)
1811 {
1812 /*
1813 * End of file, see how big it is...
1814 */
1815
1816 fstat(con->file, &filestats);
1817
1818 LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
1819 con->http.fd, con->file, (int)filestats.st_size);
1820
1821 close(con->file);
1822 con->file = -1;
1823
1824 if (filestats.st_size > MaxRequestSize &&
1825 MaxRequestSize > 0)
1826 {
1827 /*
1828 * Request is too big; remove it and send an error...
1829 */
1830
1831 LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
1832 con->http.fd, con->filename);
1833 unlink(con->filename);
1834 ClearString(&con->filename);
1835
1836 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1837 {
1838 CloseClient(con);
1839 return (0);
1840 }
1841 }
1842
1843 /*
1844 * Install the configuration file...
1845 */
1846
1847 status = install_conf_file(con);
1848
1849 /*
1850 * Return the status to the client...
1851 */
1852
1853 if (!SendError(con, status))
1854 {
1855 CloseClient(con);
1856 return (0);
1857 }
1858 }
1859 break;
1860
1861 case HTTP_POST_RECV :
1862 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
1863 con->http.fd,
1864 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
1865 con->http.data_remaining, con->file);
1866
1867 if (con->request != NULL)
1868 {
1869 /*
1870 * Grab any request data from the connection...
1871 */
1872
1873 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
1874 {
1875 LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!",
1876 con->http.fd);
1877
1878 if (!SendError(con, HTTP_BAD_REQUEST))
1879 {
1880 CloseClient(con);
1881 return (0);
1882 }
1883
1884 CloseClient(con);
1885 return (0);
1886 }
1887 else if (ipp_state != IPP_DATA)
1888 break;
1889 else
1890 con->bytes += ippLength(con->request);
1891 }
1892
1893 if (con->file < 0 && con->http.state != HTTP_POST_SEND)
1894 {
1895 /*
1896 * Create a file as needed for the request data...
1897 */
1898
1899 SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
1900 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1901 fchmod(con->file, 0640);
1902 fchown(con->file, getuid(), Group);
1903
1904 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
1905 con->filename, con->file);
1906
1907 if (con->file < 0)
1908 {
1909 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1910 {
1911 CloseClient(con);
1912 return (0);
1913 }
1914 }
1915 }
1916
1917 if (con->http.state != HTTP_POST_SEND)
1918 {
1919 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
1920 {
1921 CloseClient(con);
1922 return (0);
1923 }
1924 else if (bytes > 0)
1925 {
1926 con->bytes += bytes;
1927
1928 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
1929 con->http.fd, bytes, con->file);
1930
1931 if (write(con->file, line, bytes) < bytes)
1932 {
1933 LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s",
1934 bytes, con->filename, strerror(errno));
1935
1936 LogMessage(L_DEBUG2, "ReadClient: Closing file %d...",
1937 con->file);
1938
1939 close(con->file);
1940 con->file = -1;
1941 unlink(con->filename);
1942 ClearString(&con->filename);
1943
1944 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1945 {
1946 CloseClient(con);
1947 return (0);
1948 }
1949 }
1950 }
1951 else if (con->http.state == HTTP_POST_RECV)
1952 {
1953 return (0);
1954 }
1955 else if (con->http.state != HTTP_POST_SEND)
1956 {
1957 CloseClient(con);
1958 return (0);
1959 }
1960 }
1961
1962 if (con->http.state == HTTP_POST_SEND)
1963 {
1964 if (con->file >= 0)
1965 {
1966 fstat(con->file, &filestats);
1967
1968 LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
1969 con->http.fd, con->file, (int)filestats.st_size);
1970
1971 close(con->file);
1972 con->file = -1;
1973
1974 if (filestats.st_size > MaxRequestSize &&
1975 MaxRequestSize > 0)
1976 {
1977 /*
1978 * Request is too big; remove it and send an error...
1979 */
1980
1981 LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
1982 con->http.fd, con->filename);
1983 unlink(con->filename);
1984 ClearString(&con->filename);
1985
1986 if (con->request)
1987 {
1988 /*
1989 * Delete any IPP request data...
1990 */
1991
1992 ippDelete(con->request);
1993 con->request = NULL;
1994 }
1995
1996 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1997 {
1998 CloseClient(con);
1999 return (0);
2000 }
2001 }
2002
2003 if (con->command)
2004 {
2005 if (!SendCommand(con, con->command, con->options))
2006 {
2007 if (!SendError(con, HTTP_NOT_FOUND))
2008 {
2009 CloseClient(con);
2010 return (0);
2011 }
2012 }
2013 else
2014 LogRequest(con, HTTP_OK);
2015 }
2016 }
2017
2018 if (con->request)
2019 return (ProcessIPPRequest(con));
2020 }
2021 break;
2022
2023 default :
2024 break; /* Anti-compiler-warning-code */
2025 }
2026
2027 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
2028 {
2029 CloseClient(con);
2030 return (0);
2031 }
2032 else
2033 return (1);
2034 }
2035
2036
2037 /*
2038 * 'SendCommand()' - Send output from a command via HTTP.
2039 */
2040
2041 int
2042 SendCommand(client_t *con,
2043 char *command,
2044 char *options)
2045 {
2046 int fd;
2047
2048
2049 if (con->filename)
2050 fd = open(con->filename, O_RDONLY);
2051 else
2052 fd = open("/dev/null", O_RDONLY);
2053
2054 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
2055
2056 close(fd);
2057
2058 LogMessage(L_INFO, "Started \"%s\" (pid=%d)", command, con->pipe_pid);
2059
2060 LogMessage(L_DEBUG, "SendCommand: %d file=%d", con->http.fd, con->file);
2061
2062 if (con->pipe_pid == 0)
2063 return (0);
2064
2065 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2066
2067 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to InputSet...", con->file);
2068 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to OutputSet...",
2069 con->http.fd);
2070
2071 FD_SET(con->file, InputSet);
2072 FD_SET(con->http.fd, OutputSet);
2073
2074 if (!SendHeader(con, HTTP_OK, NULL))
2075 return (0);
2076
2077 if (con->http.version == HTTP_1_1)
2078 {
2079 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
2080
2081 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
2082 return (0);
2083 }
2084
2085 con->file_ready = 0;
2086 con->got_fields = 0;
2087 con->field_col = 0;
2088
2089 return (1);
2090 }
2091
2092
2093 /*
2094 * 'SendError()' - Send an error message via HTTP.
2095 */
2096
2097 int /* O - 1 if successful, 0 otherwise */
2098 SendError(client_t *con, /* I - Connection */
2099 http_status_t code) /* I - Error code */
2100 {
2101 char message[1024]; /* Message for user */
2102
2103
2104 /*
2105 * Put the request in the access_log file...
2106 */
2107
2108 if (con->operation > HTTP_WAITING)
2109 LogRequest(con, code);
2110
2111 LogMessage(L_DEBUG, "SendError() %d code=%d (%s)", con->http.fd, code,
2112 httpStatus(code));
2113
2114 /*
2115 * To work around bugs in some proxies, don't use Keep-Alive for some
2116 * error messages...
2117 */
2118
2119 if (code >= HTTP_BAD_REQUEST)
2120 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
2121
2122 /*
2123 * Send an error message back to the client. If the error code is a
2124 * 400 or 500 series, make sure the message contains some text, too!
2125 */
2126
2127 if (!SendHeader(con, code, NULL))
2128 return (0);
2129
2130 #ifdef HAVE_SSL
2131 if (code == HTTP_UPGRADE_REQUIRED)
2132 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
2133 return (0);
2134
2135 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0)
2136 return (0);
2137 #endif /* HAVE_SSL */
2138
2139 if ((con->http.version >= HTTP_1_1 && !con->http.keep_alive) ||
2140 (code >= HTTP_BAD_REQUEST && code != HTTP_UPGRADE_REQUIRED))
2141 {
2142 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
2143 return (0);
2144 }
2145
2146 if (code >= HTTP_BAD_REQUEST)
2147 {
2148 /*
2149 * Send a human-readable error message.
2150 */
2151
2152 snprintf(message, sizeof(message),
2153 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
2154 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
2155 code, httpStatus(code), httpStatus(code),
2156 con->language ? con->language->messages[code] :
2157 httpStatus(code));
2158
2159 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
2160 return (0);
2161 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
2162 (int)strlen(message)) < 0)
2163 return (0);
2164 if (httpPrintf(HTTP(con), "\r\n") < 0)
2165 return (0);
2166 if (httpPrintf(HTTP(con), "%s", message) < 0)
2167 return (0);
2168 }
2169 else if (httpPrintf(HTTP(con), "\r\n") < 0)
2170 return (0);
2171
2172 con->http.state = HTTP_WAITING;
2173
2174 return (1);
2175 }
2176
2177
2178 /*
2179 * 'SendFile()' - Send a file via HTTP.
2180 */
2181
2182 int
2183 SendFile(client_t *con,
2184 http_status_t code,
2185 char *filename,
2186 char *type,
2187 struct stat *filestats)
2188 {
2189 con->file = open(filename, O_RDONLY);
2190
2191 LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
2192
2193 if (con->file < 0)
2194 return (0);
2195
2196 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2197
2198 con->pipe_pid = 0;
2199
2200 if (!SendHeader(con, code, type))
2201 return (0);
2202
2203 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
2204 return (0);
2205 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
2206 (unsigned long)filestats->st_size) < 0)
2207 return (0);
2208 if (httpPrintf(HTTP(con), "\r\n") < 0)
2209 return (0);
2210
2211 LogMessage(L_DEBUG2, "SendFile: Adding fd %d to OutputSet...", con->http.fd);
2212
2213 FD_SET(con->http.fd, OutputSet);
2214
2215 return (1);
2216 }
2217
2218
2219 /*
2220 * 'SendHeader()' - Send an HTTP request.
2221 */
2222
2223 int /* O - 1 on success, 0 on failure */
2224 SendHeader(client_t *con, /* I - Client to send to */
2225 http_status_t code, /* I - HTTP status code */
2226 char *type) /* I - MIME type of document */
2227 {
2228 location_t *loc; /* Authentication location */
2229
2230
2231 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
2232 con->http.version % 100, code, httpStatus(code)) < 0)
2233 return (0);
2234 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
2235 return (0);
2236 if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
2237 return (0);
2238 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
2239 {
2240 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
2241 return (0);
2242 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
2243 return (0);
2244 }
2245 if (code == HTTP_METHOD_NOT_ALLOWED)
2246 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
2247 return (0);
2248
2249 if (code == HTTP_UNAUTHORIZED)
2250 {
2251 /*
2252 * This already succeeded in IsAuthorized...
2253 */
2254
2255 loc = FindBest(con->uri, con->http.state);
2256
2257 if (loc->type != AUTH_DIGEST)
2258 {
2259 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
2260 return (0);
2261 }
2262 else
2263 {
2264 if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" "
2265 "nonce=\"%s\"\r\n", con->http.hostname) < 0)
2266 return (0);
2267 }
2268 }
2269 if (con->language != NULL)
2270 {
2271 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
2272 con->language->language) < 0)
2273 return (0);
2274
2275 if (type != NULL)
2276 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
2277 cupsLangEncoding(con->language)) < 0)
2278 return (0);
2279 }
2280 else if (type != NULL)
2281 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
2282 return (0);
2283
2284 return (1);
2285 }
2286
2287
2288 /*
2289 * 'UpdateCGI()' - Read status messages from CGI scripts and programs.
2290 */
2291
2292 void
2293 UpdateCGI(void)
2294 {
2295 int bytes; /* Number of bytes read */
2296 char *lineptr, /* Pointer to end of line in buffer */
2297 *message; /* Pointer to message text */
2298 int loglevel; /* Log level for message */
2299 static int bufused = 0; /* Number of bytes used in buffer */
2300 static char buffer[1024]; /* Status buffer */
2301
2302
2303 if ((bytes = read(CGIPipes[0], buffer + bufused,
2304 sizeof(buffer) - bufused - 1)) > 0)
2305 {
2306 bufused += bytes;
2307 buffer[bufused] = '\0';
2308 lineptr = strchr(buffer, '\n');
2309 }
2310 else if (bytes < 0 && errno == EINTR)
2311 return;
2312 else
2313 {
2314 lineptr = buffer + bufused;
2315 lineptr[1] = 0;
2316 }
2317
2318 if (bytes == 0 && bufused == 0)
2319 lineptr = NULL;
2320
2321 while (lineptr != NULL)
2322 {
2323 /*
2324 * Terminate each line and process it...
2325 */
2326
2327 *lineptr++ = '\0';
2328
2329 /*
2330 * Figure out the logging level...
2331 */
2332
2333 if (strncmp(buffer, "EMERG:", 6) == 0)
2334 {
2335 loglevel = L_EMERG;
2336 message = buffer + 6;
2337 }
2338 else if (strncmp(buffer, "ALERT:", 6) == 0)
2339 {
2340 loglevel = L_ALERT;
2341 message = buffer + 6;
2342 }
2343 else if (strncmp(buffer, "CRIT:", 5) == 0)
2344 {
2345 loglevel = L_CRIT;
2346 message = buffer + 5;
2347 }
2348 else if (strncmp(buffer, "ERROR:", 6) == 0)
2349 {
2350 loglevel = L_ERROR;
2351 message = buffer + 6;
2352 }
2353 else if (strncmp(buffer, "WARNING:", 8) == 0)
2354 {
2355 loglevel = L_WARN;
2356 message = buffer + 8;
2357 }
2358 else if (strncmp(buffer, "NOTICE:", 6) == 0)
2359 {
2360 loglevel = L_NOTICE;
2361 message = buffer + 6;
2362 }
2363 else if (strncmp(buffer, "INFO:", 5) == 0)
2364 {
2365 loglevel = L_INFO;
2366 message = buffer + 5;
2367 }
2368 else if (strncmp(buffer, "DEBUG:", 6) == 0)
2369 {
2370 loglevel = L_DEBUG;
2371 message = buffer + 6;
2372 }
2373 else if (strncmp(buffer, "DEBUG2:", 7) == 0)
2374 {
2375 loglevel = L_DEBUG2;
2376 message = buffer + 7;
2377 }
2378 else if (strncmp(buffer, "PAGE:", 5) == 0)
2379 {
2380 loglevel = L_PAGE;
2381 message = buffer + 5;
2382 }
2383 else
2384 {
2385 loglevel = L_DEBUG;
2386 message = buffer;
2387 }
2388
2389 /*
2390 * Skip leading whitespace in the message...
2391 */
2392
2393 while (isspace(*message))
2394 message ++;
2395
2396 LogMessage(loglevel, "[CGI] %s", message);
2397
2398 /*
2399 * Copy over the buffer data we've used up...
2400 */
2401
2402 strcpy(buffer, lineptr);
2403 bufused -= lineptr - buffer;
2404
2405 if (bufused < 0)
2406 bufused = 0;
2407
2408 lineptr = strchr(buffer, '\n');
2409 }
2410
2411 if (bytes <= 0)
2412 {
2413 /*
2414 * Fatal error on pipe - should never happen!
2415 */
2416
2417 LogMessage(L_ERROR, "UpdateCGI: error reading from CGI error pipe - %s",
2418 strerror(errno));
2419 }
2420 }
2421
2422
2423 /*
2424 * 'WriteClient()' - Write data to a client as needed.
2425 */
2426
2427 int /* O - 1 if success, 0 if fail */
2428 WriteClient(client_t *con) /* I - Client connection */
2429 {
2430 int bytes; /* Number of bytes written */
2431 char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */
2432 char *bufptr; /* Pointer into buffer */
2433 ipp_state_t ipp_state; /* IPP state value */
2434
2435
2436 #ifdef DEBUG
2437 LogMessage(L_DEBUG2, "WriteClient(con=%p) %d response=%p, file=%d pipe_pid=%d",
2438 con, con->http.fd, con->response, con->file, con->pipe_pid);
2439 #endif /* DEBUG */
2440
2441 if (con->http.state != HTTP_GET_SEND &&
2442 con->http.state != HTTP_POST_SEND)
2443 return (1);
2444
2445 if (con->response != NULL)
2446 {
2447 ipp_state = ippWrite(&(con->http), con->response);
2448 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
2449 }
2450 else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
2451 {
2452 #ifdef DEBUG
2453 LogMessage(L_DEBUG2, "WriteClient: Read %d bytes from file %d...",
2454 bytes, con->file);
2455 #endif /* DEBUG */
2456
2457 if (con->pipe_pid && !con->got_fields)
2458 {
2459 /*
2460 * Inspect the data for Content-Type and other fields.
2461 */
2462
2463 buf[bytes] = '\0';
2464
2465 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
2466 if (*bufptr == '\n')
2467 {
2468 /*
2469 * Send line to client...
2470 */
2471
2472 if (bufptr > buf && bufptr[-1] == '\r')
2473 bufptr[-1] = '\0';
2474 *bufptr++ = '\0';
2475
2476 httpPrintf(HTTP(con), "%s\r\n", buf);
2477 LogMessage(L_DEBUG2, "WriteClient: %d %s", con->http.fd, buf);
2478
2479 /*
2480 * Update buffer...
2481 */
2482
2483 bytes -= (bufptr - buf);
2484 memmove(buf, bufptr, bytes + 1);
2485 bufptr = buf - 1;
2486
2487 /*
2488 * See if the line was empty...
2489 */
2490
2491 if (con->field_col == 0)
2492 con->got_fields = 1;
2493 else
2494 con->field_col = 0;
2495 }
2496 else if (*bufptr != '\r')
2497 con->field_col ++;
2498
2499 if (bytes > 0 && !con->got_fields)
2500 {
2501 /*
2502 * Remaining text needs to go out...
2503 */
2504
2505 httpPrintf(HTTP(con), "%s", buf);
2506
2507 con->http.activity = time(NULL);
2508 return (1);
2509 }
2510 else if (bytes == 0)
2511 {
2512 con->http.activity = time(NULL);
2513 return (1);
2514 }
2515 }
2516
2517 if (httpWrite(HTTP(con), buf, bytes) < 0)
2518 {
2519 CloseClient(con);
2520 return (0);
2521 }
2522
2523 con->bytes += bytes;
2524 }
2525
2526 if (bytes <= 0)
2527 {
2528 LogRequest(con, HTTP_OK);
2529
2530 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
2531 {
2532 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
2533 {
2534 CloseClient(con);
2535 return (0);
2536 }
2537 }
2538
2539 con->http.state = HTTP_WAITING;
2540
2541 LogMessage(L_DEBUG2, "WriteClient: Removing fd %d from OutputSet...",
2542 con->http.fd);
2543
2544 FD_CLR(con->http.fd, OutputSet);
2545
2546 if (con->file >= 0)
2547 {
2548 if (FD_ISSET(con->file, InputSet))
2549 {
2550 LogMessage(L_DEBUG2, "WriteClient: Removing fd %d from InputSet...",
2551 con->file);
2552 FD_CLR(con->file, InputSet);
2553 }
2554
2555 if (con->pipe_pid)
2556 kill(con->pipe_pid, SIGTERM);
2557
2558 LogMessage(L_DEBUG2, "WriteClient() %d Closing data file %d.",
2559 con->http.fd, con->file);
2560
2561 close(con->file);
2562 con->file = -1;
2563 con->pipe_pid = 0;
2564 }
2565
2566 if (con->filename)
2567 {
2568 LogMessage(L_DEBUG2, "WriteClient() %d Removing temp file %s",
2569 con->http.fd, con->filename);
2570 unlink(con->filename);
2571 ClearString(&con->filename);
2572 }
2573
2574 if (con->request != NULL)
2575 {
2576 ippDelete(con->request);
2577 con->request = NULL;
2578 }
2579
2580 if (con->response != NULL)
2581 {
2582 ippDelete(con->response);
2583 con->response = NULL;
2584 }
2585
2586 ClearString(&con->command);
2587 ClearString(&con->options);
2588
2589 if (!con->http.keep_alive)
2590 {
2591 CloseClient(con);
2592 return (0);
2593 }
2594 }
2595 else
2596 {
2597 con->file_ready = 0;
2598
2599 if (con->pipe_pid && !FD_ISSET(con->file, InputSet))
2600 {
2601 LogMessage(L_DEBUG2, "WriteClient: Adding fd %d to InputSet...", con->file);
2602 FD_SET(con->file, InputSet);
2603 }
2604 }
2605
2606 if (bytes >= 1024)
2607 LogMessage(L_DEBUG2, "WriteClient: %d %d bytes", con->http.fd, bytes);
2608
2609 con->http.activity = time(NULL);
2610
2611 return (1);
2612 }
2613
2614
2615 /*
2616 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
2617 */
2618
2619 static int /* O - 1 if modified since */
2620 check_if_modified(client_t *con, /* I - Client connection */
2621 struct stat *filestats) /* I - File information */
2622 {
2623 char *ptr; /* Pointer into field */
2624 time_t date; /* Time/date value */
2625 int size; /* Size/length value */
2626
2627
2628 size = 0;
2629 date = 0;
2630 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
2631
2632 if (*ptr == '\0')
2633 return (1);
2634
2635 LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"",
2636 con->http.fd, ptr);
2637
2638 while (*ptr != '\0')
2639 {
2640 while (isspace(*ptr) || *ptr == ';')
2641 ptr ++;
2642
2643 if (strncasecmp(ptr, "length=", 7) == 0)
2644 {
2645 ptr += 7;
2646 size = atoi(ptr);
2647
2648 while (isdigit(*ptr))
2649 ptr ++;
2650 }
2651 else if (isalpha(*ptr))
2652 {
2653 date = httpGetDateTime(ptr);
2654 while (*ptr != '\0' && *ptr != ';')
2655 ptr ++;
2656 }
2657 }
2658
2659 LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
2660 con->http.fd, size, (int)filestats->st_size, (int)date,
2661 (int)filestats->st_mtime);
2662
2663 return ((size != filestats->st_size && size != 0) ||
2664 (date < filestats->st_mtime && date != 0) ||
2665 (size == 0 && date == 0));
2666 }
2667
2668
2669 /*
2670 * 'decode_auth()' - Decode an authorization string.
2671 */
2672
2673 static void
2674 decode_auth(client_t *con) /* I - Client to decode to */
2675 {
2676 char *s, /* Authorization string */
2677 value[1024]; /* Value string */
2678 const char *username; /* Certificate username */
2679
2680
2681 /*
2682 * Decode the string...
2683 */
2684
2685 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
2686
2687 LogMessage(L_DEBUG2, "decode_auth(%p): Authorization string = \"%s\"",
2688 con, s);
2689
2690 if (strncmp(s, "Basic", 5) == 0)
2691 {
2692 s += 5;
2693 while (isspace(*s))
2694 s ++;
2695
2696 httpDecode64(value, s);
2697
2698 /*
2699 * Pull the username and password out...
2700 */
2701
2702 if ((s = strchr(value, ':')) == NULL)
2703 {
2704 LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
2705 con->http.fd, value);
2706 return;
2707 }
2708
2709 *s++ = '\0';
2710
2711 strlcpy(con->username, value, sizeof(con->username));
2712 strlcpy(con->password, s, sizeof(con->password));
2713 }
2714 else if (strncmp(s, "Local", 5) == 0)
2715 {
2716 s += 5;
2717 while (isspace(*s))
2718 s ++;
2719
2720 if ((username = FindCert(s)) != NULL)
2721 strlcpy(con->username, username, sizeof(con->username));
2722 }
2723 else if (strncmp(s, "Digest", 5) == 0)
2724 {
2725 /*
2726 * Get the username and password from the Digest attributes...
2727 */
2728
2729 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username",
2730 value))
2731 strlcpy(con->username, value, sizeof(con->username));
2732
2733 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response",
2734 value))
2735 strlcpy(con->password, value, sizeof(con->password) - 1);
2736 }
2737
2738 LogMessage(L_DEBUG2, "decode_auth() %d username=\"%s\"",
2739 con->http.fd, con->username);
2740 }
2741
2742
2743 static char * /* O - Real filename */
2744 get_file(client_t *con, /* I - Client connection */
2745 struct stat *filestats, /* O - File information */
2746 char *filename, /* IO - Filename buffer */
2747 int len) /* I - Buffer length */
2748 {
2749 int status; /* Status of filesystem calls */
2750 char *ptr; /* Pointer info filename */
2751 int plen; /* Remaining length after pointer */
2752
2753
2754 /*
2755 * Need to add DocumentRoot global...
2756 */
2757
2758 if (strncmp(con->uri, "/ppd/", 5) == 0)
2759 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
2760 else if (strncmp(con->uri, "/admin/conf/", 12) == 0)
2761 snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
2762 else if (con->language != NULL)
2763 snprintf(filename, len, "%s/%s%s", DocumentRoot, con->language->language,
2764 con->uri);
2765 else
2766 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
2767
2768 if ((ptr = strchr(filename, '?')) != NULL)
2769 *ptr = '\0';
2770
2771 /*
2772 * Grab the status for this language; if there isn't a language-specific file
2773 * then fallback to the default one...
2774 */
2775
2776 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
2777 {
2778 /*
2779 * Drop the language prefix and try the current directory...
2780 */
2781
2782 if (strncmp(con->uri, "/ppd/", 5) != 0 &&
2783 strncmp(con->uri, "/admin/conf/", 12) != 0)
2784 {
2785 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
2786
2787 if ((ptr = strchr(filename, '?')) != NULL)
2788 *ptr = '\0';
2789
2790 status = stat(filename, filestats);
2791 }
2792 }
2793
2794 /*
2795 * If we're found a directory, get the index.html file instead...
2796 */
2797
2798 if (!status && S_ISDIR(filestats->st_mode))
2799 {
2800 if (filename[strlen(filename) - 1] != '/')
2801 strlcat(filename, "/", len);
2802
2803 ptr = filename + strlen(filename);
2804 plen = len - (ptr - filename);
2805
2806 strlcpy(ptr, "index.html", plen);
2807 status = stat(filename, filestats);
2808
2809 #ifdef HAVE_JAVA
2810 if (status)
2811 {
2812 strlcpy(ptr, "index.class", plen);
2813 status = stat(filename, filestats);
2814 }
2815 #endif /* HAVE_JAVA */
2816
2817 #ifdef HAVE_PERL
2818 if (status)
2819 {
2820 strlcpy(ptr, "index.pl", plen);
2821 status = stat(filename, filestats);
2822 }
2823 #endif /* HAVE_PERL */
2824
2825 #ifdef HAVE_PHP
2826 if (status)
2827 {
2828 strlcpy(ptr, "index.php", plen);
2829 status = stat(filename, filestats);
2830 }
2831 #endif /* HAVE_PHP */
2832
2833 #ifdef HAVE_PYTHON
2834 if (status)
2835 {
2836 strlcpy(ptr, "index.pyc", plen);
2837 status = stat(filename, filestats);
2838 }
2839
2840 if (status)
2841 {
2842 strlcpy(ptr, "index.py", plen);
2843 status = stat(filename, filestats);
2844 }
2845 #endif /* HAVE_PYTHON */
2846 }
2847
2848 LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d",
2849 con->http.fd, filename, status ? -1 : (int)filestats->st_size);
2850
2851 if (status)
2852 return (NULL);
2853 else
2854 return (filename);
2855 }
2856
2857
2858 /*
2859 * 'install_conf_file()' - Install a configuration file.
2860 */
2861
2862 static http_status_t /* O - Status */
2863 install_conf_file(client_t *con) /* I - Connection */
2864 {
2865 cups_file_t *in, /* Input file */
2866 *out; /* Output file */
2867 char buffer[1024]; /* Copy buffer */
2868 int bytes; /* Number of bytes */
2869 char conffile[1024], /* Configuration filename */
2870 newfile[1024], /* New config filename */
2871 oldfile[1024]; /* Old config filename */
2872 struct stat confinfo; /* Config file info */
2873
2874
2875 /*
2876 * First construct the filenames...
2877 */
2878
2879 snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11);
2880 snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11);
2881 snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11);
2882
2883 LogMessage(L_INFO, "Installing config file \"%s\"...", conffile);
2884
2885 /*
2886 * Get the owner, group, and permissions of the configuration file.
2887 * If it doesn't exist, assign it to the User and Group in the
2888 * cupsd.conf file with mode 0640 permissions.
2889 */
2890
2891 if (stat(conffile, &confinfo))
2892 {
2893 confinfo.st_uid = User;
2894 confinfo.st_gid = Group;
2895 confinfo.st_mode = ConfigFilePerm;
2896 }
2897
2898 /*
2899 * Open the request file and new config file...
2900 */
2901
2902 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
2903 {
2904 LogMessage(L_ERROR, "Unable to open request file \"%s\" - %s",
2905 con->filename, strerror(errno));
2906 return (HTTP_SERVER_ERROR);
2907 }
2908
2909 if ((out = cupsFileOpen(newfile, "wb")) == NULL)
2910 {
2911 cupsFileClose(in);
2912 LogMessage(L_ERROR, "Unable to open config file \"%s\" - %s",
2913 newfile, strerror(errno));
2914 return (HTTP_SERVER_ERROR);
2915 }
2916
2917 fchmod(cupsFileNumber(out), confinfo.st_mode);
2918 fchown(cupsFileNumber(out), confinfo.st_uid, confinfo.st_gid);
2919
2920 /*
2921 * Copy from the request to the new config file...
2922 */
2923
2924 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
2925 if (cupsFileWrite(out, buffer, bytes) < bytes)
2926 {
2927 LogMessage(L_ERROR, "Unable to copy to config file \"%s\" - %s",
2928 newfile, strerror(errno));
2929
2930 cupsFileClose(in);
2931 cupsFileClose(out);
2932 unlink(newfile);
2933
2934 return (HTTP_SERVER_ERROR);
2935 }
2936
2937 /*
2938 * Close the files...
2939 */
2940
2941 cupsFileClose(in);
2942 if (cupsFileClose(out))
2943 {
2944 LogMessage(L_ERROR, "Error file closing config file \"%s\" - %s",
2945 newfile, strerror(errno));
2946
2947 unlink(newfile);
2948
2949 return (HTTP_SERVER_ERROR);
2950 }
2951
2952 /*
2953 * Remove the request file...
2954 */
2955
2956 unlink(con->filename);
2957 ClearString(&con->filename);
2958
2959 /*
2960 * Unlink the old backup, rename the current config file to the backup
2961 * filename, and rename the new config file to the config file name...
2962 */
2963
2964 if (unlink(oldfile))
2965 if (errno != ENOENT)
2966 {
2967 LogMessage(L_ERROR, "Unable to remove backup config file \"%s\" - %s",
2968 oldfile, strerror(errno));
2969
2970 unlink(newfile);
2971
2972 return (HTTP_SERVER_ERROR);
2973 }
2974
2975 if (rename(conffile, oldfile))
2976 if (errno != ENOENT)
2977 {
2978 LogMessage(L_ERROR, "Unable to rename old config file \"%s\" - %s",
2979 conffile, strerror(errno));
2980
2981 unlink(newfile);
2982
2983 return (HTTP_SERVER_ERROR);
2984 }
2985
2986 if (rename(newfile, conffile))
2987 {
2988 LogMessage(L_ERROR, "Unable to rename new config file \"%s\" - %s",
2989 newfile, strerror(errno));
2990
2991 rename(oldfile, conffile);
2992 unlink(newfile);
2993
2994 return (HTTP_SERVER_ERROR);
2995 }
2996
2997 /*
2998 * If the cupsd.conf file was updated, set the NeedReload flag...
2999 */
3000
3001 if (strcmp(con->uri, "/admin/conf/cupsd.conf") == 0)
3002 NeedReload = RELOAD_CUPSD;
3003 else
3004 NeedReload = RELOAD_ALL;
3005
3006 ReloadTime = time(NULL);
3007
3008 /*
3009 * Return that the file was created successfully...
3010 */
3011
3012 return (HTTP_CREATED);
3013 }
3014
3015
3016 /*
3017 * 'pipe_command()' - Pipe the output of a command to the remote client.
3018 */
3019
3020 static int /* O - Process ID */
3021 pipe_command(client_t *con, /* I - Client connection */
3022 int infile, /* I - Standard input for command */
3023 int *outfile, /* O - Standard output for command */
3024 char *command, /* I - Command to run */
3025 char *options) /* I - Options for command */
3026 {
3027 int i; /* Looping var */
3028 int pid; /* Process ID */
3029 char *commptr; /* Command string pointer */
3030 char *uriptr; /* URI string pointer */
3031 int fd; /* Looping var */
3032 int fds[2]; /* Pipe FDs */
3033 int argc; /* Number of arguments */
3034 int envc; /* Number of environment variables */
3035 char argbuf[10240], /* Argument buffer */
3036 *argv[100], /* Argument strings */
3037 *envp[100]; /* Environment variables */
3038 char content_length[1024], /* CONTENT_LENGTH environment variable */
3039 content_type[1024], /* CONTENT_TYPE environment variable */
3040 cups_datadir[1024], /* CUPS_DATADIR environment variable */
3041 cups_serverroot[1024], /* CUPS_SERVERROOT environment variable */
3042 http_cookie[1024], /* HTTP_COOKIE environment variable */
3043 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
3044 ipp_port[1024], /* IPP_PORT environment variable */
3045 lang[1024], /* LANG environment variable */
3046 ld_library_path[1024], /* LD_LIBRARY_PATH environment variable */
3047 ld_preload[1024], /* LD_PRELOAD environment variable */
3048 dyld_library_path[1024],/* DYLD_LIBRARY_PATH environment variable */
3049 shlib_path[1024], /* SHLIB_PATH environment variable */
3050 nlspath[1024], /* NLSPATH environment variable */
3051 query_string[10240], /* QUERY_STRING env variable */
3052 remote_addr[1024], /* REMOTE_ADDR environment variable */
3053 remote_host[1024], /* REMOTE_HOST environment variable */
3054 remote_user[1024], /* REMOTE_USER environment variable */
3055 script_name[1024], /* SCRIPT_NAME environment variable */
3056 server_name[1024], /* SERVER_NAME environment variable */
3057 server_port[1024], /* SERVER_PORT environment variable */
3058 tmpdir[1024], /* TMPDIR environment variable */
3059 vg_args[1024], /* VG_ARGS environment variable */
3060 ld_assume_kernel[1024]; /* LD_ASSUME_KERNEL environment variable */
3061 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
3062 struct sigaction action; /* POSIX signal handler */
3063 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
3064 static const char * const locale_encodings[] =
3065 { /* Locale charset names */
3066 "ASCII", "ISO8859-1", "ISO8859-2", "ISO8859-3",
3067 "ISO8859-4", "ISO8859-5", "ISO8859-6", "ISO8859-7",
3068 "ISO8859-8", "ISO8859-9", "ISO8859-10", "UTF-8",
3069 "ISO8859-13", "ISO8859-14", "ISO8859-15", "CP874",
3070 "CP1250", "CP1251", "CP1252", "CP1253",
3071 "CP1254", "CP1255", "CP1256", "CP1257",
3072 "CP1258", "KOI8R", "KOI8U", "ISO8859-11",
3073 "ISO8859-16", "", "", "",
3074
3075 "", "", "", "",
3076 "", "", "", "",
3077 "", "", "", "",
3078 "", "", "", "",
3079 "", "", "", "",
3080 "", "", "", "",
3081 "", "", "", "",
3082 "", "", "", "",
3083
3084 "CP932", "CP936", "CP949", "CP950",
3085 "CP1361", "", "", "",
3086 "", "", "", "",
3087 "", "", "", "",
3088 "", "", "", "",
3089 "", "", "", "",
3090 "", "", "", "",
3091 "", "", "", "",
3092
3093 "", "", "", "",
3094 "", "", "", "",
3095 "", "", "", "",
3096 "", "", "", "",
3097 "", "", "", "",
3098 "", "", "", "",
3099 "", "", "", "",
3100 "", "", "", "",
3101
3102 "EUC-CN", "EUC-JP", "EUC-KR", "EUC-TW"
3103 };
3104
3105
3106 /*
3107 * Copy the command string...
3108 */
3109
3110 strlcpy(argbuf, options, sizeof(argbuf));
3111
3112 /*
3113 * Parse the string; arguments can be separated by + and are terminated
3114 * by ?...
3115 */
3116
3117 argv[0] = argbuf;
3118
3119 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
3120 if (*commptr == ' ' || *commptr == '+')
3121 {
3122 *commptr++ = '\0';
3123
3124 while (*commptr == ' ')
3125 commptr ++;
3126
3127 if (*commptr != '\0')
3128 {
3129 argv[argc] = commptr;
3130 argc ++;
3131 }
3132
3133 commptr --;
3134 }
3135 else if (*commptr == '%')
3136 {
3137 if (commptr[1] >= '0' && commptr[1] <= '9')
3138 *commptr = (commptr[1] - '0') << 4;
3139 else
3140 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
3141
3142 if (commptr[2] >= '0' && commptr[2] <= '9')
3143 *commptr |= commptr[2] - '0';
3144 else
3145 *commptr |= tolower(commptr[2]) - 'a' + 10;
3146
3147 cups_strcpy(commptr + 1, commptr + 3);
3148 }
3149 else if (*commptr == '?')
3150 break;
3151
3152 argv[argc] = NULL;
3153
3154 if (argv[0][0] == '\0')
3155 argv[0] = strrchr(command, '/') + 1;
3156
3157 /*
3158 * Setup the environment variables as needed...
3159 */
3160
3161 if (con->language)
3162 snprintf(lang, sizeof(lang), "LANG=%s.%s", con->language->language,
3163 locale_encodings[con->language->encoding]);
3164 else
3165 strcpy(lang, "LANG=C");
3166
3167 sprintf(ipp_port, "IPP_PORT=%d", LocalPort);
3168 #ifdef AF_INET6
3169 if (con->http.hostaddr.addr.sa_family == AF_INET6)
3170 sprintf(server_port, "SERVER_PORT=%d",
3171 ntohs(con->http.hostaddr.ipv6.sin6_port));
3172 else
3173 #endif /* AF_INET6 */
3174 sprintf(server_port, "SERVER_PORT=%d",
3175 ntohs(con->http.hostaddr.ipv4.sin_port));
3176
3177 if (strcmp(con->http.hostname, "localhost") == 0)
3178 strlcpy(server_name, "SERVER_NAME=localhost", sizeof(server_name));
3179 else
3180 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName);
3181 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname);
3182 strcpy(remote_addr, "REMOTE_ADDR=");
3183 httpAddrString(&(con->http.hostaddr), remote_addr + 12,
3184 sizeof(remote_addr) - 12);
3185 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
3186 snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
3187 snprintf(cups_datadir, sizeof(cups_datadir), "CUPS_DATADIR=%s", DataDir);
3188 snprintf(cups_serverroot, sizeof(cups_serverroot), "CUPS_SERVERROOT=%s", ServerRoot);
3189
3190 envc = 0;
3191
3192 envp[envc ++] = "PATH=/bin:/usr/bin";
3193 envp[envc ++] = "SERVER_SOFTWARE=CUPS/1.1";
3194 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
3195 if (con->http.version == HTTP_1_1)
3196 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
3197 else if (con->http.version == HTTP_1_0)
3198 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
3199 else
3200 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
3201 envp[envc ++] = "REDIRECT_STATUS=1";
3202 envp[envc ++] = "CUPS_SERVER=localhost";
3203 envp[envc ++] = ipp_port;
3204 envp[envc ++] = server_name;
3205 envp[envc ++] = server_port;
3206 envp[envc ++] = remote_addr;
3207 envp[envc ++] = remote_host;
3208 envp[envc ++] = remote_user;
3209 envp[envc ++] = lang;
3210 envp[envc ++] = TZ;
3211 envp[envc ++] = tmpdir;
3212 envp[envc ++] = cups_datadir;
3213 envp[envc ++] = cups_serverroot;
3214
3215 if (getenv("VG_ARGS") != NULL)
3216 {
3217 snprintf(vg_args, sizeof(vg_args), "VG_ARGS=%s", getenv("VG_ARGS"));
3218 envp[envc ++] = vg_args;
3219 }
3220
3221 if (getenv("LD_ASSUME_KERNEL") != NULL)
3222 {
3223 snprintf(ld_assume_kernel, sizeof(ld_assume_kernel), "LD_ASSUME_KERNEL=%s",
3224 getenv("LD_ASSUME_KERNEL"));
3225 envp[envc ++] = ld_assume_kernel;
3226 }
3227
3228 if (getenv("LD_LIBRARY_PATH") != NULL)
3229 {
3230 snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s",
3231 getenv("LD_LIBRARY_PATH"));
3232 envp[envc ++] = ld_library_path;
3233 }
3234
3235 if (getenv("LD_PRELOAD") != NULL)
3236 {
3237 snprintf(ld_preload, sizeof(ld_preload), "LD_PRELOAD=%s",
3238 getenv("LD_PRELOAD"));
3239 envp[envc ++] = ld_preload;
3240 }
3241
3242 if (getenv("DYLD_LIBRARY_PATH") != NULL)
3243 {
3244 snprintf(dyld_library_path, sizeof(dyld_library_path), "DYLD_LIBRARY_PATH=%s",
3245 getenv("DYLD_LIBRARY_PATH"));
3246 envp[envc ++] = dyld_library_path;
3247 }
3248
3249 if (getenv("SHLIB_PATH") != NULL)
3250 {
3251 snprintf(shlib_path, sizeof(shlib_path), "SHLIB_PATH=%s",
3252 getenv("SHLIB_PATH"));
3253 envp[envc ++] = shlib_path;
3254 }
3255
3256 if (getenv("NLSPATH") != NULL)
3257 {
3258 snprintf(nlspath, sizeof(nlspath), "NLSPATH=%s", getenv("NLSPATH"));
3259 envp[envc ++] = nlspath;
3260 }
3261
3262 if (con->http.cookie)
3263 {
3264 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
3265 con->http.cookie);
3266 envp[envc ++] = http_cookie;
3267 }
3268
3269 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
3270 {
3271 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
3272 con->http.fields[HTTP_FIELD_USER_AGENT]);
3273 envp[envc ++] = http_user_agent;
3274 }
3275
3276 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
3277 if ((uriptr = strchr(script_name, '?')) != NULL)
3278 *uriptr = '\0';
3279 envp[envc ++] = script_name;
3280
3281 if (con->operation == HTTP_GET)
3282 {
3283 for (i = 0; i < argc; i ++)
3284 LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
3285 envp[envc ++] = "REQUEST_METHOD=GET";
3286
3287 if (*commptr)
3288 {
3289 /*
3290 * Add GET form variables after ?...
3291 */
3292
3293 *commptr++ = '\0';
3294
3295 snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
3296 envp[envc ++] = query_string;
3297 }
3298 }
3299 else
3300 {
3301 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
3302 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
3303 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
3304
3305 envp[envc ++] = "REQUEST_METHOD=POST";
3306 envp[envc ++] = content_length;
3307 envp[envc ++] = content_type;
3308 }
3309
3310 /*
3311 * Tell the CGI if we are using encryption...
3312 */
3313
3314 if (con->http.encryption == HTTP_ENCRYPT_ALWAYS)
3315 {
3316 envp[envc ++] = "HTTPS=ON";
3317 envp[envc ++] = "CUPS_ENCRYPTION=Always";
3318 }
3319
3320 envp[envc] = NULL;
3321
3322 if (LogLevel == L_DEBUG2)
3323 {
3324 for (i = 0; i < argc; i ++)
3325 LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
3326 for (i = 0; i < envc; i ++)
3327 LogMessage(L_DEBUG2, "envp[%d] = \"%s\"", i, envp[i]);
3328 }
3329
3330 /*
3331 * Create a pipe for the output...
3332 */
3333
3334 if (pipe(fds))
3335 {
3336 LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
3337 argv[0], strerror(errno));
3338 return (0);
3339 }
3340
3341 /*
3342 * Block signals before forking...
3343 */
3344
3345 HoldSignals();
3346
3347 /*
3348 * Then execute the command...
3349 */
3350
3351 if ((pid = fork()) == 0)
3352 {
3353 /*
3354 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
3355 */
3356
3357 if (getuid() == 0)
3358 {
3359 /*
3360 * Running as root, so change to a non-priviledged user...
3361 */
3362
3363 if (setgid(Group))
3364 exit(errno);
3365
3366 if (setgroups(1, &Group))
3367 exit(errno);
3368
3369 if (setuid(User))
3370 exit(errno);
3371 }
3372 else
3373 {
3374 /*
3375 * Reset group membership to just the main one we belong to.
3376 */
3377
3378 setgroups(1, &Group);
3379 }
3380
3381 /*
3382 * Update stdin/stdout/stderr...
3383 */
3384
3385 if (infile)
3386 {
3387 close(0);
3388 if (dup(infile) < 0)
3389 exit(errno);
3390 }
3391
3392 close(1);
3393 if (dup(fds[1]) < 0)
3394 exit(errno);
3395
3396 close(2);
3397 dup(CGIPipes[1]);
3398
3399 /*
3400 * Close extra file descriptors...
3401 */
3402
3403 for (fd = 3; fd < MaxFDs; fd ++)
3404 close(fd);
3405
3406 /*
3407 * Change umask to restrict permissions on created files...
3408 */
3409
3410 umask(077);
3411
3412 /*
3413 * Unblock signals before doing the exec...
3414 */
3415
3416 #ifdef HAVE_SIGSET
3417 sigset(SIGTERM, SIG_DFL);
3418 sigset(SIGCHLD, SIG_DFL);
3419 #elif defined(HAVE_SIGACTION)
3420 memset(&action, 0, sizeof(action));
3421
3422 sigemptyset(&action.sa_mask);
3423 action.sa_handler = SIG_DFL;
3424
3425 sigaction(SIGTERM, &action, NULL);
3426 sigaction(SIGCHLD, &action, NULL);
3427 #else
3428 signal(SIGTERM, SIG_DFL);
3429 signal(SIGCHLD, SIG_DFL);
3430 #endif /* HAVE_SIGSET */
3431
3432 ReleaseSignals();
3433
3434 /*
3435 * Execute the pipe program; if an error occurs, exit with status 1...
3436 */
3437
3438 execve(command, argv, envp);
3439 exit(errno);
3440 return (0);
3441 }
3442 else if (pid < 0)
3443 {
3444 /*
3445 * Error - can't fork!
3446 */
3447
3448 LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
3449 strerror(errno));
3450
3451 close(fds[0]);
3452 close(fds[1]);
3453 pid = 0;
3454 }
3455 else
3456 {
3457 /*
3458 * Fork successful - return the PID...
3459 */
3460
3461 AddCert(pid, con->username);
3462
3463 LogMessage(L_DEBUG, "CGI %s started - PID = %d", command, pid);
3464
3465 *outfile = fds[0];
3466 close(fds[1]);
3467 }
3468
3469 ReleaseSignals();
3470
3471 return (pid);
3472 }
3473
3474
3475 #if defined(HAVE_CDSASSL)
3476 /*
3477 * 'CDSAReadFunc()' - Read function for CDSA decryption code.
3478 */
3479
3480 static OSStatus /* O - -1 on error, 0 on success */
3481 CDSAReadFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
3482 void *data, /* I - Data buffer */
3483 size_t *dataLength) /* IO - Number of bytes */
3484 {
3485 ssize_t bytes; /* Number of bytes read */
3486
3487
3488 bytes = recv((int)connection, data, *dataLength, 0);
3489 if (bytes >= 0)
3490 {
3491 *dataLength = bytes;
3492 return (0);
3493 }
3494 else
3495 return (-1);
3496 }
3497
3498
3499 /*
3500 * 'CDSAWriteFunc()' - Write function for CDSA encryption code.
3501 */
3502
3503 static OSStatus /* O - -1 on error, 0 on success */
3504 CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
3505 const void *data, /* I - Data buffer */
3506 size_t *dataLength) /* IO - Number of bytes */
3507 {
3508 ssize_t bytes;
3509
3510
3511 bytes = write((int)connection, data, *dataLength);
3512 if (bytes >= 0)
3513 {
3514 *dataLength = bytes;
3515 return (0);
3516 }
3517 else
3518 return (-1);
3519 }
3520 #endif /* HAVE_CDSASSL */
3521
3522
3523 /*
3524 * End of "$Id: client.c,v 1.91.2.76 2004/02/25 16:22:01 mike Exp $".
3525 */