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