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