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