]> 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.70 2003/09/11 23:01:53 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, User, 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, User, 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 /*
2718 * 'get_file()' - Get a filename and state info.
2719 */
2720
2721 static char * /* O - Real filename */
2722 get_file(client_t *con, /* I - Client connection */
2723 struct stat *filestats,/* O - File information */
2724 char *filename, /* IO - Filename buffer */
2725 int len) /* I - Buffer length */
2726 {
2727 int status; /* Status of filesystem calls */
2728 char *params; /* Pointer to parameters in URI */
2729
2730
2731 /*
2732 * Need to add DocumentRoot global...
2733 */
2734
2735 if (strncmp(con->uri, "/ppd/", 5) == 0)
2736 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
2737 else if (strncmp(con->uri, "/admin/conf/", 12) == 0)
2738 snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
2739 else if (con->language != NULL)
2740 snprintf(filename, len, "%s/%s%s", DocumentRoot, con->language->language,
2741 con->uri);
2742 else
2743 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
2744
2745 if ((params = strchr(filename, '?')) != NULL)
2746 *params = '\0';
2747
2748 /*
2749 * Grab the status for this language; if there isn't a language-specific file
2750 * then fallback to the default one...
2751 */
2752
2753 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
2754 {
2755 /*
2756 * Drop the language prefix and try the current directory...
2757 */
2758
2759 if (strncmp(con->uri, "/ppd/", 5) != 0 &&
2760 strncmp(con->uri, "/admin/conf/", 12) != 0)
2761 {
2762 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
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, "index.html", len);
2776 else
2777 strlcat(filename, "/index.html", len);
2778
2779 status = stat(filename, filestats);
2780 }
2781
2782 LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d",
2783 con->http.fd, filename, status ? -1 : (int)filestats->st_size);
2784
2785 if (status)
2786 return (NULL);
2787 else
2788 return (filename);
2789 }
2790
2791
2792 /*
2793 * 'install_conf_file()' - Install a configuration file.
2794 */
2795
2796 static http_status_t /* O - Status */
2797 install_conf_file(client_t *con) /* I - Connection */
2798 {
2799 cups_file_t *in, /* Input file */
2800 *out; /* Output file */
2801 char buffer[1024]; /* Copy buffer */
2802 int bytes; /* Number of bytes */
2803 char conffile[1024], /* Configuration filename */
2804 newfile[1024], /* New config filename */
2805 oldfile[1024]; /* Old config filename */
2806 struct stat confinfo; /* Config file info */
2807
2808
2809 /*
2810 * First construct the filenames...
2811 */
2812
2813 snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11);
2814 snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11);
2815 snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11);
2816
2817 LogMessage(L_INFO, "Installing config file \"%s\"...", conffile);
2818
2819 /*
2820 * Get the owner, group, and permissions of the configuration file.
2821 * If it doesn't exist, assign it to the User and Group in the
2822 * cupsd.conf file with mode 0640 permissions.
2823 */
2824
2825 if (stat(conffile, &confinfo))
2826 {
2827 confinfo.st_uid = User;
2828 confinfo.st_gid = Group;
2829 confinfo.st_mode = ConfigFilePerm;
2830 }
2831
2832 /*
2833 * Open the request file and new config file...
2834 */
2835
2836 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
2837 {
2838 LogMessage(L_ERROR, "Unable to open request file \"%s\" - %s",
2839 con->filename, strerror(errno));
2840 return (HTTP_SERVER_ERROR);
2841 }
2842
2843 if ((out = cupsFileOpen(newfile, "wb")) == NULL)
2844 {
2845 cupsFileClose(in);
2846 LogMessage(L_ERROR, "Unable to open config file \"%s\" - %s",
2847 newfile, strerror(errno));
2848 return (HTTP_SERVER_ERROR);
2849 }
2850
2851 fchmod(cupsFileNumber(out), confinfo.st_mode);
2852 fchown(cupsFileNumber(out), confinfo.st_uid, confinfo.st_gid);
2853
2854 /*
2855 * Copy from the request to the new config file...
2856 */
2857
2858 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
2859 if (cupsFileWrite(out, buffer, bytes) < bytes)
2860 {
2861 LogMessage(L_ERROR, "Unable to copy to config file \"%s\" - %s",
2862 newfile, strerror(errno));
2863
2864 cupsFileClose(in);
2865 cupsFileClose(out);
2866 unlink(newfile);
2867
2868 return (HTTP_SERVER_ERROR);
2869 }
2870
2871 /*
2872 * Close the files...
2873 */
2874
2875 cupsFileClose(in);
2876 if (cupsFileClose(out))
2877 {
2878 LogMessage(L_ERROR, "Error file closing config file \"%s\" - %s",
2879 newfile, strerror(errno));
2880
2881 unlink(newfile);
2882
2883 return (HTTP_SERVER_ERROR);
2884 }
2885
2886 /*
2887 * Remove the request file...
2888 */
2889
2890 unlink(con->filename);
2891 ClearString(&con->filename);
2892
2893 /*
2894 * Unlink the old backup, rename the current config file to the backup
2895 * filename, and rename the new config file to the config file name...
2896 */
2897
2898 if (unlink(oldfile))
2899 if (errno != ENOENT)
2900 {
2901 LogMessage(L_ERROR, "Unable to remove backup config file \"%s\" - %s",
2902 oldfile, strerror(errno));
2903
2904 unlink(newfile);
2905
2906 return (HTTP_SERVER_ERROR);
2907 }
2908
2909 if (rename(conffile, oldfile))
2910 if (errno != ENOENT)
2911 {
2912 LogMessage(L_ERROR, "Unable to rename old config file \"%s\" - %s",
2913 conffile, strerror(errno));
2914
2915 unlink(newfile);
2916
2917 return (HTTP_SERVER_ERROR);
2918 }
2919
2920 if (rename(newfile, conffile))
2921 {
2922 LogMessage(L_ERROR, "Unable to rename new config file \"%s\" - %s",
2923 newfile, strerror(errno));
2924
2925 rename(oldfile, conffile);
2926 unlink(newfile);
2927
2928 return (HTTP_SERVER_ERROR);
2929 }
2930
2931 /*
2932 * If the cupsd.conf file was updated, set the NeedReload flag...
2933 */
2934
2935 if (strcmp(con->uri, "/admin/conf/cupsd.conf") == 0)
2936 NeedReload = RELOAD_CUPSD;
2937 else
2938 NeedReload = RELOAD_ALL;
2939
2940 ReloadTime = time(NULL);
2941
2942 /*
2943 * Return that the file was created successfully...
2944 */
2945
2946 return (HTTP_CREATED);
2947 }
2948
2949
2950 /*
2951 * 'pipe_command()' - Pipe the output of a command to the remote client.
2952 */
2953
2954 static int /* O - Process ID */
2955 pipe_command(client_t *con, /* I - Client connection */
2956 int infile, /* I - Standard input for command */
2957 int *outfile, /* O - Standard output for command */
2958 char *command, /* I - Command to run */
2959 char *options) /* I - Options for command */
2960 {
2961 int i; /* Looping var */
2962 int pid; /* Process ID */
2963 char *commptr; /* Command string pointer */
2964 char *uriptr; /* URI string pointer */
2965 int fd; /* Looping var */
2966 int fds[2]; /* Pipe FDs */
2967 int argc; /* Number of arguments */
2968 int envc; /* Number of environment variables */
2969 char argbuf[10240], /* Argument buffer */
2970 *argv[100], /* Argument strings */
2971 *envp[100]; /* Environment variables */
2972 char content_length[1024], /* CONTENT_LENGTH environment variable */
2973 content_type[1024], /* CONTENT_TYPE environment variable */
2974 cups_datadir[1024], /* CUPS_DATADIR environment variable */
2975 cups_serverroot[1024], /* CUPS_SERVERROOT environment variable */
2976 http_cookie[1024], /* HTTP_COOKIE environment variable */
2977 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
2978 ipp_port[1024], /* IPP_PORT environment variable */
2979 lang[1024], /* LANG environment variable */
2980 ld_library_path[1024], /* LD_LIBRARY_PATH environment variable */
2981 ld_preload[1024], /* LD_PRELOAD environment variable */
2982 dyld_library_path[1024],/* DYLD_LIBRARY_PATH environment variable */
2983 shlib_path[1024], /* SHLIB_PATH environment variable */
2984 nlspath[1024], /* NLSPATH environment variable */
2985 query_string[10240], /* QUERY_STRING env variable */
2986 remote_addr[1024], /* REMOTE_ADDR environment variable */
2987 remote_host[1024], /* REMOTE_HOST environment variable */
2988 remote_user[1024], /* REMOTE_USER environment variable */
2989 script_name[1024], /* SCRIPT_NAME environment variable */
2990 server_name[1024], /* SERVER_NAME environment variable */
2991 server_port[1024], /* SERVER_PORT environment variable */
2992 tmpdir[1024], /* TMPDIR environment variable */
2993 vg_args[1024], /* VG_ARGS environment variable */
2994 ld_assume_kernel[1024]; /* LD_ASSUME_KERNEL environment variable */
2995 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
2996 struct sigaction action; /* POSIX signal handler */
2997 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
2998 static const char * const locale_encodings[] =
2999 { /* Locale charset names */
3000 "ASCII", "ISO8859-1", "ISO8859-2", "ISO8859-3",
3001 "ISO8859-4", "ISO8859-5", "ISO8859-6", "ISO8859-7",
3002 "ISO8859-8", "ISO8859-9", "ISO8859-10", "UTF-8",
3003 "ISO8859-13", "ISO8859-14", "ISO8859-15", "CP874",
3004 "CP1250", "CP1251", "CP1252", "CP1253",
3005 "CP1254", "CP1255", "CP1256", "CP1257",
3006 "CP1258", "KOI8R", "KOI8U", "ISO8859-11",
3007 "ISO8859-16", "", "", "",
3008
3009 "", "", "", "",
3010 "", "", "", "",
3011 "", "", "", "",
3012 "", "", "", "",
3013 "", "", "", "",
3014 "", "", "", "",
3015 "", "", "", "",
3016 "", "", "", "",
3017
3018 "CP932", "CP936", "CP949", "CP950",
3019 "CP1361", "", "", "",
3020 "", "", "", "",
3021 "", "", "", "",
3022 "", "", "", "",
3023 "", "", "", "",
3024 "", "", "", "",
3025 "", "", "", "",
3026
3027 "", "", "", "",
3028 "", "", "", "",
3029 "", "", "", "",
3030 "", "", "", "",
3031 "", "", "", "",
3032 "", "", "", "",
3033 "", "", "", "",
3034 "", "", "", "",
3035
3036 "EUC-CN", "EUC-JP", "EUC-KR", "EUC-TW"
3037 };
3038
3039
3040 /*
3041 * Copy the command string...
3042 */
3043
3044 strlcpy(argbuf, options, sizeof(argbuf));
3045
3046 /*
3047 * Parse the string; arguments can be separated by + and are terminated
3048 * by ?...
3049 */
3050
3051 argv[0] = argbuf;
3052
3053 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
3054 if (*commptr == ' ' || *commptr == '+')
3055 {
3056 *commptr++ = '\0';
3057
3058 while (*commptr == ' ')
3059 commptr ++;
3060
3061 if (*commptr != '\0')
3062 {
3063 argv[argc] = commptr;
3064 argc ++;
3065 }
3066
3067 commptr --;
3068 }
3069 else if (*commptr == '%')
3070 {
3071 if (commptr[1] >= '0' && commptr[1] <= '9')
3072 *commptr = (commptr[1] - '0') << 4;
3073 else
3074 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
3075
3076 if (commptr[2] >= '0' && commptr[2] <= '9')
3077 *commptr |= commptr[2] - '0';
3078 else
3079 *commptr |= tolower(commptr[2]) - 'a' + 10;
3080
3081 cups_strcpy(commptr + 1, commptr + 3);
3082 }
3083 else if (*commptr == '?')
3084 break;
3085
3086 argv[argc] = NULL;
3087
3088 if (argv[0][0] == '\0')
3089 argv[0] = strrchr(command, '/') + 1;
3090
3091 /*
3092 * Setup the environment variables as needed...
3093 */
3094
3095 if (con->language)
3096 snprintf(lang, sizeof(lang), "LANG=%s.%s", con->language->language,
3097 locale_encodings[con->language->encoding]);
3098 else
3099 strcpy(lang, "LANG=C");
3100
3101 sprintf(ipp_port, "IPP_PORT=%d", LocalPort);
3102 #ifdef AF_INET6
3103 if (con->http.hostaddr.addr.sa_family == AF_INET6)
3104 sprintf(server_port, "SERVER_PORT=%d",
3105 ntohs(con->http.hostaddr.ipv6.sin6_port));
3106 else
3107 #endif /* AF_INET6 */
3108 sprintf(server_port, "SERVER_PORT=%d",
3109 ntohs(con->http.hostaddr.ipv4.sin_port));
3110
3111 if (strcmp(con->http.hostname, "localhost") == 0)
3112 strlcpy(server_name, "SERVER_NAME=localhost", sizeof(server_name));
3113 else
3114 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName);
3115 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname);
3116 strcpy(remote_addr, "REMOTE_ADDR=");
3117 httpAddrString(&(con->http.hostaddr), remote_addr + 12,
3118 sizeof(remote_addr) - 12);
3119 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
3120 snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
3121 snprintf(cups_datadir, sizeof(cups_datadir), "CUPS_DATADIR=%s", DataDir);
3122 snprintf(cups_serverroot, sizeof(cups_serverroot), "CUPS_SERVERROOT=%s", ServerRoot);
3123
3124 envc = 0;
3125
3126 envp[envc ++] = "PATH=/bin:/usr/bin";
3127 envp[envc ++] = "SERVER_SOFTWARE=CUPS/1.1";
3128 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
3129 if (con->http.version == HTTP_1_1)
3130 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
3131 else if (con->http.version == HTTP_1_0)
3132 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
3133 else
3134 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
3135 envp[envc ++] = "REDIRECT_STATUS=1";
3136 envp[envc ++] = "CUPS_SERVER=localhost";
3137 envp[envc ++] = ipp_port;
3138 envp[envc ++] = server_name;
3139 envp[envc ++] = server_port;
3140 envp[envc ++] = remote_addr;
3141 envp[envc ++] = remote_host;
3142 envp[envc ++] = remote_user;
3143 envp[envc ++] = lang;
3144 envp[envc ++] = TZ;
3145 envp[envc ++] = tmpdir;
3146 envp[envc ++] = cups_datadir;
3147 envp[envc ++] = cups_serverroot;
3148
3149 if (getenv("VG_ARGS") != NULL)
3150 {
3151 snprintf(vg_args, sizeof(vg_args), "VG_ARGS=%s", getenv("VG_ARGS"));
3152 envp[envc ++] = vg_args;
3153 }
3154
3155 if (getenv("LD_ASSUME_KERNEL") != NULL)
3156 {
3157 snprintf(ld_assume_kernel, sizeof(ld_assume_kernel), "LD_ASSUME_KERNEL=%s",
3158 getenv("LD_ASSUME_KERNEL"));
3159 envp[envc ++] = ld_assume_kernel;
3160 }
3161
3162 if (getenv("LD_LIBRARY_PATH") != NULL)
3163 {
3164 snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s",
3165 getenv("LD_LIBRARY_PATH"));
3166 envp[envc ++] = ld_library_path;
3167 }
3168
3169 if (getenv("LD_PRELOAD") != NULL)
3170 {
3171 snprintf(ld_preload, sizeof(ld_preload), "LD_PRELOAD=%s",
3172 getenv("LD_PRELOAD"));
3173 envp[envc ++] = ld_preload;
3174 }
3175
3176 if (getenv("DYLD_LIBRARY_PATH") != NULL)
3177 {
3178 snprintf(dyld_library_path, sizeof(dyld_library_path), "DYLD_LIBRARY_PATH=%s",
3179 getenv("DYLD_LIBRARY_PATH"));
3180 envp[envc ++] = dyld_library_path;
3181 }
3182
3183 if (getenv("SHLIB_PATH") != NULL)
3184 {
3185 snprintf(shlib_path, sizeof(shlib_path), "SHLIB_PATH=%s",
3186 getenv("SHLIB_PATH"));
3187 envp[envc ++] = shlib_path;
3188 }
3189
3190 if (getenv("NLSPATH") != NULL)
3191 {
3192 snprintf(nlspath, sizeof(nlspath), "NLSPATH=%s", getenv("NLSPATH"));
3193 envp[envc ++] = nlspath;
3194 }
3195
3196 if (con->http.cookie)
3197 {
3198 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
3199 con->http.cookie);
3200 envp[envc ++] = http_cookie;
3201 }
3202
3203 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
3204 {
3205 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
3206 con->http.fields[HTTP_FIELD_USER_AGENT]);
3207 envp[envc ++] = http_user_agent;
3208 }
3209
3210 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
3211 if ((uriptr = strchr(script_name, '?')) != NULL)
3212 *uriptr = '\0';
3213 envp[envc ++] = script_name;
3214
3215 if (con->operation == HTTP_GET)
3216 {
3217 for (i = 0; i < argc; i ++)
3218 LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
3219 envp[envc ++] = "REQUEST_METHOD=GET";
3220
3221 if (*commptr)
3222 {
3223 /*
3224 * Add GET form variables after ?...
3225 */
3226
3227 *commptr++ = '\0';
3228
3229 snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
3230 envp[envc ++] = query_string;
3231 }
3232 }
3233 else
3234 {
3235 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
3236 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
3237 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
3238
3239 envp[envc ++] = "REQUEST_METHOD=POST";
3240 envp[envc ++] = content_length;
3241 envp[envc ++] = content_type;
3242 }
3243
3244 /*
3245 * Tell the CGI if we are using encryption...
3246 */
3247
3248 if (con->http.encryption == HTTP_ENCRYPT_ALWAYS)
3249 {
3250 envp[envc ++] = "HTTPS=ON";
3251 envp[envc ++] = "CUPS_ENCRYPTION=Always";
3252 }
3253
3254 envp[envc] = NULL;
3255
3256 if (LogLevel == L_DEBUG2)
3257 {
3258 for (i = 0; i < argc; i ++)
3259 LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
3260 for (i = 0; i < envc; i ++)
3261 LogMessage(L_DEBUG2, "envp[%d] = \"%s\"", i, envp[i]);
3262 }
3263
3264 /*
3265 * Create a pipe for the output...
3266 */
3267
3268 if (pipe(fds))
3269 {
3270 LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
3271 argv[0], strerror(errno));
3272 return (0);
3273 }
3274
3275 /*
3276 * Block signals before forking...
3277 */
3278
3279 HoldSignals();
3280
3281 /*
3282 * Then execute the command...
3283 */
3284
3285 if ((pid = fork()) == 0)
3286 {
3287 /*
3288 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
3289 */
3290
3291 if (getuid() == 0)
3292 {
3293 /*
3294 * Running as root, so change to a non-priviledged user...
3295 */
3296
3297 if (setgid(Group))
3298 exit(errno);
3299
3300 if (setgroups(1, &Group))
3301 exit(errno);
3302
3303 if (setuid(User))
3304 exit(errno);
3305 }
3306 else
3307 {
3308 /*
3309 * Reset group membership to just the main one we belong to.
3310 */
3311
3312 setgroups(1, &Group);
3313 }
3314
3315 /*
3316 * Update stdin/stdout/stderr...
3317 */
3318
3319 if (infile)
3320 {
3321 close(0);
3322 if (dup(infile) < 0)
3323 exit(errno);
3324 }
3325
3326 close(1);
3327 if (dup(fds[1]) < 0)
3328 exit(errno);
3329
3330 close(2);
3331 dup(CGIPipes[1]);
3332
3333 /*
3334 * Close extra file descriptors...
3335 */
3336
3337 for (fd = 3; fd < MaxFDs; fd ++)
3338 close(fd);
3339
3340 /*
3341 * Change umask to restrict permissions on created files...
3342 */
3343
3344 umask(077);
3345
3346 /*
3347 * Unblock signals before doing the exec...
3348 */
3349
3350 #ifdef HAVE_SIGSET
3351 sigset(SIGTERM, SIG_DFL);
3352 sigset(SIGCHLD, SIG_DFL);
3353 #elif defined(HAVE_SIGACTION)
3354 memset(&action, 0, sizeof(action));
3355
3356 sigemptyset(&action.sa_mask);
3357 action.sa_handler = SIG_DFL;
3358
3359 sigaction(SIGTERM, &action, NULL);
3360 sigaction(SIGCHLD, &action, NULL);
3361 #else
3362 signal(SIGTERM, SIG_DFL);
3363 signal(SIGCHLD, SIG_DFL);
3364 #endif /* HAVE_SIGSET */
3365
3366 ReleaseSignals();
3367
3368 /*
3369 * Execute the pipe program; if an error occurs, exit with status 1...
3370 */
3371
3372 execve(command, argv, envp);
3373 exit(errno);
3374 return (0);
3375 }
3376 else if (pid < 0)
3377 {
3378 /*
3379 * Error - can't fork!
3380 */
3381
3382 LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
3383 strerror(errno));
3384
3385 close(fds[0]);
3386 close(fds[1]);
3387 pid = 0;
3388 }
3389 else
3390 {
3391 /*
3392 * Fork successful - return the PID...
3393 */
3394
3395 AddCert(pid, con->username);
3396
3397 LogMessage(L_DEBUG, "CGI %s started - PID = %d", command, pid);
3398
3399 *outfile = fds[0];
3400 close(fds[1]);
3401 }
3402
3403 ReleaseSignals();
3404
3405 return (pid);
3406 }
3407
3408
3409 #if defined(HAVE_CDSASSL)
3410 /*
3411 * 'CDSAReadFunc()' - Read function for CDSA decryption code.
3412 */
3413
3414 static OSStatus /* O - -1 on error, 0 on success */
3415 CDSAReadFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
3416 void *data, /* I - Data buffer */
3417 size_t *dataLength) /* IO - Number of bytes */
3418 {
3419 ssize_t bytes; /* Number of bytes read */
3420
3421
3422 bytes = recv((int)connection, data, *dataLength, 0);
3423 if (bytes >= 0)
3424 {
3425 *dataLength = bytes;
3426 return (0);
3427 }
3428 else
3429 return (-1);
3430 }
3431
3432
3433 /*
3434 * 'CDSAWriteFunc()' - Write function for CDSA encryption code.
3435 */
3436
3437 static OSStatus /* O - -1 on error, 0 on success */
3438 CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
3439 const void *data, /* I - Data buffer */
3440 size_t *dataLength) /* IO - Number of bytes */
3441 {
3442 ssize_t bytes;
3443
3444
3445 bytes = write((int)connection, data, *dataLength);
3446 if (bytes >= 0)
3447 {
3448 *dataLength = bytes;
3449 return (0);
3450 }
3451 else
3452 return (-1);
3453 }
3454 #endif /* HAVE_CDSASSL */
3455
3456
3457 /*
3458 * End of "$Id: client.c,v 1.91.2.70 2003/09/11 23:01:53 mike Exp $".
3459 */