]> 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.65 2003/07/20 12:51:43 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 int major, minor; /* HTTP version numbers */
833 http_status_t status; /* Transfer status */
834 ipp_state_t ipp_state; /* State of IPP transfer */
835 int bytes; /* Number of bytes to POST */
836 char *filename; /* Name of file for GET/HEAD */
837 char buf[1024]; /* Buffer for real filename */
838 struct stat filestats; /* File information */
839 mime_type_t *type; /* MIME type of file */
840 printer_t *p; /* Printer */
841 location_t *best; /* Best match for authentication */
842 static unsigned request_id = 0;/* Request ID for temp files */
843
844
845 status = HTTP_CONTINUE;
846
847 LogMessage(L_DEBUG2, "ReadClient() %d, used=%d", con->http.fd,
848 con->http.used);
849
850 if (con->http.error)
851 {
852 CloseClient(con);
853 return (0);
854 }
855
856 switch (con->http.state)
857 {
858 case HTTP_WAITING :
859 /*
860 * See if we've received a request line...
861 */
862
863 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
864 {
865 CloseClient(con);
866 return (0);
867 }
868
869 /*
870 * Ignore blank request lines...
871 */
872
873 if (line[0] == '\0')
874 break;
875
876 /*
877 * Clear other state variables...
878 */
879
880 httpClearFields(HTTP(con));
881
882 con->http.activity = time(NULL);
883 con->http.version = HTTP_1_0;
884 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
885 con->http.data_encoding = HTTP_ENCODE_LENGTH;
886 con->http.data_remaining = 0;
887 con->operation = HTTP_WAITING;
888 con->bytes = 0;
889 con->file = 0;
890 con->pipe_pid = 0;
891 con->username[0] = '\0';
892 con->password[0] = '\0';
893 con->uri[0] = '\0';
894
895 ClearString(&con->command);
896 ClearString(&con->options);
897
898 if (con->language != NULL)
899 {
900 cupsLangFree(con->language);
901 con->language = NULL;
902 }
903
904 /*
905 * Grab the request line...
906 */
907
908 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
909 {
910 case 1 :
911 LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
912 SendError(con, HTTP_BAD_REQUEST);
913 ShutdownClient(con);
914 return (1);
915 case 2 :
916 con->http.version = HTTP_0_9;
917 break;
918 case 3 :
919 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
920 {
921 LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
922 SendError(con, HTTP_BAD_REQUEST);
923 ShutdownClient(con);
924 return (1);
925 }
926
927 if (major < 2)
928 {
929 con->http.version = (http_version_t)(major * 100 + minor);
930 if (con->http.version == HTTP_1_1 && KeepAlive)
931 con->http.keep_alive = HTTP_KEEPALIVE_ON;
932 else
933 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
934 }
935 else
936 {
937 SendError(con, HTTP_NOT_SUPPORTED);
938 ShutdownClient(con);
939 return (1);
940 }
941 break;
942 }
943
944 /*
945 * Handle full URLs in the request line...
946 */
947
948 if (con->uri[0] != '/' && strcmp(con->uri, "*"))
949 {
950 char method[HTTP_MAX_URI], /* Method/scheme */
951 userpass[HTTP_MAX_URI], /* Username:password */
952 hostname[HTTP_MAX_URI], /* Hostname */
953 resource[HTTP_MAX_URI]; /* Resource path */
954 int port; /* Port number */
955
956
957 /*
958 * Separate the URI into its components...
959 */
960
961 httpSeparate(con->uri, method, userpass, hostname, &port, resource);
962
963 /*
964 * Only allow URIs with the servername, localhost, or an IP
965 * address...
966 */
967
968 if (strcasecmp(hostname, ServerName) &&
969 strcasecmp(hostname, "localhost") &&
970 !isdigit(hostname[0]))
971 {
972 /*
973 * Nope, we don't do proxies...
974 */
975
976 LogMessage(L_ERROR, "Bad URI \"%s\" in request!", con->uri);
977 SendError(con, HTTP_METHOD_NOT_ALLOWED);
978 ShutdownClient(con);
979 return (1);
980 }
981
982 /*
983 * Copy the resource portion back into the URI; both resource and
984 * con->uri are HTTP_MAX_URI bytes in size...
985 */
986
987 strcpy(con->uri, resource);
988 }
989
990 /*
991 * Process the request...
992 */
993
994 if (strcmp(operation, "GET") == 0)
995 con->http.state = HTTP_GET;
996 else if (strcmp(operation, "PUT") == 0)
997 con->http.state = HTTP_PUT;
998 else if (strcmp(operation, "POST") == 0)
999 con->http.state = HTTP_POST;
1000 else if (strcmp(operation, "DELETE") == 0)
1001 con->http.state = HTTP_DELETE;
1002 else if (strcmp(operation, "TRACE") == 0)
1003 con->http.state = HTTP_TRACE;
1004 else if (strcmp(operation, "OPTIONS") == 0)
1005 con->http.state = HTTP_OPTIONS;
1006 else if (strcmp(operation, "HEAD") == 0)
1007 con->http.state = HTTP_HEAD;
1008 else
1009 {
1010 LogMessage(L_ERROR, "Bad operation \"%s\"!", operation);
1011 SendError(con, HTTP_BAD_REQUEST);
1012 ShutdownClient(con);
1013 return (1);
1014 }
1015
1016 con->start = time(NULL);
1017 con->operation = con->http.state;
1018
1019 LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
1020 operation, con->uri,
1021 con->http.version / 100, con->http.version % 100);
1022
1023 con->http.status = HTTP_OK;
1024
1025 case HTTP_OPTIONS :
1026 case HTTP_DELETE :
1027 case HTTP_GET :
1028 case HTTP_HEAD :
1029 case HTTP_POST :
1030 case HTTP_PUT :
1031 case HTTP_TRACE :
1032 /*
1033 * Parse incoming parameters until the status changes...
1034 */
1035
1036 status = httpUpdate(HTTP(con));
1037
1038 if (status != HTTP_OK && status != HTTP_CONTINUE)
1039 {
1040 SendError(con, HTTP_BAD_REQUEST);
1041 ShutdownClient(con);
1042 return (1);
1043 }
1044 break;
1045
1046 default :
1047 break; /* Anti-compiler-warning-code */
1048 }
1049
1050 /*
1051 * Handle new transfers...
1052 */
1053
1054 if (status == HTTP_OK)
1055 {
1056 if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
1057 con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
1058 else
1059 con->language = cupsLangGet(DefaultLanguage);
1060
1061 decode_auth(con);
1062
1063 if (strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) == 0 &&
1064 KeepAlive)
1065 con->http.keep_alive = HTTP_KEEPALIVE_ON;
1066
1067 if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
1068 con->http.version >= HTTP_1_1)
1069 {
1070 /*
1071 * HTTP/1.1 and higher require the "Host:" field...
1072 */
1073
1074 if (!SendError(con, HTTP_BAD_REQUEST))
1075 {
1076 CloseClient(con);
1077 return (0);
1078 }
1079 }
1080 else if (con->operation == HTTP_OPTIONS)
1081 {
1082 /*
1083 * Do OPTIONS command...
1084 */
1085
1086 if ((best = FindBest(con->uri, con->http.state)) != NULL &&
1087 best->type != AUTH_NONE)
1088 {
1089 if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL))
1090 {
1091 CloseClient(con);
1092 return (0);
1093 }
1094 }
1095
1096 if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
1097 con->http.tls == NULL)
1098 {
1099 #ifdef HAVE_SSL
1100 /*
1101 * Do encryption stuff...
1102 */
1103
1104 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
1105 {
1106 CloseClient(con);
1107 return (0);
1108 }
1109
1110 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1111 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1112 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1113 httpPrintf(HTTP(con), "\r\n");
1114
1115 EncryptClient(con);
1116 #else
1117 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
1118 {
1119 CloseClient(con);
1120 return (0);
1121 }
1122 #endif /* HAVE_SSL */
1123 }
1124
1125 if (con->http.expect)
1126 {
1127 /**** TODO: send expected header ****/
1128 }
1129
1130 if (!SendHeader(con, HTTP_OK, NULL))
1131 {
1132 CloseClient(con);
1133 return (0);
1134 }
1135
1136 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
1137 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1138 httpPrintf(HTTP(con), "\r\n");
1139 }
1140 else if (strstr(con->uri, "..") != NULL)
1141 {
1142 /*
1143 * Protect against malicious users!
1144 */
1145
1146 if (!SendError(con, HTTP_FORBIDDEN))
1147 {
1148 CloseClient(con);
1149 return (0);
1150 }
1151 }
1152 else
1153 {
1154 if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
1155 con->http.tls == NULL)
1156 {
1157 #ifdef HAVE_SSL
1158 /*
1159 * Do encryption stuff...
1160 */
1161
1162 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
1163 {
1164 CloseClient(con);
1165 return (0);
1166 }
1167
1168 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1169 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1170 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1171 httpPrintf(HTTP(con), "\r\n");
1172
1173 EncryptClient(con);
1174
1175 status = IsAuthorized(con);
1176 #else
1177 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
1178 {
1179 CloseClient(con);
1180 return (0);
1181 }
1182 #endif /* HAVE_SSL */
1183 }
1184
1185 if (status != HTTP_OK)
1186 {
1187 LogMessage(L_DEBUG2, "ReadClient: Unauthorized request for %s...\n",
1188 con->uri);
1189 SendError(con, status);
1190 ShutdownClient(con);
1191 return (1);
1192 }
1193
1194 if (con->http.expect)
1195 {
1196 /**** TODO: send expected header ****/
1197 }
1198
1199 switch (con->http.state)
1200 {
1201 case HTTP_GET_SEND :
1202 if (strncmp(con->uri, "/printers/", 10) == 0 &&
1203 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
1204 {
1205 /*
1206 * Send PPD file - get the real printer name since printer
1207 * names are not case sensitive but filenames can be...
1208 */
1209
1210 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1211
1212 if ((p = FindPrinter(con->uri + 10)) != NULL)
1213 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1214 else
1215 {
1216 if (!SendError(con, HTTP_NOT_FOUND))
1217 {
1218 CloseClient(con);
1219 return (0);
1220 }
1221
1222 break;
1223 }
1224 }
1225
1226 if ((strncmp(con->uri, "/admin", 6) == 0 &&
1227 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
1228 strncmp(con->uri, "/printers", 9) == 0 ||
1229 strncmp(con->uri, "/classes", 8) == 0 ||
1230 strncmp(con->uri, "/jobs", 5) == 0)
1231 {
1232 /*
1233 * Send CGI output...
1234 */
1235
1236 if (strncmp(con->uri, "/admin", 6) == 0)
1237 {
1238 SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
1239 SetString(&con->options, con->uri + 6);
1240 }
1241 else if (strncmp(con->uri, "/printers", 9) == 0)
1242 {
1243 SetStringf(&con->command, "%s/cgi-bin/printers.cgi", ServerBin);
1244 SetString(&con->options, con->uri + 9);
1245 }
1246 else if (strncmp(con->uri, "/classes", 8) == 0)
1247 {
1248 SetStringf(&con->command, "%s/cgi-bin/classes.cgi", ServerBin);
1249 SetString(&con->options, con->uri + 8);
1250 }
1251 else
1252 {
1253 SetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin);
1254 SetString(&con->options, con->uri + 5);
1255 }
1256
1257 if (con->options[0] == '/')
1258 cups_strcpy(con->options, con->options + 1);
1259
1260 if (!SendCommand(con, con->command, con->options))
1261 {
1262 if (!SendError(con, HTTP_NOT_FOUND))
1263 {
1264 CloseClient(con);
1265 return (0);
1266 }
1267 }
1268 else
1269 LogRequest(con, HTTP_OK);
1270
1271 if (con->http.version <= HTTP_1_0)
1272 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1273 }
1274 else if (strncmp(con->uri, "/admin/conf/", 12) == 0 &&
1275 (strchr(con->uri + 12, '/') != NULL ||
1276 strlen(con->uri) == 12))
1277 {
1278 /*
1279 * GET can only be done to configuration files under
1280 * /admin/conf...
1281 */
1282
1283 if (!SendError(con, HTTP_FORBIDDEN))
1284 {
1285 CloseClient(con);
1286 return (0);
1287 }
1288
1289 break;
1290 }
1291 else
1292 {
1293 /*
1294 * Serve a file...
1295 */
1296
1297 if ((filename = get_file(con, &filestats, buf,
1298 sizeof(buf))) == NULL)
1299 {
1300 if (!SendError(con, HTTP_NOT_FOUND))
1301 {
1302 CloseClient(con);
1303 return (0);
1304 }
1305
1306 break;
1307 }
1308
1309 type = mimeFileType(MimeDatabase, filename, NULL);
1310
1311 if (IsCGI(con, filename, &filestats, type))
1312 {
1313 if (!SendCommand(con, con->command, con->options))
1314 {
1315 if (!SendError(con, HTTP_NOT_FOUND))
1316 {
1317 CloseClient(con);
1318 return (0);
1319 }
1320 }
1321 else
1322 LogRequest(con, HTTP_OK);
1323
1324 if (con->http.version <= HTTP_1_0)
1325 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1326 break;
1327 }
1328
1329 if (!check_if_modified(con, &filestats))
1330 {
1331 if (!SendError(con, HTTP_NOT_MODIFIED))
1332 {
1333 CloseClient(con);
1334 return (0);
1335 }
1336 }
1337 else
1338 {
1339 if (type == NULL)
1340 strcpy(line, "text/plain");
1341 else
1342 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1343
1344 if (!SendFile(con, HTTP_OK, filename, line, &filestats))
1345 {
1346 CloseClient(con);
1347 return (0);
1348 }
1349 }
1350 }
1351 break;
1352
1353 case HTTP_POST_RECV :
1354 /*
1355 * See if the POST request includes a Content-Length field, and if
1356 * so check the length against any limits that are set...
1357 */
1358
1359 LogMessage(L_DEBUG2, "POST %s", con->uri);
1360 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1361
1362 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1363 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
1364 MaxRequestSize > 0)
1365 {
1366 /*
1367 * Request too large...
1368 */
1369
1370 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1371 {
1372 CloseClient(con);
1373 return (0);
1374 }
1375
1376 break;
1377 }
1378 else if (atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) < 0)
1379 {
1380 /*
1381 * Negative content lengths are invalid!
1382 */
1383
1384 if (!SendError(con, HTTP_BAD_REQUEST))
1385 {
1386 CloseClient(con);
1387 return (0);
1388 }
1389
1390 break;
1391 }
1392
1393 /*
1394 * See what kind of POST request this is; for IPP requests the
1395 * content-type field will be "application/ipp"...
1396 */
1397
1398 if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
1399 con->request = ippNew();
1400 else if ((strncmp(con->uri, "/admin", 6) == 0 &&
1401 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
1402 strncmp(con->uri, "/printers", 9) == 0 ||
1403 strncmp(con->uri, "/classes", 8) == 0 ||
1404 strncmp(con->uri, "/jobs", 5) == 0)
1405 {
1406 /*
1407 * CGI request...
1408 */
1409
1410 if (strncmp(con->uri, "/admin", 6) == 0)
1411 {
1412 SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
1413 SetString(&con->options, con->uri + 6);
1414 }
1415 else if (strncmp(con->uri, "/printers", 9) == 0)
1416 {
1417 SetStringf(&con->command, "%s/cgi-bin/printers.cgi", ServerBin);
1418 SetString(&con->options, con->uri + 9);
1419 }
1420 else if (strncmp(con->uri, "/classes", 8) == 0)
1421 {
1422 SetStringf(&con->command, "%s/cgi-bin/classes.cgi", ServerBin);
1423 SetString(&con->options, con->uri + 8);
1424 }
1425 else
1426 {
1427 SetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin);
1428 SetString(&con->options, con->uri + 5);
1429 }
1430
1431 if (con->options[0] == '/')
1432 cups_strcpy(con->options, con->options + 1);
1433
1434 LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"",
1435 con->http.fd, con->command, con->options);
1436
1437 if (con->http.version <= HTTP_1_0)
1438 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1439 }
1440 else
1441 {
1442 /*
1443 * POST to a file...
1444 */
1445
1446 if ((filename = get_file(con, &filestats, buf,
1447 sizeof(buf))) == NULL)
1448 {
1449 if (!SendError(con, HTTP_NOT_FOUND))
1450 {
1451 CloseClient(con);
1452 return (0);
1453 }
1454
1455 break;
1456 }
1457
1458 type = mimeFileType(MimeDatabase, filename, NULL);
1459
1460 if (!IsCGI(con, filename, &filestats, type))
1461 {
1462 /*
1463 * Only POST to CGI's...
1464 */
1465
1466 if (!SendError(con, HTTP_UNAUTHORIZED))
1467 {
1468 CloseClient(con);
1469 return (0);
1470 }
1471 }
1472 }
1473 break;
1474
1475 case HTTP_PUT_RECV :
1476 /*
1477 * Validate the resource name...
1478 */
1479
1480 if (strncmp(con->uri, "/admin/conf/", 12) != 0 ||
1481 strchr(con->uri + 12, '/') != NULL ||
1482 strlen(con->uri) == 12)
1483 {
1484 /*
1485 * PUT can only be done to configuration files under
1486 * /admin/conf...
1487 */
1488
1489 if (!SendError(con, HTTP_FORBIDDEN))
1490 {
1491 CloseClient(con);
1492 return (0);
1493 }
1494
1495 break;
1496 }
1497
1498 /*
1499 * See if the PUT request includes a Content-Length field, and if
1500 * so check the length against any limits that are set...
1501 */
1502
1503 LogMessage(L_DEBUG2, "PUT %s", con->uri);
1504 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1505
1506 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1507 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
1508 MaxRequestSize > 0)
1509 {
1510 /*
1511 * Request too large...
1512 */
1513
1514 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1515 {
1516 CloseClient(con);
1517 return (0);
1518 }
1519
1520 break;
1521 }
1522
1523 /*
1524 * Open a temporary file to hold the request...
1525 */
1526
1527 SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
1528 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1529 fchmod(con->file, 0640);
1530 fchown(con->file, User, Group);
1531
1532 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
1533 con->filename, con->file);
1534
1535 if (con->file < 0)
1536 {
1537 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1538 {
1539 CloseClient(con);
1540 return (0);
1541 }
1542 }
1543 break;
1544
1545 case HTTP_DELETE :
1546 case HTTP_TRACE :
1547 SendError(con, HTTP_NOT_IMPLEMENTED);
1548 ShutdownClient(con);
1549 return (1);
1550
1551 case HTTP_HEAD :
1552 if (strncmp(con->uri, "/printers/", 10) == 0 &&
1553 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
1554 {
1555 /*
1556 * Send PPD file - get the real printer name since printer
1557 * names are not case sensitive but filenames can be...
1558 */
1559
1560 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1561
1562 if ((p = FindPrinter(con->uri + 10)) != NULL)
1563 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1564 else
1565 {
1566 if (!SendError(con, HTTP_NOT_FOUND))
1567 {
1568 CloseClient(con);
1569 return (0);
1570 }
1571
1572 break;
1573 }
1574 }
1575
1576 if ((strncmp(con->uri, "/admin/", 7) == 0 &&
1577 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
1578 strncmp(con->uri, "/printers/", 10) == 0 ||
1579 strncmp(con->uri, "/classes/", 9) == 0 ||
1580 strncmp(con->uri, "/jobs/", 6) == 0)
1581 {
1582 /*
1583 * CGI output...
1584 */
1585
1586 if (!SendHeader(con, HTTP_OK, "text/html"))
1587 {
1588 CloseClient(con);
1589 return (0);
1590 }
1591
1592 if (httpPrintf(HTTP(con), "\r\n") < 0)
1593 {
1594 CloseClient(con);
1595 return (0);
1596 }
1597
1598 LogRequest(con, HTTP_OK);
1599 }
1600 else if (strncmp(con->uri, "/admin/conf/", 12) == 0 &&
1601 (strchr(con->uri + 12, '/') != NULL ||
1602 strlen(con->uri) == 12))
1603 {
1604 /*
1605 * HEAD can only be done to configuration files under
1606 * /admin/conf...
1607 */
1608
1609 if (!SendError(con, HTTP_FORBIDDEN))
1610 {
1611 CloseClient(con);
1612 return (0);
1613 }
1614
1615 break;
1616 }
1617 else if ((filename = get_file(con, &filestats, buf,
1618 sizeof(buf))) == NULL)
1619 {
1620 if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
1621 {
1622 CloseClient(con);
1623 return (0);
1624 }
1625
1626 LogRequest(con, HTTP_NOT_FOUND);
1627 }
1628 else if (!check_if_modified(con, &filestats))
1629 {
1630 if (!SendError(con, HTTP_NOT_MODIFIED))
1631 {
1632 CloseClient(con);
1633 return (0);
1634 }
1635
1636 LogRequest(con, HTTP_NOT_MODIFIED);
1637 }
1638 else
1639 {
1640 /*
1641 * Serve a file...
1642 */
1643
1644 type = mimeFileType(MimeDatabase, filename, NULL);
1645 if (type == NULL)
1646 strcpy(line, "text/plain");
1647 else
1648 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1649
1650 if (!SendHeader(con, HTTP_OK, line))
1651 {
1652 CloseClient(con);
1653 return (0);
1654 }
1655
1656 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1657 httpGetDateString(filestats.st_mtime)) < 0)
1658 {
1659 CloseClient(con);
1660 return (0);
1661 }
1662
1663 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1664 (unsigned long)filestats.st_size) < 0)
1665 {
1666 CloseClient(con);
1667 return (0);
1668 }
1669
1670 LogRequest(con, HTTP_OK);
1671 }
1672
1673 if (httpPrintf(HTTP(con), "\r\n") < 0)
1674 {
1675 CloseClient(con);
1676 return (0);
1677 }
1678
1679 con->http.state = HTTP_WAITING;
1680 break;
1681
1682 default :
1683 break; /* Anti-compiler-warning-code */
1684 }
1685 }
1686 }
1687
1688 /*
1689 * Handle any incoming data...
1690 */
1691
1692 switch (con->http.state)
1693 {
1694 case HTTP_PUT_RECV :
1695 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
1696 con->http.fd,
1697 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
1698 con->http.data_remaining, con->file);
1699
1700 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
1701 {
1702 CloseClient(con);
1703 return (0);
1704 }
1705 else if (bytes > 0)
1706 {
1707 con->bytes += bytes;
1708
1709 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
1710 con->http.fd, bytes, con->file);
1711
1712 if (write(con->file, line, bytes) < bytes)
1713 {
1714 LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s",
1715 bytes, con->filename, strerror(errno));
1716
1717 LogMessage(L_DEBUG2, "ReadClient: Closing data file %d...",
1718 con->file);
1719
1720 close(con->file);
1721 con->file = 0;
1722 unlink(con->filename);
1723 ClearString(&con->filename);
1724
1725 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1726 {
1727 CloseClient(con);
1728 return (0);
1729 }
1730 }
1731 }
1732
1733 if (con->http.state == HTTP_WAITING)
1734 {
1735 /*
1736 * End of file, see how big it is...
1737 */
1738
1739 fstat(con->file, &filestats);
1740
1741 LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
1742 con->http.fd, con->file, (int)filestats.st_size);
1743
1744 close(con->file);
1745 con->file = 0;
1746
1747 if (filestats.st_size > MaxRequestSize &&
1748 MaxRequestSize > 0)
1749 {
1750 /*
1751 * Request is too big; remove it and send an error...
1752 */
1753
1754 LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
1755 con->http.fd, con->filename);
1756 unlink(con->filename);
1757 ClearString(&con->filename);
1758
1759 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1760 {
1761 CloseClient(con);
1762 return (0);
1763 }
1764 }
1765
1766 /*
1767 * Install the configuration file...
1768 */
1769
1770 status = install_conf_file(con);
1771
1772 /*
1773 * Return the status to the client...
1774 */
1775
1776 if (!SendError(con, status))
1777 {
1778 CloseClient(con);
1779 return (0);
1780 }
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 if (!SendError(con, HTTP_BAD_REQUEST))
1802 {
1803 CloseClient(con);
1804 return (0);
1805 }
1806
1807 ShutdownClient(con);
1808 return (1);
1809 }
1810 else if (ipp_state != IPP_DATA)
1811 break;
1812 else
1813 con->bytes += ippLength(con->request);
1814 }
1815
1816 if (con->file == 0 && con->http.state != HTTP_POST_SEND)
1817 {
1818 /*
1819 * Create a file as needed for the request data...
1820 */
1821
1822 SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
1823 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1824 fchmod(con->file, 0640);
1825 fchown(con->file, User, Group);
1826
1827 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
1828 con->filename, con->file);
1829
1830 if (con->file < 0)
1831 {
1832 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1833 {
1834 CloseClient(con);
1835 return (0);
1836 }
1837 }
1838 }
1839
1840 if (con->http.state != HTTP_POST_SEND)
1841 {
1842 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
1843 {
1844 CloseClient(con);
1845 return (0);
1846 }
1847 else if (bytes > 0)
1848 {
1849 con->bytes += bytes;
1850
1851 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
1852 con->http.fd, bytes, con->file);
1853
1854 if (write(con->file, line, bytes) < bytes)
1855 {
1856 LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s",
1857 bytes, con->filename, strerror(errno));
1858
1859 LogMessage(L_DEBUG2, "ReadClient: Closing file %d...",
1860 con->file);
1861
1862 close(con->file);
1863 con->file = 0;
1864 unlink(con->filename);
1865 ClearString(&con->filename);
1866
1867 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1868 {
1869 CloseClient(con);
1870 return (0);
1871 }
1872 }
1873 }
1874 else if (con->http.state == HTTP_POST_RECV)
1875 {
1876 return (0);
1877 }
1878 else if (con->http.state != HTTP_POST_SEND)
1879 {
1880 CloseClient(con);
1881 return (0);
1882 }
1883 }
1884
1885 if (con->http.state == HTTP_POST_SEND)
1886 {
1887 if (con->file)
1888 {
1889 fstat(con->file, &filestats);
1890
1891 LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
1892 con->http.fd, con->file, (int)filestats.st_size);
1893
1894 close(con->file);
1895 con->file = 0;
1896
1897 if (filestats.st_size > MaxRequestSize &&
1898 MaxRequestSize > 0)
1899 {
1900 /*
1901 * Request is too big; remove it and send an error...
1902 */
1903
1904 LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
1905 con->http.fd, con->filename);
1906 unlink(con->filename);
1907 ClearString(&con->filename);
1908
1909 if (con->request)
1910 {
1911 /*
1912 * Delete any IPP request data...
1913 */
1914
1915 ippDelete(con->request);
1916 con->request = NULL;
1917 }
1918
1919 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1920 {
1921 CloseClient(con);
1922 return (0);
1923 }
1924 }
1925
1926 if (con->command)
1927 {
1928 if (!SendCommand(con, con->command, con->options))
1929 {
1930 if (!SendError(con, HTTP_NOT_FOUND))
1931 {
1932 CloseClient(con);
1933 return (0);
1934 }
1935 }
1936 else
1937 LogRequest(con, HTTP_OK);
1938 }
1939 }
1940
1941 if (con->request)
1942 return (ProcessIPPRequest(con));
1943 }
1944 break;
1945
1946 default :
1947 break; /* Anti-compiler-warning-code */
1948 }
1949
1950 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
1951 {
1952 CloseClient(con);
1953 return (0);
1954 }
1955 else
1956 return (1);
1957 }
1958
1959
1960 /*
1961 * 'SendCommand()' - Send output from a command via HTTP.
1962 */
1963
1964 int
1965 SendCommand(client_t *con,
1966 char *command,
1967 char *options)
1968 {
1969 int fd;
1970
1971
1972 if (con->filename)
1973 fd = open(con->filename, O_RDONLY);
1974 else
1975 fd = open("/dev/null", O_RDONLY);
1976
1977 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
1978
1979 close(fd);
1980
1981 LogMessage(L_INFO, "Started \"%s\" (pid=%d)", command, con->pipe_pid);
1982
1983 LogMessage(L_DEBUG, "SendCommand() %d file=%d", con->http.fd, con->file);
1984
1985 if (con->pipe_pid == 0)
1986 return (0);
1987
1988 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1989
1990 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to InputSet...", con->file);
1991 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to OutputSet...",
1992 con->http.fd);
1993
1994 FD_SET(con->file, InputSet);
1995 FD_SET(con->http.fd, OutputSet);
1996
1997 if (!SendHeader(con, HTTP_OK, NULL))
1998 return (0);
1999
2000 if (con->http.version == HTTP_1_1)
2001 {
2002 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
2003
2004 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
2005 return (0);
2006 }
2007
2008 con->got_fields = 0;
2009 con->field_col = 0;
2010
2011 return (1);
2012 }
2013
2014
2015 /*
2016 * 'SendError()' - Send an error message via HTTP.
2017 */
2018
2019 int /* O - 1 if successful, 0 otherwise */
2020 SendError(client_t *con, /* I - Connection */
2021 http_status_t code) /* I - Error code */
2022 {
2023 char message[1024]; /* Message for user */
2024
2025
2026 /*
2027 * Put the request in the access_log file...
2028 */
2029
2030 if (con->operation > HTTP_WAITING)
2031 LogRequest(con, code);
2032
2033 LogMessage(L_DEBUG, "SendError() %d code=%d", con->http.fd, code);
2034
2035 /*
2036 * To work around bugs in some proxies, don't use Keep-Alive for some
2037 * error messages...
2038 */
2039
2040 if (code >= HTTP_BAD_REQUEST)
2041 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
2042
2043 /*
2044 * Send an error message back to the client. If the error code is a
2045 * 400 or 500 series, make sure the message contains some text, too!
2046 */
2047
2048 if (!SendHeader(con, code, NULL))
2049 return (0);
2050
2051 #ifdef HAVE_SSL
2052 if (code == HTTP_UPGRADE_REQUIRED)
2053 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
2054 return (0);
2055
2056 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0)
2057 return (0);
2058 #endif /* HAVE_SSL */
2059
2060 if ((con->http.version >= HTTP_1_1 && !con->http.keep_alive) ||
2061 (code >= HTTP_BAD_REQUEST && code != HTTP_UPGRADE_REQUIRED))
2062 {
2063 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
2064 return (0);
2065 }
2066
2067 if (code >= HTTP_BAD_REQUEST)
2068 {
2069 /*
2070 * Send a human-readable error message.
2071 */
2072
2073 snprintf(message, sizeof(message),
2074 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
2075 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
2076 code, httpStatus(code), httpStatus(code),
2077 con->language ? con->language->messages[code] :
2078 httpStatus(code));
2079
2080 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
2081 return (0);
2082 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
2083 (int)strlen(message)) < 0)
2084 return (0);
2085 if (httpPrintf(HTTP(con), "\r\n") < 0)
2086 return (0);
2087 if (httpPrintf(HTTP(con), "%s", message) < 0)
2088 return (0);
2089 }
2090 else if (httpPrintf(HTTP(con), "\r\n") < 0)
2091 return (0);
2092
2093 con->http.state = HTTP_WAITING;
2094
2095 return (1);
2096 }
2097
2098
2099 /*
2100 * 'SendFile()' - Send a file via HTTP.
2101 */
2102
2103 int
2104 SendFile(client_t *con,
2105 http_status_t code,
2106 char *filename,
2107 char *type,
2108 struct stat *filestats)
2109 {
2110 con->file = open(filename, O_RDONLY);
2111
2112 LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
2113
2114 if (con->file < 0)
2115 return (0);
2116
2117 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2118
2119 con->pipe_pid = 0;
2120
2121 if (!SendHeader(con, code, type))
2122 return (0);
2123
2124 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
2125 return (0);
2126 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
2127 (unsigned long)filestats->st_size) < 0)
2128 return (0);
2129 if (httpPrintf(HTTP(con), "\r\n") < 0)
2130 return (0);
2131
2132 LogMessage(L_DEBUG2, "SendFile: Adding fd %d to OutputSet...", con->http.fd);
2133
2134 FD_SET(con->http.fd, OutputSet);
2135
2136 return (1);
2137 }
2138
2139
2140 /*
2141 * 'SendHeader()' - Send an HTTP request.
2142 */
2143
2144 int /* O - 1 on success, 0 on failure */
2145 SendHeader(client_t *con, /* I - Client to send to */
2146 http_status_t code, /* I - HTTP status code */
2147 char *type) /* I - MIME type of document */
2148 {
2149 location_t *loc; /* Authentication location */
2150
2151
2152 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
2153 con->http.version % 100, code, httpStatus(code)) < 0)
2154 return (0);
2155 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
2156 return (0);
2157 if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
2158 return (0);
2159 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
2160 {
2161 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
2162 return (0);
2163 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
2164 return (0);
2165 }
2166 if (code == HTTP_METHOD_NOT_ALLOWED)
2167 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
2168 return (0);
2169
2170 if (code == HTTP_UNAUTHORIZED)
2171 {
2172 /*
2173 * This already succeeded in IsAuthorized...
2174 */
2175
2176 loc = FindBest(con->uri, con->http.state);
2177
2178 if (loc->type != AUTH_DIGEST)
2179 {
2180 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
2181 return (0);
2182 }
2183 else
2184 {
2185 if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" "
2186 "nonce=\"%s\"\r\n", con->http.hostname) < 0)
2187 return (0);
2188 }
2189 }
2190 if (con->language != NULL)
2191 {
2192 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
2193 con->language->language) < 0)
2194 return (0);
2195
2196 if (type != NULL)
2197 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
2198 cupsLangEncoding(con->language)) < 0)
2199 return (0);
2200 }
2201 else if (type != NULL)
2202 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
2203 return (0);
2204
2205 return (1);
2206 }
2207
2208
2209 /*
2210 * 'ShutdownClient()' - Shutdown the receiving end of a connection.
2211 */
2212
2213 void
2214 ShutdownClient(client_t *con) /* I - Client connection */
2215 {
2216 /*
2217 * Shutdown the receiving end of the socket, since the client
2218 * still needs to read the error message...
2219 */
2220
2221 shutdown(con->http.fd, 0);
2222 con->http.used = 0;
2223
2224 /*
2225 * Update the activity time so that we timeout after 30 seconds rather
2226 * then the current Timeout setting (300 by default). This prevents
2227 * some DoS situations...
2228 */
2229
2230 con->http.activity = time(NULL) - Timeout + 30;
2231
2232 LogMessage(L_DEBUG2, "ShutdownClient: Removing fd %d from InputSet...",
2233 con->http.fd);
2234
2235 FD_CLR(con->http.fd, InputSet);
2236 }
2237
2238
2239 /*
2240 * 'UpdateCGI()' - Read status messages from CGI scripts and programs.
2241 */
2242
2243 void
2244 UpdateCGI(void)
2245 {
2246 int bytes; /* Number of bytes read */
2247 char *lineptr, /* Pointer to end of line in buffer */
2248 *message; /* Pointer to message text */
2249 int loglevel; /* Log level for message */
2250 static int bufused = 0; /* Number of bytes used in buffer */
2251 static char buffer[1024]; /* Status buffer */
2252
2253
2254 if ((bytes = read(CGIPipes[0], buffer + bufused,
2255 sizeof(buffer) - bufused - 1)) > 0)
2256 {
2257 bufused += bytes;
2258 buffer[bufused] = '\0';
2259 lineptr = strchr(buffer, '\n');
2260 }
2261 else if (bytes < 0 && errno == EINTR)
2262 return;
2263 else
2264 {
2265 lineptr = buffer + bufused;
2266 lineptr[1] = 0;
2267 }
2268
2269 if (bytes == 0 && bufused == 0)
2270 lineptr = NULL;
2271
2272 while (lineptr != NULL)
2273 {
2274 /*
2275 * Terminate each line and process it...
2276 */
2277
2278 *lineptr++ = '\0';
2279
2280 /*
2281 * Figure out the logging level...
2282 */
2283
2284 if (strncmp(buffer, "EMERG:", 6) == 0)
2285 {
2286 loglevel = L_EMERG;
2287 message = buffer + 6;
2288 }
2289 else if (strncmp(buffer, "ALERT:", 6) == 0)
2290 {
2291 loglevel = L_ALERT;
2292 message = buffer + 6;
2293 }
2294 else if (strncmp(buffer, "CRIT:", 5) == 0)
2295 {
2296 loglevel = L_CRIT;
2297 message = buffer + 5;
2298 }
2299 else if (strncmp(buffer, "ERROR:", 6) == 0)
2300 {
2301 loglevel = L_ERROR;
2302 message = buffer + 6;
2303 }
2304 else if (strncmp(buffer, "WARNING:", 8) == 0)
2305 {
2306 loglevel = L_WARN;
2307 message = buffer + 8;
2308 }
2309 else if (strncmp(buffer, "NOTICE:", 6) == 0)
2310 {
2311 loglevel = L_NOTICE;
2312 message = buffer + 6;
2313 }
2314 else if (strncmp(buffer, "INFO:", 5) == 0)
2315 {
2316 loglevel = L_INFO;
2317 message = buffer + 5;
2318 }
2319 else if (strncmp(buffer, "DEBUG:", 6) == 0)
2320 {
2321 loglevel = L_DEBUG;
2322 message = buffer + 6;
2323 }
2324 else if (strncmp(buffer, "DEBUG2:", 7) == 0)
2325 {
2326 loglevel = L_DEBUG2;
2327 message = buffer + 7;
2328 }
2329 else if (strncmp(buffer, "PAGE:", 5) == 0)
2330 {
2331 loglevel = L_PAGE;
2332 message = buffer + 5;
2333 }
2334 else
2335 {
2336 loglevel = L_DEBUG;
2337 message = buffer;
2338 }
2339
2340 /*
2341 * Skip leading whitespace in the message...
2342 */
2343
2344 while (isspace(*message))
2345 message ++;
2346
2347 LogMessage(loglevel, "[CGI] %s", message);
2348
2349 /*
2350 * Copy over the buffer data we've used up...
2351 */
2352
2353 strcpy(buffer, lineptr);
2354 bufused -= lineptr - buffer;
2355
2356 if (bufused < 0)
2357 bufused = 0;
2358
2359 lineptr = strchr(buffer, '\n');
2360 }
2361
2362 if (bytes <= 0)
2363 {
2364 /*
2365 * Fatal error on pipe - should never happen!
2366 */
2367
2368 LogMessage(L_ERROR, "UpdateCGI: error reading from CGI error pipe - %s",
2369 strerror(errno));
2370 }
2371 }
2372
2373
2374 /*
2375 * 'WriteClient()' - Write data to a client as needed.
2376 */
2377
2378 int /* O - 1 if success, 0 if fail */
2379 WriteClient(client_t *con) /* I - Client connection */
2380 {
2381 int bytes; /* Number of bytes written */
2382 char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */
2383 char *bufptr; /* Pointer into buffer */
2384 ipp_state_t ipp_state; /* IPP state value */
2385
2386
2387 if (con->http.state != HTTP_GET_SEND &&
2388 con->http.state != HTTP_POST_SEND)
2389 return (1);
2390
2391 if (con->response != NULL)
2392 {
2393 ipp_state = ippWrite(&(con->http), con->response);
2394 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
2395 }
2396 else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
2397 {
2398 if (con->pipe_pid && !con->got_fields)
2399 {
2400 /*
2401 * Inspect the data for Content-Type and other fields.
2402 */
2403
2404 buf[bytes] = '\0';
2405
2406 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
2407 if (*bufptr == '\n')
2408 {
2409 /*
2410 * Send line to client...
2411 */
2412
2413 if (bufptr > buf && bufptr[-1] == '\r')
2414 bufptr[-1] = '\0';
2415 *bufptr++ = '\0';
2416
2417 httpPrintf(HTTP(con), "%s\r\n", buf);
2418 LogMessage(L_DEBUG2, "WriteClient() %d %s", con->http.fd, buf);
2419
2420 /*
2421 * Update buffer...
2422 */
2423
2424 bytes -= (bufptr - buf);
2425 memcpy(buf, bufptr, bytes + 1);
2426 bufptr = buf - 1;
2427
2428 /*
2429 * See if the line was empty...
2430 */
2431
2432 if (con->field_col == 0)
2433 con->got_fields = 1;
2434 else
2435 con->field_col = 0;
2436 }
2437 else if (*bufptr != '\r')
2438 con->field_col ++;
2439
2440 if (bytes > 0 && !con->got_fields)
2441 {
2442 /*
2443 * Remaining text needs to go out...
2444 */
2445
2446 httpPrintf(HTTP(con), "%s", buf);
2447
2448 con->http.activity = time(NULL);
2449 return (1);
2450 }
2451 else if (bytes == 0)
2452 {
2453 con->http.activity = time(NULL);
2454 return (1);
2455 }
2456 }
2457
2458 if (httpWrite(HTTP(con), buf, bytes) < 0)
2459 {
2460 CloseClient(con);
2461 return (0);
2462 }
2463
2464 con->bytes += bytes;
2465 }
2466
2467 if (bytes <= 0)
2468 {
2469 LogRequest(con, HTTP_OK);
2470
2471 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
2472 {
2473 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
2474 {
2475 CloseClient(con);
2476 return (0);
2477 }
2478 }
2479
2480 con->http.state = HTTP_WAITING;
2481
2482 LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from OutputSet...",
2483 con->http.fd);
2484
2485 FD_CLR(con->http.fd, OutputSet);
2486
2487 if (con->file)
2488 {
2489 LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from InputSet...",
2490 con->file);
2491 FD_CLR(con->file, InputSet);
2492
2493 if (con->pipe_pid)
2494 kill(con->pipe_pid, SIGTERM);
2495
2496 LogMessage(L_DEBUG2, "WriteClient() %d Closing data file %d.",
2497 con->http.fd, con->file);
2498
2499 close(con->file);
2500 con->file = 0;
2501 con->pipe_pid = 0;
2502 }
2503
2504 if (con->filename)
2505 {
2506 LogMessage(L_DEBUG2, "WriteClient() %d Removing temp file %s",
2507 con->http.fd, con->filename);
2508 unlink(con->filename);
2509 ClearString(&con->filename);
2510 }
2511
2512 if (con->request != NULL)
2513 {
2514 ippDelete(con->request);
2515 con->request = NULL;
2516 }
2517
2518 if (con->response != NULL)
2519 {
2520 ippDelete(con->response);
2521 con->response = NULL;
2522 }
2523
2524 ClearString(&con->command);
2525 ClearString(&con->options);
2526
2527 if (!con->http.keep_alive)
2528 {
2529 CloseClient(con);
2530 return (0);
2531 }
2532 }
2533
2534 if (bytes >= 1024)
2535 LogMessage(L_DEBUG2, "WriteClient() %d %d bytes", con->http.fd, bytes);
2536
2537 con->http.activity = time(NULL);
2538
2539 return (1);
2540 }
2541
2542
2543 /*
2544 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
2545 */
2546
2547 static int /* O - 1 if modified since */
2548 check_if_modified(client_t *con, /* I - Client connection */
2549 struct stat *filestats) /* I - File information */
2550 {
2551 char *ptr; /* Pointer into field */
2552 time_t date; /* Time/date value */
2553 int size; /* Size/length value */
2554
2555
2556 size = 0;
2557 date = 0;
2558 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
2559
2560 if (*ptr == '\0')
2561 return (1);
2562
2563 LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"",
2564 con->http.fd, ptr);
2565
2566 while (*ptr != '\0')
2567 {
2568 while (isspace(*ptr) || *ptr == ';')
2569 ptr ++;
2570
2571 if (strncasecmp(ptr, "length=", 7) == 0)
2572 {
2573 ptr += 7;
2574 size = atoi(ptr);
2575
2576 while (isdigit(*ptr))
2577 ptr ++;
2578 }
2579 else if (isalpha(*ptr))
2580 {
2581 date = httpGetDateTime(ptr);
2582 while (*ptr != '\0' && *ptr != ';')
2583 ptr ++;
2584 }
2585 }
2586
2587 LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
2588 con->http.fd, size, (int)filestats->st_size, (int)date,
2589 (int)filestats->st_mtime);
2590
2591 return ((size != filestats->st_size && size != 0) ||
2592 (date < filestats->st_mtime && date != 0) ||
2593 (size == 0 && date == 0));
2594 }
2595
2596
2597 /*
2598 * 'decode_auth()' - Decode an authorization string.
2599 */
2600
2601 static void
2602 decode_auth(client_t *con) /* I - Client to decode to */
2603 {
2604 char *s, /* Authorization string */
2605 value[1024]; /* Value string */
2606 const char *username; /* Certificate username */
2607
2608
2609 /*
2610 * Decode the string...
2611 */
2612
2613 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
2614
2615 LogMessage(L_DEBUG2, "decode_auth(%p): Authorization string = \"%s\"",
2616 con, s);
2617
2618 if (strncmp(s, "Basic", 5) == 0)
2619 {
2620 s += 5;
2621 while (isspace(*s))
2622 s ++;
2623
2624 httpDecode64(value, s);
2625
2626 /*
2627 * Pull the username and password out...
2628 */
2629
2630 if ((s = strchr(value, ':')) == NULL)
2631 {
2632 LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
2633 con->http.fd, value);
2634 return;
2635 }
2636
2637 *s++ = '\0';
2638
2639 strlcpy(con->username, value, sizeof(con->username));
2640 strlcpy(con->password, s, sizeof(con->password));
2641 }
2642 else if (strncmp(s, "Local", 5) == 0)
2643 {
2644 s += 5;
2645 while (isspace(*s))
2646 s ++;
2647
2648 if ((username = FindCert(s)) != NULL)
2649 strlcpy(con->username, username, sizeof(con->username));
2650 }
2651 else if (strncmp(s, "Digest", 5) == 0)
2652 {
2653 /*
2654 * Get the username and password from the Digest attributes...
2655 */
2656
2657 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username",
2658 value))
2659 strlcpy(con->username, value, sizeof(con->username));
2660
2661 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response",
2662 value))
2663 strlcpy(con->password, value, sizeof(con->password) - 1);
2664 }
2665
2666 LogMessage(L_DEBUG2, "decode_auth() %d username=\"%s\"",
2667 con->http.fd, con->username);
2668 }
2669
2670
2671 /*
2672 * 'get_file()' - Get a filename and state info.
2673 */
2674
2675 static char * /* O - Real filename */
2676 get_file(client_t *con, /* I - Client connection */
2677 struct stat *filestats,/* O - File information */
2678 char *filename, /* IO - Filename buffer */
2679 int len) /* I - Buffer length */
2680 {
2681 int status; /* Status of filesystem calls */
2682 char *params; /* Pointer to parameters in URI */
2683
2684
2685 /*
2686 * Need to add DocumentRoot global...
2687 */
2688
2689 if (strncmp(con->uri, "/ppd/", 5) == 0)
2690 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
2691 else if (strncmp(con->uri, "/admin/conf/", 12) == 0)
2692 snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
2693 else if (con->language != NULL)
2694 snprintf(filename, len, "%s/%s%s", DocumentRoot, con->language->language,
2695 con->uri);
2696 else
2697 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
2698
2699 if ((params = strchr(filename, '?')) != NULL)
2700 *params = '\0';
2701
2702 /*
2703 * Grab the status for this language; if there isn't a language-specific file
2704 * then fallback to the default one...
2705 */
2706
2707 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
2708 {
2709 /*
2710 * Drop the language prefix and try the current directory...
2711 */
2712
2713 if (strncmp(con->uri, "/ppd/", 5) != 0 &&
2714 strncmp(con->uri, "/admin/conf/", 12) != 0)
2715 {
2716 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
2717
2718 status = stat(filename, filestats);
2719 }
2720 }
2721
2722 /*
2723 * If we're found a directory, get the index.html file instead...
2724 */
2725
2726 if (!status && S_ISDIR(filestats->st_mode))
2727 {
2728 if (filename[strlen(filename) - 1] == '/')
2729 strlcat(filename, "index.html", len);
2730 else
2731 strlcat(filename, "/index.html", len);
2732
2733 status = stat(filename, filestats);
2734 }
2735
2736 LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d",
2737 con->http.fd, filename, status ? -1 : (int)filestats->st_size);
2738
2739 if (status)
2740 return (NULL);
2741 else
2742 return (filename);
2743 }
2744
2745
2746 /*
2747 * 'install_conf_file()' - Install a configuration file.
2748 */
2749
2750 static http_status_t /* O - Status */
2751 install_conf_file(client_t *con) /* I - Connection */
2752 {
2753 cups_file_t *in, /* Input file */
2754 *out; /* Output file */
2755 char buffer[1024]; /* Copy buffer */
2756 int bytes; /* Number of bytes */
2757 char conffile[1024], /* Configuration filename */
2758 newfile[1024], /* New config filename */
2759 oldfile[1024]; /* Old config filename */
2760 struct stat confinfo; /* Config file info */
2761
2762
2763 /*
2764 * First construct the filenames...
2765 */
2766
2767 snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11);
2768 snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11);
2769 snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11);
2770
2771 LogMessage(L_INFO, "Installing config file \"%s\"...", conffile);
2772
2773 /*
2774 * Get the owner, group, and permissions of the configuration file.
2775 * If it doesn't exist, assign it to the User and Group in the
2776 * cupsd.conf file with mode 0640 permissions.
2777 */
2778
2779 if (stat(conffile, &confinfo))
2780 {
2781 confinfo.st_uid = User;
2782 confinfo.st_gid = Group;
2783 confinfo.st_mode = ConfigFilePerm;
2784 }
2785
2786 /*
2787 * Open the request file and new config file...
2788 */
2789
2790 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
2791 {
2792 LogMessage(L_ERROR, "Unable to open request file \"%s\" - %s",
2793 con->filename, strerror(errno));
2794 return (HTTP_SERVER_ERROR);
2795 }
2796
2797 if ((out = cupsFileOpen(newfile, "wb")) == NULL)
2798 {
2799 cupsFileClose(in);
2800 LogMessage(L_ERROR, "Unable to open config file \"%s\" - %s",
2801 newfile, strerror(errno));
2802 return (HTTP_SERVER_ERROR);
2803 }
2804
2805 fchmod(cupsFileNumber(out), confinfo.st_mode);
2806 fchown(cupsFileNumber(out), confinfo.st_uid, confinfo.st_gid);
2807
2808 /*
2809 * Copy from the request to the new config file...
2810 */
2811
2812 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
2813 if (cupsFileWrite(out, buffer, bytes) < bytes)
2814 {
2815 LogMessage(L_ERROR, "Unable to copy to config file \"%s\" - %s",
2816 newfile, strerror(errno));
2817
2818 cupsFileClose(in);
2819 cupsFileClose(out);
2820 unlink(newfile);
2821
2822 return (HTTP_SERVER_ERROR);
2823 }
2824
2825 /*
2826 * Close the files...
2827 */
2828
2829 cupsFileClose(in);
2830 if (cupsFileClose(out))
2831 {
2832 LogMessage(L_ERROR, "Error file closing config file \"%s\" - %s",
2833 newfile, strerror(errno));
2834
2835 unlink(newfile);
2836
2837 return (HTTP_SERVER_ERROR);
2838 }
2839
2840 /*
2841 * Remove the request file...
2842 */
2843
2844 unlink(con->filename);
2845 ClearString(&con->filename);
2846
2847 /*
2848 * Unlink the old backup, rename the current config file to the backup
2849 * filename, and rename the new config file to the config file name...
2850 */
2851
2852 if (unlink(oldfile))
2853 if (errno != ENOENT)
2854 {
2855 LogMessage(L_ERROR, "Unable to remove backup config file \"%s\" - %s",
2856 oldfile, strerror(errno));
2857
2858 unlink(newfile);
2859
2860 return (HTTP_SERVER_ERROR);
2861 }
2862
2863 if (rename(conffile, oldfile))
2864 if (errno != ENOENT)
2865 {
2866 LogMessage(L_ERROR, "Unable to rename old config file \"%s\" - %s",
2867 conffile, strerror(errno));
2868
2869 unlink(newfile);
2870
2871 return (HTTP_SERVER_ERROR);
2872 }
2873
2874 if (rename(newfile, conffile))
2875 {
2876 LogMessage(L_ERROR, "Unable to rename new config file \"%s\" - %s",
2877 newfile, strerror(errno));
2878
2879 rename(oldfile, conffile);
2880 unlink(newfile);
2881
2882 return (HTTP_SERVER_ERROR);
2883 }
2884
2885 /*
2886 * If the cupsd.conf file was updated, set the NeedReload flag...
2887 */
2888
2889 if (strcmp(con->uri, "/admin/conf/cupsd.conf") == 0)
2890 NeedReload = RELOAD_CUPSD;
2891 else
2892 NeedReload = RELOAD_ALL;
2893
2894 /*
2895 * Return that the file was created successfully...
2896 */
2897
2898 return (HTTP_CREATED);
2899 }
2900
2901
2902 /*
2903 * 'pipe_command()' - Pipe the output of a command to the remote client.
2904 */
2905
2906 static int /* O - Process ID */
2907 pipe_command(client_t *con, /* I - Client connection */
2908 int infile, /* I - Standard input for command */
2909 int *outfile, /* O - Standard output for command */
2910 char *command, /* I - Command to run */
2911 char *options) /* I - Options for command */
2912 {
2913 int i; /* Looping var */
2914 int pid; /* Process ID */
2915 char *commptr; /* Command string pointer */
2916 char *uriptr; /* URI string pointer */
2917 int fd; /* Looping var */
2918 int fds[2]; /* Pipe FDs */
2919 int argc; /* Number of arguments */
2920 int envc; /* Number of environment variables */
2921 char argbuf[10240], /* Argument buffer */
2922 *argv[100], /* Argument strings */
2923 *envp[100]; /* Environment variables */
2924 char content_length[1024], /* CONTENT_LENGTH environment variable */
2925 content_type[1024], /* CONTENT_TYPE environment variable */
2926 cups_datadir[1024], /* CUPS_DATADIR environment variable */
2927 cups_serverroot[1024], /* CUPS_SERVERROOT environment variable */
2928 http_cookie[1024], /* HTTP_COOKIE environment variable */
2929 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
2930 ipp_port[1024], /* IPP_PORT environment variable */
2931 lang[1024], /* LANG environment variable */
2932 ld_library_path[1024], /* LD_LIBRARY_PATH environment variable */
2933 ld_preload[1024], /* LD_PRELOAD environment variable */
2934 dyld_library_path[1024],/* DYLD_LIBRARY_PATH environment variable */
2935 shlib_path[1024], /* SHLIB_PATH environment variable */
2936 nlspath[1024], /* NLSPATH environment variable */
2937 query_string[10240], /* QUERY_STRING env variable */
2938 remote_addr[1024], /* REMOTE_ADDR environment variable */
2939 remote_host[1024], /* REMOTE_HOST environment variable */
2940 remote_user[1024], /* REMOTE_USER environment variable */
2941 script_name[1024], /* SCRIPT_NAME environment variable */
2942 server_name[1024], /* SERVER_NAME environment variable */
2943 server_port[1024], /* SERVER_PORT environment variable */
2944 tmpdir[1024], /* TMPDIR environment variable */
2945 vg_args[1024]; /* VG_ARGS environment variable */
2946 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
2947 struct sigaction action; /* POSIX signal handler */
2948 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
2949
2950
2951 /*
2952 * Copy the command string...
2953 */
2954
2955 strlcpy(argbuf, options, sizeof(argbuf));
2956
2957 /*
2958 * Parse the string; arguments can be separated by + and are terminated
2959 * by ?...
2960 */
2961
2962 argv[0] = argbuf;
2963
2964 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
2965 if (*commptr == ' ' || *commptr == '+')
2966 {
2967 *commptr++ = '\0';
2968
2969 while (*commptr == ' ')
2970 commptr ++;
2971
2972 if (*commptr != '\0')
2973 {
2974 argv[argc] = commptr;
2975 argc ++;
2976 }
2977
2978 commptr --;
2979 }
2980 else if (*commptr == '%')
2981 {
2982 if (commptr[1] >= '0' && commptr[1] <= '9')
2983 *commptr = (commptr[1] - '0') << 4;
2984 else
2985 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
2986
2987 if (commptr[2] >= '0' && commptr[2] <= '9')
2988 *commptr |= commptr[2] - '0';
2989 else
2990 *commptr |= tolower(commptr[2]) - 'a' + 10;
2991
2992 cups_strcpy(commptr + 1, commptr + 3);
2993 }
2994 else if (*commptr == '?')
2995 break;
2996
2997 argv[argc] = NULL;
2998
2999 if (argv[0][0] == '\0')
3000 argv[0] = strrchr(command, '/') + 1;
3001
3002 /*
3003 * Setup the environment variables as needed...
3004 */
3005
3006 snprintf(lang, sizeof(lang), "LANG=%s",
3007 con->language ? con->language->language : "C");
3008 sprintf(ipp_port, "IPP_PORT=%d", LocalPort);
3009 #ifdef AF_INET6
3010 if (con->http.hostaddr.addr.sa_family == AF_INET6)
3011 sprintf(server_port, "SERVER_PORT=%d",
3012 ntohs(con->http.hostaddr.ipv6.sin6_port));
3013 else
3014 #endif /* AF_INET6 */
3015 sprintf(server_port, "SERVER_PORT=%d",
3016 ntohs(con->http.hostaddr.ipv4.sin_port));
3017
3018 if (strcmp(con->http.hostname, "localhost") == 0)
3019 strlcpy(server_name, "SERVER_NAME=localhost", sizeof(server_name));
3020 else
3021 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName);
3022 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname);
3023 strcpy(remote_addr, "REMOTE_ADDR=");
3024 httpAddrString(&(con->http.hostaddr), remote_addr + 12,
3025 sizeof(remote_addr) - 12);
3026 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
3027 snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
3028 snprintf(cups_datadir, sizeof(cups_datadir), "CUPS_DATADIR=%s", DataDir);
3029 snprintf(cups_serverroot, sizeof(cups_serverroot), "CUPS_SERVERROOT=%s", ServerRoot);
3030
3031 envc = 0;
3032
3033 envp[envc ++] = "PATH=/bin:/usr/bin";
3034 envp[envc ++] = "SERVER_SOFTWARE=CUPS/1.1";
3035 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
3036 if (con->http.version == HTTP_1_1)
3037 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
3038 else if (con->http.version == HTTP_1_0)
3039 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
3040 else
3041 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
3042 envp[envc ++] = "REDIRECT_STATUS=1";
3043 envp[envc ++] = "CUPS_SERVER=localhost";
3044 envp[envc ++] = ipp_port;
3045 envp[envc ++] = server_name;
3046 envp[envc ++] = server_port;
3047 envp[envc ++] = remote_addr;
3048 envp[envc ++] = remote_host;
3049 envp[envc ++] = remote_user;
3050 envp[envc ++] = lang;
3051 envp[envc ++] = TZ;
3052 envp[envc ++] = tmpdir;
3053 envp[envc ++] = cups_datadir;
3054 envp[envc ++] = cups_serverroot;
3055
3056 if (getenv("VG_ARGS") != NULL)
3057 {
3058 snprintf(vg_args, sizeof(vg_args), "VG_ARGS=%s", getenv("VG_ARGS"));
3059 envp[envc ++] = vg_args;
3060 }
3061
3062 if (getenv("LD_LIBRARY_PATH") != NULL)
3063 {
3064 snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s",
3065 getenv("LD_LIBRARY_PATH"));
3066 envp[envc ++] = ld_library_path;
3067 }
3068
3069 if (getenv("LD_PRELOAD") != NULL)
3070 {
3071 snprintf(ld_preload, sizeof(ld_preload), "LD_PRELOAD=%s",
3072 getenv("LD_PRELOAD"));
3073 envp[envc ++] = ld_preload;
3074 }
3075
3076 if (getenv("DYLD_LIBRARY_PATH") != NULL)
3077 {
3078 snprintf(dyld_library_path, sizeof(dyld_library_path), "DYLD_LIBRARY_PATH=%s",
3079 getenv("DYLD_LIBRARY_PATH"));
3080 envp[envc ++] = dyld_library_path;
3081 }
3082
3083 if (getenv("SHLIB_PATH") != NULL)
3084 {
3085 snprintf(shlib_path, sizeof(shlib_path), "SHLIB_PATH=%s",
3086 getenv("SHLIB_PATH"));
3087 envp[envc ++] = shlib_path;
3088 }
3089
3090 if (getenv("NLSPATH") != NULL)
3091 {
3092 snprintf(nlspath, sizeof(nlspath), "NLSPATH=%s", getenv("NLSPATH"));
3093 envp[envc ++] = nlspath;
3094 }
3095
3096 if (con->http.cookie)
3097 {
3098 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
3099 con->http.cookie);
3100 envp[envc ++] = http_cookie;
3101 }
3102
3103 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
3104 {
3105 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
3106 con->http.fields[HTTP_FIELD_USER_AGENT]);
3107 envp[envc ++] = http_user_agent;
3108 }
3109
3110 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
3111 if ((uriptr = strchr(script_name, '?')) != NULL)
3112 *uriptr = '\0';
3113 envp[envc ++] = script_name;
3114
3115 if (con->operation == HTTP_GET)
3116 {
3117 for (i = 0; i < argc; i ++)
3118 LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
3119 envp[envc ++] = "REQUEST_METHOD=GET";
3120
3121 if (*commptr)
3122 {
3123 /*
3124 * Add GET form variables after ?...
3125 */
3126
3127 *commptr++ = '\0';
3128
3129 snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
3130 envp[envc ++] = query_string;
3131 }
3132 }
3133 else
3134 {
3135 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
3136 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
3137 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
3138
3139 envp[envc ++] = "REQUEST_METHOD=POST";
3140 envp[envc ++] = content_length;
3141 envp[envc ++] = content_type;
3142 }
3143
3144 /*
3145 * Tell the CGI if we are using encryption...
3146 */
3147
3148 if (con->http.encryption == HTTP_ENCRYPT_ALWAYS)
3149 {
3150 envp[envc ++] = "HTTPS=ON";
3151 envp[envc ++] = "CUPS_ENCRYPTION=Always";
3152 }
3153
3154 envp[envc] = NULL;
3155
3156 if (LogLevel == L_DEBUG2)
3157 {
3158 for (i = 0; i < argc; i ++)
3159 LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
3160 for (i = 0; i < envc; i ++)
3161 LogMessage(L_DEBUG2, "envp[%d] = \"%s\"", i, envp[i]);
3162 }
3163
3164 /*
3165 * Create a pipe for the output...
3166 */
3167
3168 if (pipe(fds))
3169 {
3170 LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
3171 argv[0], strerror(errno));
3172 return (0);
3173 }
3174
3175 /*
3176 * Block signals before forking...
3177 */
3178
3179 HoldSignals();
3180
3181 /*
3182 * Then execute the command...
3183 */
3184
3185 if ((pid = fork()) == 0)
3186 {
3187 /*
3188 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
3189 */
3190
3191 if (getuid() == 0)
3192 {
3193 /*
3194 * Running as root, so change to a non-priviledged user...
3195 */
3196
3197 if (setgid(Group))
3198 exit(errno);
3199
3200 if (setgroups(0, NULL))
3201 exit(errno);
3202
3203 if (setuid(User))
3204 exit(errno);
3205 }
3206 else
3207 {
3208 /*
3209 * Reset group membership to just the main one we belong to.
3210 */
3211
3212 setgroups(0, NULL);
3213 }
3214
3215 /*
3216 * Update stdin/stdout/stderr...
3217 */
3218
3219 if (infile)
3220 {
3221 close(0);
3222 if (dup(infile) < 0)
3223 exit(errno);
3224 }
3225
3226 close(1);
3227 if (dup(fds[1]) < 0)
3228 exit(errno);
3229
3230 close(2);
3231 dup(CGIPipes[1]);
3232
3233 /*
3234 * Close extra file descriptors...
3235 */
3236
3237 for (fd = 3; fd < MaxFDs; fd ++)
3238 close(fd);
3239
3240 /*
3241 * Change umask to restrict permissions on created files...
3242 */
3243
3244 umask(077);
3245
3246 /*
3247 * Unblock signals before doing the exec...
3248 */
3249
3250 #ifdef HAVE_SIGSET
3251 sigset(SIGTERM, SIG_DFL);
3252 sigset(SIGCHLD, SIG_DFL);
3253 #elif defined(HAVE_SIGACTION)
3254 memset(&action, 0, sizeof(action));
3255
3256 sigemptyset(&action.sa_mask);
3257 action.sa_handler = SIG_DFL;
3258
3259 sigaction(SIGTERM, &action, NULL);
3260 sigaction(SIGCHLD, &action, NULL);
3261 #else
3262 signal(SIGTERM, SIG_DFL);
3263 signal(SIGCHLD, SIG_DFL);
3264 #endif /* HAVE_SIGSET */
3265
3266 ReleaseSignals();
3267
3268 /*
3269 * Execute the pipe program; if an error occurs, exit with status 1...
3270 */
3271
3272 execve(command, argv, envp);
3273 exit(errno);
3274 return (0);
3275 }
3276 else if (pid < 0)
3277 {
3278 /*
3279 * Error - can't fork!
3280 */
3281
3282 LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
3283 strerror(errno));
3284
3285 close(fds[0]);
3286 close(fds[1]);
3287 pid = 0;
3288 }
3289 else
3290 {
3291 /*
3292 * Fork successful - return the PID...
3293 */
3294
3295 AddCert(pid, con->username);
3296
3297 LogMessage(L_DEBUG, "CGI %s started - PID = %d", command, pid);
3298
3299 *outfile = fds[0];
3300 close(fds[1]);
3301 }
3302
3303 ReleaseSignals();
3304
3305 return (pid);
3306 }
3307
3308
3309 #if defined(HAVE_CDSASSL)
3310 /*
3311 * 'CDSAReadFunc()' - Read function for CDSA decryption code.
3312 */
3313
3314 static OSStatus /* O - -1 on error, 0 on success */
3315 CDSAReadFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
3316 void *data, /* I - Data buffer */
3317 size_t *dataLength) /* IO - Number of bytes */
3318 {
3319 ssize_t bytes; /* Number of bytes read */
3320
3321
3322 bytes = recv((int)connection, data, *dataLength, 0);
3323 if (bytes >= 0)
3324 {
3325 *dataLength = bytes;
3326 return (0);
3327 }
3328 else
3329 return (-1);
3330 }
3331
3332
3333 /*
3334 * 'CDSAWriteFunc()' - Write function for CDSA encryption code.
3335 */
3336
3337 static OSStatus /* O - -1 on error, 0 on success */
3338 CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
3339 const void *data, /* I - Data buffer */
3340 size_t *dataLength) /* IO - Number of bytes */
3341 {
3342 ssize_t bytes;
3343
3344
3345 bytes = write((int)connection, data, *dataLength);
3346 if (bytes >= 0)
3347 {
3348 *dataLength = bytes;
3349 return (0);
3350 }
3351 else
3352 return (-1);
3353 }
3354 #endif /* HAVE_CDSASSL */
3355
3356
3357 /*
3358 * End of "$Id: client.c,v 1.91.2.65 2003/07/20 12:51:43 mike Exp $".
3359 */