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