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