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