]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/client.c
0bd27c6f1eb65440487197b4c767da9651e3382b
[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 * cupsdAcceptClient() - Accept a new client.
27 * cupsdCloseAllClients() - Close all remote clients immediately.
28 * cupsdCloseClient() - Close a remote client.
29 * cupsdEncryptClient() - Enable encryption for the client...
30 * cupsdIsCGI() - Is the resource a CGI script/program?
31 * cupsdReadClient() - Read data from a client.
32 * cupsdSendCommand() - Send output from a command via HTTP.
33 * cupsdSendError() - Send an error message via HTTP.
34 * cupsdSendFile() - Send a file via HTTP.
35 * cupsdSendHeader() - Send an HTTP request.
36 * cupsdUpdateCGI() - Read status messages from CGI scripts and programs.
37 * cupsdWriteClient() - 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(cupsd_client_t *con,
61 struct stat *filestats);
62 static void decode_auth(cupsd_client_t *con);
63 static char *get_file(cupsd_client_t *con, struct stat *filestats,
64 char *filename, int len);
65 static http_status_t install_conf_file(cupsd_client_t *con);
66 static int is_path_absolute(const char *path);
67 static int pipe_command(cupsd_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 * 'cupsdAcceptClient()' - Accept a new client.
80 */
81
82 void
83 cupsdAcceptClient(cupsd_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 cupsd_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 cupsdLogMessage(L_DEBUG2, "cupsdAcceptClient(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(cupsd_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 cupsdLogMessage(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 cupsdLogMessage(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 cupsdLogMessage(L_DEBUG2, "cupsdAcceptClient: 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 cupsdLogMessage(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 cupsdLogMessage(L_DEBUG2, "cupsdAcceptClient: 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 cupsdLogMessage(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 cupsdLogMessage(L_DEBUG, "cupsdAcceptClient: %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 cupsdLogMessage(L_DEBUG, "cupsdAcceptClient: %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 cupsdLogMessage(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 cupsdLogMessage(L_DEBUG2, "cupsdAcceptClient: %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 cupsdLogMessage(L_DEBUG2, "cupsdAcceptClient: 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 cupsdPauseListening();
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 cupsdEncryptClient(con);
372 }
373 else
374 con->auto_ssl = 1;
375 #endif /* HAVE_SSL */
376 }
377
378
379 /*
380 * 'cupsdCloseAllClients()' - Close all remote clients immediately.
381 */
382
383 void
384 cupsdCloseAllClients(void)
385 {
386 while (NumClients > 0)
387 cupsdCloseClient(Clients);
388 }
389
390
391 /*
392 * 'cupsdCloseClient()' - Close a remote client.
393 */
394
395 int /* O - 1 if partial close, 0 if fully closed */
396 cupsdCloseClient(cupsd_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 cupsdLogMessage(L_DEBUG, "cupsdCloseClient: %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 cupsdLogMessage(L_INFO, "cupsdCloseClient: SSL shutdown successful!");
440 break;
441 case -1 :
442 cupsdLogMessage(L_ERROR, "cupsdCloseClient: Fatal error during SSL shutdown!");
443 default :
444 while ((error = ERR_get_error()) != 0)
445 cupsdLogMessage(L_ERROR, "cupsdCloseClient: %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 cupsdLogMessage(L_INFO, "cupsdCloseClient: SSL shutdown successful!");
461 break;
462 default:
463 cupsdLogMessage(L_ERROR, "cupsdCloseClient: %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 if (con->pipe_pid != 0)
482 {
483 /*
484 * Stop any CGI process...
485 */
486
487 cupsdLogMessage(L_DEBUG2, "cupsdCloseClient: %d Killing process ID %d...",
488 con->http.fd, con->pipe_pid);
489 cupsdEndProcess(con->pipe_pid, 1);
490 }
491
492 if (con->file >= 0)
493 {
494 if (FD_ISSET(con->file, InputSet))
495 {
496 cupsdLogMessage(L_DEBUG2, "cupsdCloseClient: %d Removing fd %d from InputSet...",
497 con->http.fd, con->file);
498 FD_CLR(con->file, InputSet);
499 }
500
501 cupsdLogMessage(L_DEBUG2, "cupsdCloseClient: %d Closing data file %d.",
502 con->http.fd, con->file);
503
504 close(con->file);
505 con->file = -1;
506 }
507
508 /*
509 * Close the socket and clear the file from the input set for select()...
510 */
511
512 if (con->http.fd > 0)
513 {
514 if (partial)
515 {
516 /*
517 * Only do a partial close so that the encrypted client gets everything.
518 */
519
520 cupsdLogMessage(L_DEBUG2, "cupsdCloseClient: Removing fd %d from OutputSet...",
521 con->http.fd);
522 shutdown(con->http.fd, 0);
523 FD_CLR(con->http.fd, OutputSet);
524 }
525 else
526 {
527 /*
528 * Shut the socket down fully...
529 */
530
531 cupsdLogMessage(L_DEBUG2, "cupsdCloseClient: Removing fd %d from InputSet and OutputSet...",
532 con->http.fd);
533 close(con->http.fd);
534 FD_CLR(con->http.fd, InputSet);
535 FD_CLR(con->http.fd, OutputSet);
536 con->http.fd = -1;
537 }
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 cupsdClearString(&con->filename);
552 cupsdClearString(&con->command);
553 cupsdClearString(&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 cupsdResumeListening();
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(cupsd_client_t));
589 }
590
591 return (partial);
592 }
593
594
595 /*
596 * 'cupsdEncryptClient()' - Enable encryption for the client...
597 */
598
599 int /* O - 1 on success, 0 on error */
600 cupsdEncryptClient(cupsd_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 cupsdLogMessage(L_ERROR, "cupsdEncryptClient: Unable to encrypt connection from %s!",
624 con->http.hostname);
625
626 while ((error = ERR_get_error()) != 0)
627 cupsdLogMessage(L_ERROR, "cupsdEncryptClient: %s", ERR_error_string(error, NULL));
628
629 SSL_CTX_free(context);
630 SSL_free(conn);
631 return (0);
632 }
633
634 cupsdLogMessage(L_DEBUG, "cupsdEncryptClient: %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 cupsdLogMessage(L_ERROR, "cupsdEncryptClient: Unable to encrypt connection from %s!",
660 con->http.hostname);
661 cupsdLogMessage(L_ERROR, "cupsdEncryptClient: %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 cupsdLogMessage(L_ERROR, "cupsdEncryptClient: Unable to encrypt connection from %s!",
681 con->http.hostname);
682 cupsdLogMessage(L_ERROR, "cupsdEncryptClient: %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 cupsdLogMessage(L_DEBUG, "cupsdEncryptClient: %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 cupsdLogMessage(L_ERROR, "cupsdEncryptClient: Unable to encrypt connection from %s!",
753 con->http.hostname);
754
755 cupsdLogMessage(L_ERROR, "cupsdEncryptClient: 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 cupsdLogMessage(L_DEBUG, "cupsdEncryptClient: %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 * 'cupsdIsCGI()' - Is the resource a CGI script/program?
780 */
781
782 int /* O - 1 = CGI, 0 = file */
783 cupsdIsCGI(cupsd_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 cupsdLogMessage(L_DEBUG2, "cupsdIsCGI(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 cupsdLogMessage(L_DEBUG2, "cupsdIsCGI: 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 cupsdSetString(&con->command, filename);
820
821 filename = strrchr(filename, '/') + 1; /* Filename always absolute */
822
823 if (options)
824 cupsdSetStringf(&con->options, "%s %s", filename, options);
825 else
826 cupsdSetStringf(&con->options, "%s", filename);
827
828 cupsdLogMessage(L_DEBUG2, "cupsdIsCGI: 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 cupsdSetString(&con->command, CUPS_JAVA);
841
842 if (options)
843 cupsdSetStringf(&con->options, "java %s %s", filename, options);
844 else
845 cupsdSetStringf(&con->options, "java %s", filename);
846
847 cupsdLogMessage(L_DEBUG2, "cupsdIsCGI: 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 cupsdSetString(&con->command, CUPS_PERL);
861
862 if (options)
863 cupsdSetStringf(&con->options, "perl %s %s", filename, options);
864 else
865 cupsdSetStringf(&con->options, "perl %s", filename);
866
867 cupsdLogMessage(L_DEBUG2, "cupsdIsCGI: 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 cupsdSetString(&con->command, CUPS_PHP);
881
882 if (options)
883 cupsdSetStringf(&con->options, "php %s %s", filename, options);
884 else
885 cupsdSetStringf(&con->options, "php %s", filename);
886
887 cupsdLogMessage(L_DEBUG2, "cupsdIsCGI: 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 cupsdSetString(&con->command, CUPS_PYTHON);
901
902 if (options)
903 cupsdSetStringf(&con->options, "python %s %s", filename, options);
904 else
905 cupsdSetStringf(&con->options, "python %s", filename);
906
907 cupsdLogMessage(L_DEBUG2, "cupsdIsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
908 con->command, con->options);
909
910 return (1);
911 }
912 #endif /* HAVE_PYTHON */
913
914 cupsdLogMessage(L_DEBUG2, "cupsdIsCGI: Returning 0...");
915
916 return (0);
917 }
918
919
920 /*
921 * 'cupsdReadClient()' - Read data from a client.
922 */
923
924 int /* O - 1 on success, 0 on error */
925 cupsdReadClient(cupsd_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 cupsd_printer_t *p; /* Printer */
941 static unsigned request_id = 0; /* Request ID for temp files */
942
943
944 status = HTTP_CONTINUE;
945
946 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: %d, used=%d, file=%d", con->http.fd,
947 con->http.used, con->file);
948
949 if (con->http.error)
950 {
951 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: http error seen...");
952 return (cupsdCloseClient(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 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: Saw first byte %02X, auto-negotiating SSL/TLS session...",
972 buf[0] & 255);
973
974 cupsdEncryptClient(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 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: httpGets returned EOF...");
990 return (cupsdCloseClient(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 cupsdClearString(&con->command);
1021 cupsdClearString(&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 cupsdLogMessage(L_ERROR, "Bad request line \"%s\" from %s!", line,
1037 con->http.hostname);
1038 cupsdSendError(con, HTTP_BAD_REQUEST);
1039 return (cupsdCloseClient(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 cupsdLogMessage(L_ERROR, "Bad request line \"%s\" from %s!", line,
1047 con->http.hostname);
1048 cupsdSendError(con, HTTP_BAD_REQUEST);
1049 return (cupsdCloseClient(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 cupsdSendError(con, HTTP_NOT_SUPPORTED);
1063 return (cupsdCloseClient(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 cupsdLogMessage(L_ERROR, "Bad URI \"%s\" in request!", con->uri);
1101 cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED);
1102 return (cupsdCloseClient(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 cupsdLogMessage(L_ERROR, "Bad operation \"%s\"!", operation);
1134 cupsdSendError(con, HTTP_BAD_REQUEST);
1135 return (cupsdCloseClient(con));
1136 }
1137
1138 con->start = time(NULL);
1139 con->operation = con->http.state;
1140
1141 cupsdLogMessage(L_DEBUG, "cupsdReadClient: %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 cupsdSendError(con, HTTP_BAD_REQUEST);
1163 return (cupsdCloseClient(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 (!cupsdSendError(con, HTTP_BAD_REQUEST))
1226 return (cupsdCloseClient(con));
1227 }
1228 else if (con->operation == HTTP_OPTIONS)
1229 {
1230 /*
1231 * Do OPTIONS command...
1232 */
1233
1234 if ((con->best = cupsdFindBest(con->uri, con->http.state)) != NULL &&
1235 con->best->type != AUTH_NONE)
1236 {
1237 if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL))
1238 return (cupsdCloseClient(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 (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
1250 return (cupsdCloseClient(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 cupsdEncryptClient(con);
1258 #else
1259 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED))
1260 return (cupsdCloseClient(con));
1261 #endif /* HAVE_SSL */
1262 }
1263
1264 if (con->http.expect)
1265 {
1266 /**** TODO: send expected header ****/
1267 }
1268
1269 if (!cupsdSendHeader(con, HTTP_OK, NULL))
1270 return (cupsdCloseClient(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 (!cupsdSendError(con, HTTP_FORBIDDEN))
1283 return (cupsdCloseClient(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 (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
1296 return (cupsdCloseClient(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 cupsdEncryptClient(con);
1304 #else
1305 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED))
1306 return (cupsdCloseClient(con));
1307 #endif /* HAVE_SSL */
1308 }
1309
1310 con->best = cupsdFindBest(con->uri, con->http.state);
1311
1312 if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_OK)
1313 {
1314 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: Unauthorized request for %s...\n",
1315 con->uri);
1316 cupsdSendError(con, status);
1317 return (cupsdCloseClient(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 = cupsdFindPrinter(con->uri + 10)) != NULL)
1339 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1340 else
1341 {
1342 if (!cupsdSendError(con, HTTP_NOT_FOUND))
1343 return (cupsdCloseClient(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 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
1364
1365 if ((ptr = strchr(con->uri + 6, '?')) != NULL)
1366 cupsdSetStringf(&con->options, "admin%s", ptr);
1367 else
1368 cupsdSetString(&con->options, "admin");
1369 }
1370 else if (!strncmp(con->uri, "/printers", 9))
1371 {
1372 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi", ServerBin);
1373 cupsdSetString(&con->options, con->uri + 9);
1374 }
1375 else if (!strncmp(con->uri, "/classes", 8))
1376 {
1377 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi", ServerBin);
1378 cupsdSetString(&con->options, con->uri + 8);
1379 }
1380 else if (!strncmp(con->uri, "/jobs", 5))
1381 {
1382 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin);
1383 cupsdSetString(&con->options, con->uri + 5);
1384 }
1385 else
1386 {
1387 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi", ServerBin);
1388 cupsdSetString(&con->options, con->uri + 5);
1389 }
1390
1391 if (con->options[0] == '/')
1392 _cups_strcpy(con->options, con->options + 1);
1393
1394 if (!cupsdSendCommand(con, con->command, con->options, 0))
1395 {
1396 if (!cupsdSendError(con, HTTP_NOT_FOUND))
1397 return (cupsdCloseClient(con));
1398 }
1399 else
1400 cupsdLogRequest(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 (!cupsdSendError(con, HTTP_FORBIDDEN))
1418 return (cupsdCloseClient(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 (!cupsdSendError(con, HTTP_NOT_FOUND))
1432 return (cupsdCloseClient(con));
1433
1434 break;
1435 }
1436
1437 type = mimeFileType(MimeDatabase, filename, NULL);
1438
1439 if (cupsdIsCGI(con, filename, &filestats, type))
1440 {
1441 /*
1442 * Note: con->command and con->options were set by
1443 * cupsdIsCGI()...
1444 */
1445
1446 if (!cupsdSendCommand(con, con->command, con->options, 0))
1447 {
1448 if (!cupsdSendError(con, HTTP_NOT_FOUND))
1449 return (cupsdCloseClient(con));
1450 }
1451 else
1452 cupsdLogRequest(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 (!cupsdSendError(con, HTTP_NOT_MODIFIED))
1462 return (cupsdCloseClient(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 (!cupsdSendFile(con, HTTP_OK, filename, line, &filestats))
1472 return (cupsdCloseClient(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 cupsdLogMessage(L_DEBUG2, "POST %s", con->uri);
1484 cupsdLogMessage(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 (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1495 return (cupsdCloseClient(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 (!cupsdSendError(con, HTTP_BAD_REQUEST))
1506 return (cupsdCloseClient(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 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
1533
1534 if ((ptr = strchr(con->uri + 6, '?')) != NULL)
1535 cupsdSetStringf(&con->options, "admin%s", ptr);
1536 else
1537 cupsdSetString(&con->options, "admin");
1538 }
1539 else if (!strncmp(con->uri, "/printers", 9))
1540 {
1541 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi", ServerBin);
1542 cupsdSetString(&con->options, con->uri + 9);
1543 }
1544 else if (!strncmp(con->uri, "/classes", 8))
1545 {
1546 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi", ServerBin);
1547 cupsdSetString(&con->options, con->uri + 8);
1548 }
1549 else if (!strncmp(con->uri, "/jobs", 5))
1550 {
1551 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin);
1552 cupsdSetString(&con->options, con->uri + 5);
1553 }
1554 else
1555 {
1556 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi", ServerBin);
1557 cupsdSetString(&con->options, con->uri + 5);
1558 }
1559
1560 if (con->options[0] == '/')
1561 _cups_strcpy(con->options, con->options + 1);
1562
1563 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: %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 (!cupsdSendError(con, HTTP_NOT_FOUND))
1579 return (cupsdCloseClient(con));
1580
1581 break;
1582 }
1583
1584 type = mimeFileType(MimeDatabase, filename, NULL);
1585
1586 if (!cupsdIsCGI(con, filename, &filestats, type))
1587 {
1588 /*
1589 * Only POST to CGI's...
1590 */
1591
1592 if (!cupsdSendError(con, HTTP_UNAUTHORIZED))
1593 return (cupsdCloseClient(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 (!cupsdSendError(con, HTTP_FORBIDDEN))
1613 return (cupsdCloseClient(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 cupsdLogMessage(L_DEBUG2, "PUT %s", con->uri);
1624 cupsdLogMessage(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 (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1635 return (cupsdCloseClient(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 (!cupsdSendError(con, HTTP_BAD_REQUEST))
1646 return (cupsdCloseClient(con));
1647
1648 break;
1649 }
1650
1651 /*
1652 * Open a temporary file to hold the request...
1653 */
1654
1655 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
1656 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1657
1658 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: %d REQUEST %s=%d", con->http.fd,
1659 con->filename, con->file);
1660
1661 if (con->file < 0)
1662 {
1663 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1664 return (cupsdCloseClient(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 cupsdSendError(con, HTTP_NOT_IMPLEMENTED);
1675 return (cupsdCloseClient(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 = cupsdFindPrinter(con->uri + 10)) != NULL)
1689 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1690 else
1691 {
1692 if (!cupsdSendError(con, HTTP_NOT_FOUND))
1693 return (cupsdCloseClient(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 (!cupsdSendHeader(con, HTTP_OK, "text/html"))
1712 return (cupsdCloseClient(con));
1713
1714 if (httpPrintf(HTTP(con), "\r\n") < 0)
1715 return (cupsdCloseClient(con));
1716
1717 cupsdLogRequest(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 (!cupsdSendError(con, HTTP_FORBIDDEN))
1732 return (cupsdCloseClient(con));
1733
1734 break;
1735 }
1736 else if ((filename = get_file(con, &filestats, buf,
1737 sizeof(buf))) == NULL)
1738 {
1739 if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html"))
1740 return (cupsdCloseClient(con));
1741
1742 cupsdLogRequest(con, HTTP_NOT_FOUND);
1743 }
1744 else if (!check_if_modified(con, &filestats))
1745 {
1746 if (!cupsdSendError(con, HTTP_NOT_MODIFIED))
1747 return (cupsdCloseClient(con));
1748
1749 cupsdLogRequest(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 (!cupsdSendHeader(con, HTTP_OK, line))
1764 return (cupsdCloseClient(con));
1765
1766 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1767 httpGetDateString(filestats.st_mtime)) < 0)
1768 return (cupsdCloseClient(con));
1769
1770 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1771 (unsigned long)filestats.st_size) < 0)
1772 return (cupsdCloseClient(con));
1773
1774 cupsdLogRequest(con, HTTP_OK);
1775 }
1776
1777 if (httpPrintf(HTTP(con), "\r\n") < 0)
1778 return (cupsdCloseClient(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 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: %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 (cupsdCloseClient(con));
1803 else if (bytes > 0)
1804 {
1805 con->bytes += bytes;
1806
1807 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: %d writing %d bytes to %d",
1808 con->http.fd, bytes, con->file);
1809
1810 if (write(con->file, line, bytes) < bytes)
1811 {
1812 cupsdLogMessage(L_ERROR, "cupsdReadClient: Unable to write %d bytes to %s: %s",
1813 bytes, con->filename, strerror(errno));
1814
1815 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: Closing data file %d...",
1816 con->file);
1817
1818 close(con->file);
1819 con->file = -1;
1820 unlink(con->filename);
1821 cupsdClearString(&con->filename);
1822
1823 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1824 return (cupsdCloseClient(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 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: %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 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: %d Removing temp file %s",
1850 con->http.fd, con->filename);
1851 unlink(con->filename);
1852 cupsdClearString(&con->filename);
1853
1854 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1855 return (cupsdCloseClient(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 (!cupsdSendError(con, status))
1869 return (cupsdCloseClient(con));
1870 }
1871 break;
1872
1873 case HTTP_POST_RECV :
1874 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: %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 cupsdLogMessage(L_ERROR, "cupsdReadClient: %d IPP Read Error!",
1888 con->http.fd);
1889
1890 cupsdSendError(con, HTTP_BAD_REQUEST);
1891 return (cupsdCloseClient(con));
1892 }
1893 else if (ipp_state != IPP_DATA)
1894 {
1895 if (con->http.state == HTTP_POST_SEND)
1896 {
1897 cupsdSendError(con, HTTP_BAD_REQUEST);
1898 return (cupsdCloseClient(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 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
1914 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1915
1916 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: %d REQUEST %s=%d", con->http.fd,
1917 con->filename, con->file);
1918
1919 if (con->file < 0)
1920 {
1921 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1922 return (cupsdCloseClient(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 (cupsdCloseClient(con));
1934 else if (bytes > 0)
1935 {
1936 con->bytes += bytes;
1937
1938 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: %d writing %d bytes to %d",
1939 con->http.fd, bytes, con->file);
1940
1941 if (write(con->file, line, bytes) < bytes)
1942 {
1943 cupsdLogMessage(L_ERROR, "cupsdReadClient: Unable to write %d bytes to %s: %s",
1944 bytes, con->filename, strerror(errno));
1945
1946 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: Closing file %d...",
1947 con->file);
1948
1949 close(con->file);
1950 con->file = -1;
1951 unlink(con->filename);
1952 cupsdClearString(&con->filename);
1953
1954 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1955 return (cupsdCloseClient(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 (cupsdCloseClient(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 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: %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 cupsdLogMessage(L_DEBUG2, "cupsdReadClient: %d Removing temp file %s",
1984 con->http.fd, con->filename);
1985 unlink(con->filename);
1986 cupsdClearString(&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 (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
1999 return (cupsdCloseClient(con));
2000 }
2001
2002 if (con->command)
2003 {
2004 if (!cupsdSendCommand(con, con->command, con->options, 0))
2005 {
2006 if (!cupsdSendError(con, HTTP_NOT_FOUND))
2007 return (cupsdCloseClient(con));
2008 }
2009 else
2010 cupsdLogRequest(con, HTTP_OK);
2011 }
2012 }
2013
2014 if (con->request)
2015 return (cupsdProcessIPPRequest(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 (cupsdCloseClient(con));
2025 else
2026 return (1);
2027 }
2028
2029
2030 /*
2031 * 'cupsdSendCommand()' - Send output from a command via HTTP.
2032 */
2033
2034 int /* O - 1 on success, 0 on failure */
2035 cupsdSendCommand(cupsd_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 cupsdLogMessage(L_ERROR, "cupsdSendCommand: %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 cupsdLogMessage(L_INFO, "Started \"%s\" (pid=%d)", command, con->pipe_pid);
2063
2064 cupsdLogMessage(L_DEBUG, "cupsdSendCommand: %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 cupsdLogMessage(L_DEBUG2, "cupsdSendCommand: Adding fd %d to InputSet...", con->file);
2072 cupsdLogMessage(L_DEBUG2, "cupsdSendCommand: 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 * 'cupsdSendError()' - Send an error message via HTTP.
2089 */
2090
2091 int /* O - 1 if successful, 0 otherwise */
2092 cupsdSendError(cupsd_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 cupsdLogRequest(con, code);
2103
2104 cupsdLogMessage(L_DEBUG, "cupsdSendError: %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 (!cupsdSendHeader(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 * 'cupsdSendFile()' - Send a file via HTTP.
2173 */
2174
2175 int
2176 cupsdSendFile(cupsd_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 cupsdLogMessage(L_DEBUG, "cupsdSendFile: %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 (!cupsdSendHeader(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 cupsdLogMessage(L_DEBUG2, "cupsdSendFile: 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 * 'cupsdSendHeader()' - Send an HTTP request.
2214 */
2215
2216 int /* O - 1 on success, 0 on failure */
2217 cupsdSendHeader(cupsd_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 * 'cupsdUpdateCGI()' - Read status messages from CGI scripts and programs.
2287 */
2288
2289 void
2290 cupsdUpdateCGI(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 cupsdLogMessage(L_CRIT, "cupsdUpdateCGI: error reading from CGI error pipe - %s",
2309 strerror(errno));
2310 }
2311 }
2312
2313
2314 /*
2315 * 'cupsdWriteClient()' - Write data to a client as needed.
2316 */
2317
2318 int /* O - 1 if success, 0 if fail */
2319 cupsdWriteClient(cupsd_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 cupsdLogMessage(L_DEBUG2, "cupsdWriteClient(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 cupsdLogMessage(L_DEBUG2, "cupsdWriteClient: Read %d bytes from file %d...",
2344 bytes, con->file);
2345
2346 if (con->pipe_pid && !con->got_fields)
2347 {
2348 /*
2349 * Inspect the data for Content-Type and other fields.
2350 */
2351
2352 buf[bytes] = '\0';
2353
2354 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
2355 if (*bufptr == '\n')
2356 {
2357 /*
2358 * Send line to client...
2359 */
2360
2361 if (bufptr > buf && bufptr[-1] == '\r')
2362 bufptr[-1] = '\0';
2363 *bufptr++ = '\0';
2364
2365 cupsdLogMessage(L_DEBUG2, "Script header: %s", buf);
2366
2367 if (!con->sent_header)
2368 {
2369 /*
2370 * Handle redirection and CGI status codes...
2371 */
2372
2373 if (!strncasecmp(buf, "Location:", 9))
2374 cupsdSendHeader(con, HTTP_SEE_OTHER, NULL);
2375 else if (!strncasecmp(buf, "Status:", 7))
2376 cupsdSendHeader(con, atoi(buf + 7), NULL);
2377 else
2378 cupsdSendHeader(con, HTTP_OK, NULL);
2379
2380 if (con->http.version == HTTP_1_1)
2381 {
2382 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
2383
2384 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
2385 return (0);
2386 }
2387
2388 con->sent_header = 1;
2389 }
2390
2391 if (strncasecmp(buf, "Status:", 7))
2392 httpPrintf(HTTP(con), "%s\r\n", buf);
2393
2394 cupsdLogMessage(L_DEBUG2, "cupsdWriteClient: %d %s", con->http.fd, buf);
2395
2396 /*
2397 * Update buffer...
2398 */
2399
2400 bytes -= (bufptr - buf);
2401 memmove(buf, bufptr, bytes + 1);
2402 bufptr = buf - 1;
2403
2404 /*
2405 * See if the line was empty...
2406 */
2407
2408 if (con->field_col == 0)
2409 con->got_fields = 1;
2410 else
2411 con->field_col = 0;
2412 }
2413 else if (*bufptr != '\r')
2414 con->field_col ++;
2415
2416 cupsdLogMessage(L_DEBUG2, "cupsdWriteClient: %d bytes=%d, got_fields=%d",
2417 con->http.fd, bytes, con->got_fields);
2418
2419 if (bytes > 0 && !con->got_fields)
2420 {
2421 /*
2422 * Remaining text needs to go out...
2423 */
2424
2425 httpPrintf(HTTP(con), "%s", buf);
2426
2427 con->http.activity = time(NULL);
2428 return (1);
2429 }
2430 else if (bytes == 0)
2431 {
2432 con->http.activity = time(NULL);
2433 return (1);
2434 }
2435 }
2436
2437 if (httpWrite(HTTP(con), buf, bytes) < 0)
2438 {
2439 cupsdLogMessage(L_DEBUG2, "cupsdWriteClient: %d Write of %d bytes failed!",
2440 con->http.fd, bytes);
2441
2442 cupsdCloseClient(con);
2443 return (0);
2444 }
2445
2446 con->bytes += bytes;
2447 }
2448
2449 if (bytes <= 0)
2450 {
2451 cupsdLogMessage(L_DEBUG2, "cupsdWriteClient: %d bytes < 0", con->http.fd);
2452
2453 cupsdLogRequest(con, HTTP_OK);
2454
2455 httpFlushWrite(HTTP(con));
2456
2457 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
2458 {
2459 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
2460 {
2461 cupsdCloseClient(con);
2462 return (0);
2463 }
2464 }
2465
2466 con->http.state = HTTP_WAITING;
2467
2468 cupsdLogMessage(L_DEBUG2, "cupsdWriteClient: Removing fd %d from OutputSet...",
2469 con->http.fd);
2470
2471 FD_CLR(con->http.fd, OutputSet);
2472
2473 if (con->file >= 0)
2474 {
2475 if (FD_ISSET(con->file, InputSet))
2476 {
2477 cupsdLogMessage(L_DEBUG2, "cupsdWriteClient: Removing fd %d from InputSet...",
2478 con->file);
2479 FD_CLR(con->file, InputSet);
2480 }
2481
2482 if (con->pipe_pid)
2483 cupsdEndProcess(con->pipe_pid, 0);
2484
2485 cupsdLogMessage(L_DEBUG2, "cupsdWriteClient: %d Closing data file %d.",
2486 con->http.fd, con->file);
2487
2488 close(con->file);
2489 con->file = -1;
2490 con->pipe_pid = 0;
2491 }
2492
2493 if (con->filename)
2494 {
2495 cupsdLogMessage(L_DEBUG2, "cupsdWriteClient: %d Removing temp file %s",
2496 con->http.fd, con->filename);
2497 unlink(con->filename);
2498 cupsdClearString(&con->filename);
2499 }
2500
2501 if (con->request != NULL)
2502 {
2503 ippDelete(con->request);
2504 con->request = NULL;
2505 }
2506
2507 if (con->response != NULL)
2508 {
2509 ippDelete(con->response);
2510 con->response = NULL;
2511 }
2512
2513 cupsdClearString(&con->command);
2514 cupsdClearString(&con->options);
2515
2516 if (!con->http.keep_alive)
2517 {
2518 cupsdCloseClient(con);
2519 return (0);
2520 }
2521 }
2522 else
2523 {
2524 con->file_ready = 0;
2525
2526 if (con->pipe_pid && !FD_ISSET(con->file, InputSet))
2527 {
2528 cupsdLogMessage(L_DEBUG2, "cupsdWriteClient: Adding fd %d to InputSet...", con->file);
2529 FD_SET(con->file, InputSet);
2530 }
2531 }
2532
2533 con->http.activity = time(NULL);
2534
2535 return (1);
2536 }
2537
2538
2539 /*
2540 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
2541 */
2542
2543 static int /* O - 1 if modified since */
2544 check_if_modified(cupsd_client_t *con, /* I - Client connection */
2545 struct stat *filestats) /* I - File information */
2546 {
2547 char *ptr; /* Pointer into field */
2548 time_t date; /* Time/date value */
2549 int size; /* Size/length value */
2550
2551
2552 size = 0;
2553 date = 0;
2554 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
2555
2556 if (*ptr == '\0')
2557 return (1);
2558
2559 cupsdLogMessage(L_DEBUG2, "check_if_modified: %d If-Modified-Since=\"%s\"",
2560 con->http.fd, ptr);
2561
2562 while (*ptr != '\0')
2563 {
2564 while (isspace(*ptr) || *ptr == ';')
2565 ptr ++;
2566
2567 if (strncasecmp(ptr, "length=", 7) == 0)
2568 {
2569 ptr += 7;
2570 size = atoi(ptr);
2571
2572 while (isdigit(*ptr))
2573 ptr ++;
2574 }
2575 else if (isalpha(*ptr))
2576 {
2577 date = httpGetDateTime(ptr);
2578 while (*ptr != '\0' && *ptr != ';')
2579 ptr ++;
2580 }
2581 }
2582
2583 cupsdLogMessage(L_DEBUG2, "check_if_modified: %d sizes=%d,%d dates=%d,%d",
2584 con->http.fd, size, (int)filestats->st_size, (int)date,
2585 (int)filestats->st_mtime);
2586
2587 return ((size != filestats->st_size && size != 0) ||
2588 (date < filestats->st_mtime && date != 0) ||
2589 (size == 0 && date == 0));
2590 }
2591
2592
2593 /*
2594 * 'decode_auth()' - Decode an authorization string.
2595 */
2596
2597 static void
2598 decode_auth(cupsd_client_t *con) /* I - Client to decode to */
2599 {
2600 char *s, /* Authorization string */
2601 value[1024]; /* Value string */
2602 const char *username; /* Certificate username */
2603
2604
2605 /*
2606 * Decode the string...
2607 */
2608
2609 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
2610
2611 cupsdLogMessage(L_DEBUG2, "decode_auth(%p): Authorization string = \"%s\"",
2612 con, s);
2613
2614 if (strncmp(s, "Basic", 5) == 0)
2615 {
2616 s += 5;
2617 while (isspace(*s))
2618 s ++;
2619
2620 httpDecode64(value, s);
2621
2622 /*
2623 * Pull the username and password out...
2624 */
2625
2626 if ((s = strchr(value, ':')) == NULL)
2627 {
2628 cupsdLogMessage(L_DEBUG, "decode_auth: %d no colon in auth string \"%s\"",
2629 con->http.fd, value);
2630 return;
2631 }
2632
2633 *s++ = '\0';
2634
2635 strlcpy(con->username, value, sizeof(con->username));
2636 strlcpy(con->password, s, sizeof(con->password));
2637 }
2638 else if (strncmp(s, "Local", 5) == 0)
2639 {
2640 s += 5;
2641 while (isspace(*s))
2642 s ++;
2643
2644 if ((username = cupsdFindCert(s)) != NULL)
2645 strlcpy(con->username, username, sizeof(con->username));
2646 }
2647 else if (strncmp(s, "Digest", 5) == 0)
2648 {
2649 /*
2650 * Get the username and password from the Digest attributes...
2651 */
2652
2653 if (httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "username",
2654 value))
2655 strlcpy(con->username, value, sizeof(con->username));
2656
2657 if (httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "response",
2658 value))
2659 strlcpy(con->password, value, sizeof(con->password));
2660 }
2661
2662 cupsdLogMessage(L_DEBUG2, "decode_auth: %d username=\"%s\"",
2663 con->http.fd, con->username);
2664 }
2665
2666
2667 /*
2668 * 'get_file()' - Get a filename and state info.
2669 */
2670
2671 static char * /* O - Real filename */
2672 get_file(cupsd_client_t *con, /* I - Client connection */
2673 struct stat *filestats, /* O - File information */
2674 char *filename, /* IO - Filename buffer */
2675 int len) /* I - Buffer length */
2676 {
2677 int status; /* Status of filesystem calls */
2678 char *ptr; /* Pointer info filename */
2679 int plen; /* Remaining length after pointer */
2680
2681
2682 /*
2683 * Need to add DocumentRoot global...
2684 */
2685
2686 if (!strncmp(con->uri, "/ppd/", 5))
2687 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
2688 else if (!strncmp(con->uri, "/admin/conf/", 12))
2689 snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
2690 else if (!strncmp(con->uri, "/admin/log/", 11))
2691 {
2692 if (!strcmp(con->uri + 11, "access_log") && AccessLog[0] == '/')
2693 strlcpy(filename, AccessLog, len);
2694 else if (!strcmp(con->uri + 11, "error_log") && ErrorLog[0] == '/')
2695 strlcpy(filename, ErrorLog, len);
2696 else if (!strcmp(con->uri + 11, "page_log") && PageLog[0] == '/')
2697 strlcpy(filename, PageLog, len);
2698 else
2699 return (NULL);
2700 }
2701 else if (con->language != NULL)
2702 snprintf(filename, len, "%s/%s%s", DocumentRoot, con->language->language,
2703 con->uri);
2704 else
2705 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
2706
2707 if ((ptr = strchr(filename, '?')) != NULL)
2708 *ptr = '\0';
2709
2710 /*
2711 * Grab the status for this language; if there isn't a language-specific file
2712 * then fallback to the default one...
2713 */
2714
2715 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
2716 {
2717 /*
2718 * Drop the language prefix and try the current directory...
2719 */
2720
2721 if (strncmp(con->uri, "/ppd/", 5) &&
2722 strncmp(con->uri, "/admin/conf/", 12) &&
2723 strncmp(con->uri, "/admin/log/", 11))
2724 {
2725 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
2726
2727 if ((ptr = strchr(filename, '?')) != NULL)
2728 *ptr = '\0';
2729
2730 status = stat(filename, filestats);
2731 }
2732 }
2733
2734 /*
2735 * If we're found a directory, get the index.html file instead...
2736 */
2737
2738 if (!status && S_ISDIR(filestats->st_mode))
2739 {
2740 if (filename[strlen(filename) - 1] != '/')
2741 strlcat(filename, "/", len);
2742
2743 ptr = filename + strlen(filename);
2744 plen = len - (ptr - filename);
2745
2746 strlcpy(ptr, "index.html", plen);
2747 status = stat(filename, filestats);
2748
2749 #ifdef HAVE_JAVA
2750 if (status)
2751 {
2752 strlcpy(ptr, "index.class", plen);
2753 status = stat(filename, filestats);
2754 }
2755 #endif /* HAVE_JAVA */
2756
2757 #ifdef HAVE_PERL
2758 if (status)
2759 {
2760 strlcpy(ptr, "index.pl", plen);
2761 status = stat(filename, filestats);
2762 }
2763 #endif /* HAVE_PERL */
2764
2765 #ifdef HAVE_PHP
2766 if (status)
2767 {
2768 strlcpy(ptr, "index.php", plen);
2769 status = stat(filename, filestats);
2770 }
2771 #endif /* HAVE_PHP */
2772
2773 #ifdef HAVE_PYTHON
2774 if (status)
2775 {
2776 strlcpy(ptr, "index.pyc", plen);
2777 status = stat(filename, filestats);
2778 }
2779
2780 if (status)
2781 {
2782 strlcpy(ptr, "index.py", plen);
2783 status = stat(filename, filestats);
2784 }
2785 #endif /* HAVE_PYTHON */
2786 }
2787
2788 cupsdLogMessage(L_DEBUG2, "get_file: %d filename=%s size=%d",
2789 con->http.fd, filename, status ? -1 : (int)filestats->st_size);
2790
2791 if (!status)
2792 con->http.data_remaining = (int)filestats->st_size;
2793
2794 if (status)
2795 return (NULL);
2796 else
2797 return (filename);
2798 }
2799
2800
2801 /*
2802 * 'install_conf_file()' - Install a configuration file.
2803 */
2804
2805 static http_status_t /* O - Status */
2806 install_conf_file(cupsd_client_t *con) /* I - Connection */
2807 {
2808 cups_file_t *in, /* Input file */
2809 *out; /* Output file */
2810 char buffer[1024]; /* Copy buffer */
2811 int bytes; /* Number of bytes */
2812 char conffile[1024], /* Configuration filename */
2813 newfile[1024], /* New config filename */
2814 oldfile[1024]; /* Old config filename */
2815 struct stat confinfo; /* Config file info */
2816
2817
2818 /*
2819 * First construct the filenames...
2820 */
2821
2822 snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11);
2823 snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11);
2824 snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11);
2825
2826 cupsdLogMessage(L_INFO, "Installing config file \"%s\"...", conffile);
2827
2828 /*
2829 * Get the owner, group, and permissions of the configuration file.
2830 * If it doesn't exist, assign it to the User and Group in the
2831 * cupsd.conf file with mode 0640 permissions.
2832 */
2833
2834 if (stat(conffile, &confinfo))
2835 {
2836 confinfo.st_uid = User;
2837 confinfo.st_gid = Group;
2838 confinfo.st_mode = ConfigFilePerm;
2839 }
2840
2841 /*
2842 * Open the request file and new config file...
2843 */
2844
2845 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
2846 {
2847 cupsdLogMessage(L_ERROR, "Unable to open request file \"%s\" - %s",
2848 con->filename, strerror(errno));
2849 return (HTTP_SERVER_ERROR);
2850 }
2851
2852 if ((out = cupsFileOpen(newfile, "wb")) == NULL)
2853 {
2854 cupsFileClose(in);
2855 cupsdLogMessage(L_ERROR, "Unable to open config file \"%s\" - %s",
2856 newfile, strerror(errno));
2857 return (HTTP_SERVER_ERROR);
2858 }
2859
2860 fchmod(cupsFileNumber(out), confinfo.st_mode);
2861 fchown(cupsFileNumber(out), confinfo.st_uid, confinfo.st_gid);
2862
2863 /*
2864 * Copy from the request to the new config file...
2865 */
2866
2867 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
2868 if (cupsFileWrite(out, buffer, bytes) < bytes)
2869 {
2870 cupsdLogMessage(L_ERROR, "Unable to copy to config file \"%s\" - %s",
2871 newfile, strerror(errno));
2872
2873 cupsFileClose(in);
2874 cupsFileClose(out);
2875 unlink(newfile);
2876
2877 return (HTTP_SERVER_ERROR);
2878 }
2879
2880 /*
2881 * Close the files...
2882 */
2883
2884 cupsFileClose(in);
2885 if (cupsFileClose(out))
2886 {
2887 cupsdLogMessage(L_ERROR, "Error file closing config file \"%s\" - %s",
2888 newfile, strerror(errno));
2889
2890 unlink(newfile);
2891
2892 return (HTTP_SERVER_ERROR);
2893 }
2894
2895 /*
2896 * Remove the request file...
2897 */
2898
2899 unlink(con->filename);
2900 cupsdClearString(&con->filename);
2901
2902 /*
2903 * Unlink the old backup, rename the current config file to the backup
2904 * filename, and rename the new config file to the config file name...
2905 */
2906
2907 if (unlink(oldfile))
2908 if (errno != ENOENT)
2909 {
2910 cupsdLogMessage(L_ERROR, "Unable to remove backup config file \"%s\" - %s",
2911 oldfile, strerror(errno));
2912
2913 unlink(newfile);
2914
2915 return (HTTP_SERVER_ERROR);
2916 }
2917
2918 if (rename(conffile, oldfile))
2919 if (errno != ENOENT)
2920 {
2921 cupsdLogMessage(L_ERROR, "Unable to rename old config file \"%s\" - %s",
2922 conffile, strerror(errno));
2923
2924 unlink(newfile);
2925
2926 return (HTTP_SERVER_ERROR);
2927 }
2928
2929 if (rename(newfile, conffile))
2930 {
2931 cupsdLogMessage(L_ERROR, "Unable to rename new config file \"%s\" - %s",
2932 newfile, strerror(errno));
2933
2934 rename(oldfile, conffile);
2935 unlink(newfile);
2936
2937 return (HTTP_SERVER_ERROR);
2938 }
2939
2940 /*
2941 * If the cupsd.conf file was updated, set the NeedReload flag...
2942 */
2943
2944 if (!strcmp(con->uri, "/admin/conf/cupsd.conf"))
2945 NeedReload = RELOAD_CUPSD;
2946 else
2947 NeedReload = RELOAD_ALL;
2948
2949 ReloadTime = time(NULL);
2950
2951 /*
2952 * Return that the file was created successfully...
2953 */
2954
2955 return (HTTP_CREATED);
2956 }
2957
2958
2959 /*
2960 * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. "..").
2961 */
2962
2963 static int /* O - 0 if relative, 1 if absolute */
2964 is_path_absolute(const char *path) /* I - Input path */
2965 {
2966 /*
2967 * Check for a leading slash...
2968 */
2969
2970 if (path[0] != '/')
2971 return (0);
2972
2973 /*
2974 * Check for "/.." in the path...
2975 */
2976
2977 while ((path = strstr(path, "/..")) != NULL)
2978 {
2979 if (!path[3] || path[3] == '/')
2980 return (0);
2981
2982 path ++;
2983 }
2984
2985 /*
2986 * If we haven't found any relative paths, return 1 indicating an
2987 * absolute path...
2988 */
2989
2990 return (1);
2991 }
2992
2993
2994 /*
2995 * 'pipe_command()' - Pipe the output of a command to the remote client.
2996 */
2997
2998 static int /* O - Process ID */
2999 pipe_command(cupsd_client_t *con, /* I - Client connection */
3000 int infile, /* I - Standard input for command */
3001 int *outfile, /* O - Standard output for command */
3002 char *command, /* I - Command to run */
3003 char *options, /* I - Options for command */
3004 int root) /* I - Run as root? */
3005 {
3006 int i; /* Looping var */
3007 int pid; /* Process ID */
3008 char *commptr; /* Command string pointer */
3009 char *uriptr; /* URI string pointer */
3010 int fds[2]; /* Pipe FDs */
3011 int argc; /* Number of arguments */
3012 int envc; /* Number of environment variables */
3013 char argbuf[10240], /* Argument buffer */
3014 *argv[100], /* Argument strings */
3015 *envp[100]; /* Environment variables */
3016 char content_length[1024], /* CONTENT_LENGTH environment variable */
3017 content_type[1024], /* CONTENT_TYPE environment variable */
3018 http_cookie[1024], /* HTTP_COOKIE environment variable */
3019 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
3020 lang[1024], /* LANG environment variable */
3021 *query_string, /* QUERY_STRING env variable */
3022 remote_addr[1024], /* REMOTE_ADDR environment variable */
3023 remote_host[1024], /* REMOTE_HOST environment variable */
3024 remote_user[1024], /* REMOTE_USER environment variable */
3025 script_name[1024], /* SCRIPT_NAME environment variable */
3026 server_name[1024], /* SERVER_NAME environment variable */
3027 server_port[1024]; /* SERVER_PORT environment variable */
3028 static const char * const locale_encodings[] =
3029 { /* Locale charset names */
3030 "ASCII", "ISO8859-1", "ISO8859-2", "ISO8859-3",
3031 "ISO8859-4", "ISO8859-5", "ISO8859-6", "ISO8859-7",
3032 "ISO8859-8", "ISO8859-9", "ISO8859-10", "UTF-8",
3033 "ISO8859-13", "ISO8859-14", "ISO8859-15", "CP874",
3034 "CP1250", "CP1251", "CP1252", "CP1253",
3035 "CP1254", "CP1255", "CP1256", "CP1257",
3036 "CP1258", "KOI8R", "KOI8U", "ISO8859-11",
3037 "ISO8859-16", "", "", "",
3038
3039 "", "", "", "",
3040 "", "", "", "",
3041 "", "", "", "",
3042 "", "", "", "",
3043 "", "", "", "",
3044 "", "", "", "",
3045 "", "", "", "",
3046 "", "", "", "",
3047
3048 "CP932", "CP936", "CP949", "CP950",
3049 "CP1361", "", "", "",
3050 "", "", "", "",
3051 "", "", "", "",
3052 "", "", "", "",
3053 "", "", "", "",
3054 "", "", "", "",
3055 "", "", "", "",
3056
3057 "", "", "", "",
3058 "", "", "", "",
3059 "", "", "", "",
3060 "", "", "", "",
3061 "", "", "", "",
3062 "", "", "", "",
3063 "", "", "", "",
3064 "", "", "", "",
3065
3066 "EUC-CN", "EUC-JP", "EUC-KR", "EUC-TW"
3067 };
3068
3069
3070 /*
3071 * Parse a copy of the options string, which is of the form:
3072 *
3073 * name argument+argument+argument
3074 * name?argument+argument+argument
3075 * name param=value&param=value
3076 * name?param=value&param=value
3077 *
3078 * If the string contains an "=" character after the initial name,
3079 * then we treat it as a HTTP GET form request and make a copy of
3080 * the remaining string for the environment variable.
3081 *
3082 * The string is always parsed out as command-line arguments, to
3083 * be consistent with Apache...
3084 */
3085
3086 cupsdLogMessage(L_DEBUG2, "pipe_command: command=\"%s\", options=\"%s\"",
3087 command, options);
3088
3089 strlcpy(argbuf, options, sizeof(argbuf));
3090
3091 argv[0] = argbuf;
3092 query_string = NULL;
3093
3094 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
3095 {
3096 /*
3097 * Break arguments whenever we see a + or space...
3098 */
3099
3100 if (*commptr == ' ' || *commptr == '+' || (*commptr == '?' && argc == 1))
3101 {
3102 /*
3103 * Terminate the current string and skip trailing whitespace...
3104 */
3105
3106 *commptr++ = '\0';
3107
3108 while (*commptr == ' ')
3109 commptr ++;
3110
3111 /*
3112 * If we don't have a blank string, save it as another argument...
3113 */
3114
3115 if (*commptr)
3116 {
3117 argv[argc] = commptr;
3118 argc ++;
3119 }
3120 else
3121 break;
3122
3123 /*
3124 * If we see an "=" in the remaining string, make a copy of it since
3125 * it will be query data...
3126 */
3127
3128 if (argc == 2 && strchr(commptr, '=') && con->operation == HTTP_GET)
3129 cupsdSetStringf(&query_string, "QUERY_STRING=%s", commptr);
3130
3131 /*
3132 * Don't skip the first non-blank character...
3133 */
3134
3135 commptr --;
3136 }
3137 else if (*commptr == '%' && isxdigit(commptr[1] & 255) &&
3138 isxdigit(commptr[2] & 255))
3139 {
3140 /*
3141 * Convert the %xx notation to the individual character.
3142 */
3143
3144 if (commptr[1] >= '0' && commptr[1] <= '9')
3145 *commptr = (commptr[1] - '0') << 4;
3146 else
3147 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
3148
3149 if (commptr[2] >= '0' && commptr[2] <= '9')
3150 *commptr |= commptr[2] - '0';
3151 else
3152 *commptr |= tolower(commptr[2]) - 'a' + 10;
3153
3154 _cups_strcpy(commptr + 1, commptr + 3);
3155
3156 /*
3157 * Check for a %00 and break if that is the case...
3158 */
3159
3160 if (!*commptr)
3161 break;
3162 }
3163 }
3164
3165 argv[argc] = NULL;
3166
3167 if (argv[0][0] == '\0')
3168 argv[0] = strrchr(command, '/') + 1;
3169
3170 /*
3171 * Setup the environment variables as needed...
3172 */
3173
3174 if (con->language)
3175 snprintf(lang, sizeof(lang), "LANG=%s.%s", con->language->language,
3176 locale_encodings[con->language->encoding]);
3177 else
3178 strcpy(lang, "LANG=C");
3179
3180 strcpy(remote_addr, "REMOTE_ADDR=");
3181 httpAddrString(&(con->http.hostaddr), remote_addr + 12,
3182 sizeof(remote_addr) - 12);
3183
3184 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s",
3185 con->http.hostname);
3186
3187 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
3188 if ((uriptr = strchr(script_name, '?')) != NULL)
3189 *uriptr = '\0';
3190
3191 sprintf(server_port, "SERVER_PORT=%d", con->serverport);
3192
3193 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
3194 con->servername);
3195
3196 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
3197
3198 envp[envc ++] = lang;
3199 envp[envc ++] = "REDIRECT_STATUS=1";
3200 envp[envc ++] = server_name;
3201 envp[envc ++] = server_port;
3202 envp[envc ++] = remote_addr;
3203 envp[envc ++] = remote_host;
3204 envp[envc ++] = script_name;
3205
3206 if (con->username[0])
3207 {
3208 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
3209
3210 envp[envc ++] = remote_user;
3211 }
3212
3213 if (con->http.version == HTTP_1_1)
3214 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
3215 else if (con->http.version == HTTP_1_0)
3216 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
3217 else
3218 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
3219
3220 if (con->http.cookie)
3221 {
3222 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
3223 con->http.cookie);
3224 envp[envc ++] = http_cookie;
3225 }
3226
3227 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
3228 {
3229 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
3230 con->http.fields[HTTP_FIELD_USER_AGENT]);
3231 envp[envc ++] = http_user_agent;
3232 }
3233
3234 if (con->operation == HTTP_GET)
3235 {
3236 for (i = 0; i < argc; i ++)
3237 cupsdLogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
3238
3239 envp[envc ++] = "REQUEST_METHOD=GET";
3240
3241 if (query_string)
3242 {
3243 /*
3244 * Add GET form variables after ?...
3245 */
3246
3247 envp[envc ++] = query_string;
3248 }
3249 }
3250 else
3251 {
3252 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
3253 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
3254 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
3255
3256 envp[envc ++] = "REQUEST_METHOD=POST";
3257 envp[envc ++] = content_length;
3258 envp[envc ++] = content_type;
3259 }
3260
3261 /*
3262 * Tell the CGI if we are using encryption...
3263 */
3264
3265 if (con->http.encryption == HTTP_ENCRYPT_ALWAYS)
3266 envp[envc ++] = "HTTPS=ON";
3267
3268 /*
3269 * Terminate the environment array...
3270 */
3271
3272 envp[envc] = NULL;
3273
3274 if (LogLevel == L_DEBUG2)
3275 {
3276 for (i = 0; i < argc; i ++)
3277 cupsdLogMessage(L_DEBUG2, "pipe_command: argv[%d] = \"%s\"", i, argv[i]);
3278 for (i = 0; i < envc; i ++)
3279 cupsdLogMessage(L_DEBUG2, "pipe_command: envp[%d] = \"%s\"", i, envp[i]);
3280 }
3281
3282 /*
3283 * Create a pipe for the output...
3284 */
3285
3286 if (cupsdOpenPipe(fds))
3287 {
3288 cupsdClearString(&query_string);
3289
3290 cupsdLogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
3291 argv[0], strerror(errno));
3292 return (0);
3293 }
3294
3295 /*
3296 * Then execute the command...
3297 */
3298
3299 if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1],
3300 -1, root, &pid) < 0)
3301 {
3302 /*
3303 * Error - can't fork!
3304 */
3305
3306 cupsdLogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
3307 strerror(errno));
3308
3309 cupsdClosePipe(fds);
3310 pid = 0;
3311 }
3312 else
3313 {
3314 /*
3315 * Fork successful - return the PID...
3316 */
3317
3318 if (con->username[0])
3319 cupsdAddCert(pid, con->username);
3320
3321 cupsdLogMessage(L_DEBUG, "CGI %s started - PID = %d", command, pid);
3322
3323 *outfile = fds[0];
3324 close(fds[1]);
3325 }
3326
3327 cupsdClearString(&query_string);
3328
3329 return (pid);
3330 }
3331
3332
3333 #if defined(HAVE_CDSASSL)
3334 /*
3335 * 'CDSAReadFunc()' - Read function for CDSA decryption code.
3336 */
3337
3338 static OSStatus /* O - -1 on error, 0 on success */
3339 CDSAReadFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
3340 void *data, /* I - Data buffer */
3341 size_t *dataLength) /* IO - Number of bytes */
3342 {
3343 ssize_t bytes; /* Number of bytes read */
3344
3345
3346 bytes = recv((int)connection, data, *dataLength, 0);
3347 if (bytes >= 0)
3348 {
3349 *dataLength = bytes;
3350 return (0);
3351 }
3352 else
3353 return (-1);
3354 }
3355
3356
3357 /*
3358 * 'CDSAWriteFunc()' - Write function for CDSA encryption code.
3359 */
3360
3361 static OSStatus /* O - -1 on error, 0 on success */
3362 CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
3363 const void *data, /* I - Data buffer */
3364 size_t *dataLength) /* IO - Number of bytes */
3365 {
3366 ssize_t bytes;
3367
3368
3369 bytes = write((int)connection, data, *dataLength);
3370 if (bytes >= 0)
3371 {
3372 *dataLength = bytes;
3373 return (0);
3374 }
3375 else
3376 return (-1);
3377 }
3378 #endif /* HAVE_CDSASSL */
3379
3380
3381 /*
3382 * End of "$Id$".
3383 */