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