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