]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/client.c
Merge changes from CUPS 1.5b1-r9798.
[thirdparty/cups.git] / scheduler / client.c
1 /*
2 * "$Id: client.c 7950 2008-09-17 00:21:59Z mike $"
3 *
4 * Client routines for the CUPS scheduler.
5 *
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
9 * This file contains Kerberos support code, copyright 2006 by
10 * Jelmer Vernooij.
11 *
12 * These coded instructions, statements, and computer programs are the
13 * property of Apple Inc. and are protected by Federal copyright
14 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
15 * which should have been included with this file. If this file is
16 * file is missing or damaged, see the license at "http://www.cups.org/".
17 *
18 * Contents:
19 *
20 * cupsdAcceptClient() - Accept a new client.
21 * cupsdCloseAllClients() - Close all remote clients immediately.
22 * cupsdCloseClient() - Close a remote client.
23 * cupsdFlushHeader() - Flush the header fields to the client.
24 * cupsdReadClient() - Read data from a client.
25 * cupsdSendCommand() - Send output from a command via HTTP.
26 * cupsdSendError() - Send an error message via HTTP.
27 * cupsdSendHeader() - Send an HTTP request.
28 * cupsdUpdateCGI() - Read status messages from CGI scripts and
29 * programs.
30 * cupsdWriteClient() - Write data to a client as needed.
31 * check_if_modified() - Decode an "If-Modified-Since" line.
32 * compare_clients() - Compare two client connections.
33 * data_ready() - Check whether data is available from a client.
34 * encrypt_client() - Enable encryption for the client...
35 * get_cdsa_certificate() - Get a SSL/TLS certificate from the System
36 * keychain.
37 * get_file() - Get a filename and state info.
38 * install_conf_file() - Install a configuration file.
39 * is_cgi() - Is the resource a CGI script/program?
40 * is_path_absolute() - Is a path absolute and free of relative elements
41 * (i.e. "..").
42 * make_certificate() - Make a self-signed SSL/TLS certificate.
43 * pipe_command() - Pipe the output of a command to the remote client.
44 * valid_host() - Is the Host: field valid?
45 * write_file() - Send a file via HTTP.
46 * write_pipe() - Flag that data is available on the CGI pipe.
47 */
48
49 /*
50 * Include necessary headers...
51 */
52
53 #include "cupsd.h"
54
55 #ifdef HAVE_TCPD_H
56 # include <tcpd.h>
57 #endif /* HAVE_TCPD_H */
58
59
60 /*
61 * Local functions...
62 */
63
64 static int check_if_modified(cupsd_client_t *con,
65 struct stat *filestats);
66 static int compare_clients(cupsd_client_t *a, cupsd_client_t *b,
67 void *data);
68 static int data_ready(cupsd_client_t *con);
69 #ifdef HAVE_SSL
70 static int encrypt_client(cupsd_client_t *con);
71 #endif /* HAVE_SSL */
72 #ifdef HAVE_CDSASSL
73 static CFArrayRef get_cdsa_certificate(cupsd_client_t *con);
74 #endif /* HAVE_CDSASSL */
75 static char *get_file(cupsd_client_t *con, struct stat *filestats,
76 char *filename, int len);
77 static http_status_t install_conf_file(cupsd_client_t *con);
78 static int is_cgi(cupsd_client_t *con, const char *filename,
79 struct stat *filestats, mime_type_t *type);
80 static int is_path_absolute(const char *path);
81 #ifdef HAVE_SSL
82 static int make_certificate(cupsd_client_t *con);
83 #endif /* HAVE_SSL */
84 static int pipe_command(cupsd_client_t *con, int infile, int *outfile,
85 char *command, char *options, int root);
86 static int valid_host(cupsd_client_t *con);
87 static int write_file(cupsd_client_t *con, http_status_t code,
88 char *filename, char *type,
89 struct stat *filestats);
90 static void write_pipe(cupsd_client_t *con);
91
92
93 /*
94 * 'cupsdAcceptClient()' - Accept a new client.
95 */
96
97 void
98 cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
99 {
100 int count; /* Count of connections on a host */
101 int val; /* Parameter value */
102 cupsd_client_t *con, /* New client pointer */
103 *tempcon; /* Temporary client pointer */
104 http_addrlist_t *addrlist, /* List of adddresses for host */
105 *addr; /* Current address */
106 socklen_t addrlen; /* Length of address */
107 char *hostname; /* Hostname for address */
108 http_addr_t temp; /* Temporary address variable */
109 static time_t last_dos = 0; /* Time of last DoS attack */
110 #ifdef HAVE_TCPD_H
111 struct request_info wrap_req; /* TCP wrappers request information */
112 #endif /* HAVE_TCPD_H */
113
114
115 cupsdLogMessage(CUPSD_LOG_DEBUG2,
116 "cupsdAcceptClient(lis=%p(%d)) Clients=%d",
117 lis, lis->fd, cupsArrayCount(Clients));
118
119 /*
120 * Make sure we don't have a full set of clients already...
121 */
122
123 if (cupsArrayCount(Clients) == MaxClients)
124 return;
125
126 /*
127 * Get a pointer to the next available client...
128 */
129
130 if (!Clients)
131 Clients = cupsArrayNew(NULL, NULL);
132
133 if (!Clients)
134 {
135 cupsdLogMessage(CUPSD_LOG_ERROR,
136 "Unable to allocate memory for clients array!");
137 cupsdPauseListening();
138 return;
139 }
140
141 if (!ActiveClients)
142 ActiveClients = cupsArrayNew((cups_array_func_t)compare_clients, NULL);
143
144 if (!ActiveClients)
145 {
146 cupsdLogMessage(CUPSD_LOG_ERROR,
147 "Unable to allocate memory for active clients array!");
148 cupsdPauseListening();
149 return;
150 }
151
152 if ((con = calloc(1, sizeof(cupsd_client_t))) == NULL)
153 {
154 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate memory for client!");
155 cupsdPauseListening();
156 return;
157 }
158
159 con->http.activity = time(NULL);
160 con->file = -1;
161 con->http.hostaddr = &(con->clientaddr);
162
163 /*
164 * Accept the client and get the remote address...
165 */
166
167 addrlen = sizeof(http_addr_t);
168
169 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)con->http.hostaddr,
170 &addrlen)) < 0)
171 {
172 if (errno == ENFILE || errno == EMFILE)
173 cupsdPauseListening();
174
175 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to accept client connection - %s.",
176 strerror(errno));
177 free(con);
178
179 return;
180 }
181
182 /*
183 * Save the connected port number...
184 */
185
186 _httpAddrSetPort(con->http.hostaddr, _httpAddrPort(&(lis->address)));
187
188 #ifdef AF_INET6
189 /*
190 * Convert IPv4 over IPv6 addresses (::ffff:n.n.n.n) to IPv4 forms we
191 * can more easily use...
192 */
193
194 if (lis->address.addr.sa_family == AF_INET6 &&
195 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[0] == 0 &&
196 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[1] == 0 &&
197 ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2]) == 0xffff)
198 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2] = 0;
199 #endif /* AF_INET6 */
200
201 /*
202 * Check the number of clients on the same address...
203 */
204
205 for (count = 0, tempcon = (cupsd_client_t *)cupsArrayFirst(Clients);
206 tempcon;
207 tempcon = (cupsd_client_t *)cupsArrayNext(Clients))
208 if (httpAddrEqual(tempcon->http.hostaddr, con->http.hostaddr))
209 {
210 count ++;
211 if (count >= MaxClientsPerHost)
212 break;
213 }
214
215 if (count >= MaxClientsPerHost)
216 {
217 if ((time(NULL) - last_dos) >= 60)
218 {
219 last_dos = time(NULL);
220 cupsdLogMessage(CUPSD_LOG_WARN,
221 "Possible DoS attack - more than %d clients connecting "
222 "from %s!",
223 MaxClientsPerHost,
224 httpAddrString(con->http.hostaddr, con->http.hostname,
225 sizeof(con->http.hostname)));
226 }
227
228 #ifdef WIN32
229 closesocket(con->http.fd);
230 #else
231 close(con->http.fd);
232 #endif /* WIN32 */
233
234 free(con);
235 return;
236 }
237
238 /*
239 * Get the hostname or format the IP address as needed...
240 */
241
242 if (httpAddrLocalhost(con->http.hostaddr))
243 {
244 /*
245 * Map accesses from the loopback interface to "localhost"...
246 */
247
248 strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname));
249 hostname = con->http.hostname;
250 }
251 else
252 {
253 /*
254 * Map accesses from the same host to the server name.
255 */
256
257 if (HostNameLookups)
258 hostname = httpAddrLookup(con->http.hostaddr, con->http.hostname,
259 sizeof(con->http.hostname));
260 else
261 {
262 hostname = NULL;
263 httpAddrString(con->http.hostaddr, con->http.hostname,
264 sizeof(con->http.hostname));
265 }
266 }
267
268 if (hostname == NULL && HostNameLookups == 2)
269 {
270 /*
271 * Can't have an unresolved IP address with double-lookups enabled...
272 */
273
274 #ifdef WIN32
275 closesocket(con->http.fd);
276 #else
277 close(con->http.fd);
278 #endif /* WIN32 */
279
280 cupsdLogMessage(CUPSD_LOG_WARN,
281 "Name lookup failed - connection from %s closed!",
282 con->http.hostname);
283
284 free(con);
285 return;
286 }
287
288 if (HostNameLookups == 2)
289 {
290 /*
291 * Do double lookups as needed...
292 */
293
294 if ((addrlist = httpAddrGetList(con->http.hostname, AF_UNSPEC, NULL))
295 != NULL)
296 {
297 /*
298 * See if the hostname maps to the same IP address...
299 */
300
301 for (addr = addrlist; addr; addr = addr->next)
302 if (httpAddrEqual(con->http.hostaddr, &(addr->addr)))
303 break;
304 }
305 else
306 addr = NULL;
307
308 httpAddrFreeList(addrlist);
309
310 if (!addr)
311 {
312 /*
313 * Can't have a hostname that doesn't resolve to the same IP address
314 * with double-lookups enabled...
315 */
316
317 #ifdef WIN32
318 closesocket(con->http.fd);
319 #else
320 close(con->http.fd);
321 #endif /* WIN32 */
322
323 cupsdLogMessage(CUPSD_LOG_WARN,
324 "IP lookup failed - connection from %s closed!",
325 con->http.hostname);
326 free(con);
327 return;
328 }
329 }
330
331 #ifdef HAVE_TCPD_H
332 /*
333 * See if the connection is denied by TCP wrappers...
334 */
335
336 request_init(&wrap_req, RQ_DAEMON, "cupsd", RQ_FILE, con->http.fd, NULL);
337 fromhost(&wrap_req);
338
339 if (!hosts_access(&wrap_req))
340 {
341 #ifdef WIN32
342 closesocket(con->http.fd);
343 #else
344 close(con->http.fd);
345 #endif /* WIN32 */
346
347 cupsdLogMessage(CUPSD_LOG_WARN,
348 "Connection from %s refused by /etc/hosts.allow and "
349 "/etc/hosts.deny rules.", con->http.hostname);
350 free(con);
351 return;
352 }
353 #endif /* HAVE_TCPD_H */
354
355 #ifdef AF_LOCAL
356 if (con->http.hostaddr->addr.sa_family == AF_LOCAL)
357 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s (Domain)",
358 con->http.fd, con->http.hostname);
359 else
360 #endif /* AF_LOCAL */
361 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv%d)",
362 con->http.fd, con->http.hostname,
363 _httpAddrPort(con->http.hostaddr),
364 _httpAddrFamily(con->http.hostaddr) == AF_INET ? 4 : 6);
365
366 /*
367 * Get the local address the client connected to...
368 */
369
370 addrlen = sizeof(temp);
371 if (getsockname(con->http.fd, (struct sockaddr *)&temp, &addrlen))
372 {
373 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get local address - %s",
374 strerror(errno));
375
376 strcpy(con->servername, "localhost");
377 con->serverport = LocalPort;
378 }
379 #ifdef AF_LOCAL
380 else if (_httpAddrFamily(&temp) == AF_LOCAL)
381 {
382 strcpy(con->servername, "localhost");
383 con->serverport = LocalPort;
384 }
385 #endif /* AF_LOCAL */
386 else
387 {
388 if (httpAddrLocalhost(&temp))
389 strlcpy(con->servername, "localhost", sizeof(con->servername));
390 else if (HostNameLookups || RemotePort)
391 httpAddrLookup(&temp, con->servername, sizeof(con->servername));
392 else
393 httpAddrString(&temp, con->servername, sizeof(con->servername));
394
395 con->serverport = _httpAddrPort(&(lis->address));
396 }
397
398 cupsArrayAdd(Clients, con);
399
400 /*
401 * Using TCP_NODELAY improves responsiveness, especially on systems with a slow
402 * loopback interface. Since we write large buffers when sending print files
403 * and requests there shouldn't be any performance penalty for this...
404 */
405
406 val = 1;
407 setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
408
409 /*
410 * Close this file on all execs...
411 */
412
413 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
414
415 /*
416 * Add the socket to the server select.
417 */
418
419 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
420
421 /*
422 * Temporarily suspend accept()'s until we lose a client...
423 */
424
425 if (cupsArrayCount(Clients) == MaxClients)
426 cupsdPauseListening();
427
428 #ifdef HAVE_SSL
429 /*
430 * See if we are connecting on a secure port...
431 */
432
433 if (lis->encryption == HTTP_ENCRYPT_ALWAYS)
434 {
435 /*
436 * https connection; go secure...
437 */
438
439 con->http.encryption = HTTP_ENCRYPT_ALWAYS;
440
441 if (!encrypt_client(con))
442 cupsdCloseClient(con);
443 }
444 else
445 con->auto_ssl = 1;
446 #endif /* HAVE_SSL */
447 }
448
449
450 /*
451 * 'cupsdCloseAllClients()' - Close all remote clients immediately.
452 */
453
454 void
455 cupsdCloseAllClients(void)
456 {
457 cupsd_client_t *con; /* Current client */
458
459
460 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d",
461 cupsArrayCount(Clients));
462
463 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
464 con;
465 con = (cupsd_client_t *)cupsArrayNext(Clients))
466 if (cupsdCloseClient(con))
467 cupsdCloseClient(con);
468 }
469
470
471 /*
472 * 'cupsdCloseClient()' - Close a remote client.
473 */
474
475 int /* O - 1 if partial close, 0 if fully closed */
476 cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */
477 {
478 int partial; /* Do partial close for SSL? */
479 #ifdef HAVE_LIBSSL
480 SSL_CTX *context; /* Context for encryption */
481 unsigned long error; /* Error code */
482 #elif defined(HAVE_GNUTLS)
483 int error; /* Error code */
484 gnutls_certificate_server_credentials *credentials;
485 /* TLS credentials */
486 # elif defined(HAVE_CDSASSL)
487 #endif /* HAVE_LIBSSL */
488
489
490 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCloseClient: %d", con->http.fd);
491
492 /*
493 * Flush pending writes before closing...
494 */
495
496 httpFlushWrite(HTTP(con));
497
498 partial = 0;
499
500 #ifdef HAVE_SSL
501 /*
502 * Shutdown encryption as needed...
503 */
504
505 if (con->http.tls)
506 {
507 partial = 1;
508
509 # ifdef HAVE_LIBSSL
510 context = SSL_get_SSL_CTX(con->http.tls);
511
512 switch (SSL_shutdown(con->http.tls))
513 {
514 case 1 :
515 cupsdLogMessage(CUPSD_LOG_DEBUG,
516 "SSL shutdown successful!");
517 break;
518 case -1 :
519 cupsdLogMessage(CUPSD_LOG_ERROR,
520 "Fatal error during SSL shutdown!");
521 default :
522 while ((error = ERR_get_error()) != 0)
523 cupsdLogMessage(CUPSD_LOG_ERROR, "SSL shutdown failed: %s",
524 ERR_error_string(error, NULL));
525 break;
526 }
527
528 SSL_CTX_free(context);
529 SSL_free(con->http.tls);
530
531 # elif defined(HAVE_GNUTLS)
532 credentials = (gnutls_certificate_server_credentials *)(con->http.tls_credentials);
533
534 error = gnutls_bye(con->http.tls, GNUTLS_SHUT_WR);
535 switch (error)
536 {
537 case GNUTLS_E_SUCCESS:
538 cupsdLogMessage(CUPSD_LOG_DEBUG,
539 "SSL shutdown successful!");
540 break;
541 default:
542 cupsdLogMessage(CUPSD_LOG_ERROR,
543 "SSL shutdown failed: %s", gnutls_strerror(error));
544 break;
545 }
546
547 gnutls_deinit(con->http.tls);
548 gnutls_certificate_free_credentials(*credentials);
549 free(credentials);
550
551 # elif defined(HAVE_CDSASSL)
552 while (SSLClose(con->http.tls) == errSSLWouldBlock)
553 usleep(1000);
554
555 SSLDisposeContext(con->http.tls);
556
557 if (con->http.tls_credentials)
558 CFRelease(con->http.tls_credentials);
559
560 # endif /* HAVE_LIBSSL */
561
562 con->http.tls = NULL;
563 }
564 #endif /* HAVE_SSL */
565
566 if (con->pipe_pid != 0)
567 {
568 /*
569 * Stop any CGI process...
570 */
571
572 cupsdEndProcess(con->pipe_pid, 1);
573 con->pipe_pid = 0;
574 }
575
576 if (con->file >= 0)
577 {
578 cupsdRemoveSelect(con->file);
579
580 close(con->file);
581 con->file = -1;
582 }
583
584 /*
585 * Close the socket and clear the file from the input set for select()...
586 */
587
588 if (con->http.fd >= 0)
589 {
590 cupsArrayRemove(ActiveClients, con);
591 cupsdSetBusyState();
592
593 if (partial)
594 {
595 /*
596 * Only do a partial close so that the encrypted client gets everything.
597 */
598
599 shutdown(con->http.fd, 0);
600 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
601 }
602 else
603 {
604 /*
605 * Shut the socket down fully...
606 */
607
608 cupsdRemoveSelect(con->http.fd);
609 close(con->http.fd);
610 con->http.fd = -1;
611 }
612 }
613
614 if (!partial)
615 {
616 /*
617 * Free memory...
618 */
619
620 if (con->http.input_set)
621 free(con->http.input_set);
622
623 httpClearCookie(HTTP(con));
624 httpClearFields(HTTP(con));
625
626 cupsdClearString(&con->filename);
627 cupsdClearString(&con->command);
628 cupsdClearString(&con->options);
629 cupsdClearString(&con->query_string);
630
631 if (con->request)
632 {
633 ippDelete(con->request);
634 con->request = NULL;
635 }
636
637 if (con->response)
638 {
639 ippDelete(con->response);
640 con->response = NULL;
641 }
642
643 if (con->language)
644 {
645 cupsLangFree(con->language);
646 con->language = NULL;
647 }
648
649 #ifdef HAVE_AUTHORIZATION_H
650 if (con->authref)
651 {
652 AuthorizationFree(con->authref, kAuthorizationFlagDefaults);
653 con->authref = NULL;
654 }
655 #endif /* HAVE_AUTHORIZATION_H */
656
657 /*
658 * Re-enable new client connections if we are going back under the
659 * limit...
660 */
661
662 if (cupsArrayCount(Clients) == MaxClients)
663 cupsdResumeListening();
664
665 /*
666 * Compact the list of clients as necessary...
667 */
668
669 cupsArrayRemove(Clients, con);
670
671 free(con);
672 }
673
674 return (partial);
675 }
676
677
678 /*
679 * 'cupsdFlushHeader()' - Flush the header fields to the client.
680 */
681
682 int /* I - Bytes written or -1 on error */
683 cupsdFlushHeader(cupsd_client_t *con) /* I - Client to flush to */
684 {
685 int bytes = httpFlushWrite(HTTP(con));
686
687 con->http.data_encoding = HTTP_ENCODE_LENGTH;
688
689 return (bytes);
690 }
691
692
693 /*
694 * 'cupsdReadClient()' - Read data from a client.
695 */
696
697 void
698 cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */
699 {
700 char line[32768], /* Line from client... */
701 operation[64], /* Operation code from socket */
702 version[64], /* HTTP version number string */
703 locale[64], /* Locale */
704 *ptr; /* Pointer into strings */
705 int major, minor; /* HTTP version numbers */
706 http_status_t status; /* Transfer status */
707 ipp_state_t ipp_state; /* State of IPP transfer */
708 int bytes; /* Number of bytes to POST */
709 char *filename; /* Name of file for GET/HEAD */
710 char buf[1024]; /* Buffer for real filename */
711 struct stat filestats; /* File information */
712 mime_type_t *type; /* MIME type of file */
713 cupsd_printer_t *p; /* Printer */
714 static unsigned request_id = 0; /* Request ID for temp files */
715
716
717 status = HTTP_CONTINUE;
718
719 cupsdLogMessage(CUPSD_LOG_DEBUG2,
720 "cupsdReadClient(con=%p(%d)) "
721 "con->http.error=%d "
722 "con->http.used=%d, "
723 "con->http.state=%d "
724 "con->data_encoding=HTTP_ENCODE_%s, "
725 "con->data_remaining=" CUPS_LLFMT ", "
726 "con->file=%d",
727 con, con->http.fd, con->http.error, con->http.used,
728 con->http.state,
729 con->http.data_encoding == HTTP_ENCODE_CHUNKED ?
730 "CHUNKED" : "LENGTH",
731 CUPS_LLCAST con->http.data_remaining, con->file);
732
733 #ifdef HAVE_SSL
734 if (con->auto_ssl)
735 {
736 /*
737 * Automatically check for a SSL/TLS handshake...
738 */
739
740 con->auto_ssl = 0;
741
742 if (recv(con->http.fd, buf, 1, MSG_PEEK) == 1 &&
743 (!buf[0] || !strchr("DGHOPT", buf[0])))
744 {
745 /*
746 * Encrypt this connection...
747 */
748
749 cupsdLogMessage(CUPSD_LOG_DEBUG2,
750 "cupsdReadClient: Saw first byte %02X, auto-negotiating "
751 "SSL/TLS session...", buf[0] & 255);
752
753 if (!encrypt_client(con))
754 cupsdCloseClient(con);
755
756 return;
757 }
758 }
759 #endif /* HAVE_SSL */
760
761 switch (con->http.state)
762 {
763 case HTTP_WAITING :
764 /*
765 * See if we've received a request line...
766 */
767
768 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
769 {
770 if (con->http.error && con->http.error != EPIPE)
771 cupsdLogMessage(CUPSD_LOG_DEBUG,
772 "cupsdReadClient: %d WAITING Closing for error %d "
773 "(%s)", con->http.fd, con->http.error,
774 strerror(con->http.error));
775 else
776 cupsdLogMessage(CUPSD_LOG_DEBUG,
777 "cupsdReadClient: %d WAITING Closing on EOF",
778 con->http.fd);
779
780 cupsdCloseClient(con);
781 return;
782 }
783
784 /*
785 * Ignore blank request lines...
786 */
787
788 if (line[0] == '\0')
789 break;
790
791 /*
792 * Clear other state variables...
793 */
794
795 httpClearFields(HTTP(con));
796
797 con->http.activity = time(NULL);
798 con->http.version = HTTP_1_0;
799 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
800 con->http.data_encoding = HTTP_ENCODE_LENGTH;
801 con->http.data_remaining = 0;
802 con->http._data_remaining = 0;
803 con->operation = HTTP_WAITING;
804 con->bytes = 0;
805 con->file = -1;
806 con->file_ready = 0;
807 con->pipe_pid = 0;
808 con->username[0] = '\0';
809 con->password[0] = '\0';
810 con->uri[0] = '\0';
811
812 cupsdClearString(&con->command);
813 cupsdClearString(&con->options);
814 cupsdClearString(&con->query_string);
815
816 if (con->request)
817 {
818 ippDelete(con->request);
819 con->request = NULL;
820 }
821
822 if (con->response)
823 {
824 ippDelete(con->response);
825 con->response = NULL;
826 }
827
828 if (con->language)
829 {
830 cupsLangFree(con->language);
831 con->language = NULL;
832 }
833
834 #ifdef HAVE_GSSAPI
835 con->have_gss = 0;
836 con->gss_uid = 0;
837 #endif /* HAVE_GSSAPI */
838
839 /*
840 * Grab the request line...
841 */
842
843 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
844 {
845 case 1 :
846 if (line[0])
847 {
848 cupsdLogMessage(CUPSD_LOG_ERROR,
849 "Bad request line \"%s\" from %s!",
850 _httpEncodeURI(buf, line, sizeof(buf)),
851 con->http.hostname);
852 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
853 cupsdCloseClient(con);
854 }
855 return;
856 case 2 :
857 con->http.version = HTTP_0_9;
858 break;
859 case 3 :
860 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
861 {
862 cupsdLogMessage(CUPSD_LOG_ERROR,
863 "Bad request line \"%s\" from %s!",
864 _httpEncodeURI(buf, line, sizeof(buf)),
865 con->http.hostname);
866 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
867 cupsdCloseClient(con);
868 return;
869 }
870
871 if (major < 2)
872 {
873 con->http.version = (http_version_t)(major * 100 + minor);
874 if (con->http.version == HTTP_1_1 && KeepAlive)
875 con->http.keep_alive = HTTP_KEEPALIVE_ON;
876 else
877 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
878 }
879 else
880 {
881 cupsdLogMessage(CUPSD_LOG_ERROR,
882 "Unsupported request line \"%s\" from %s!",
883 _httpEncodeURI(buf, line, sizeof(buf)),
884 con->http.hostname);
885 cupsdSendError(con, HTTP_NOT_SUPPORTED, CUPSD_AUTH_NONE);
886 cupsdCloseClient(con);
887 return;
888 }
889 break;
890 }
891
892 /*
893 * Handle full URLs in the request line...
894 */
895
896 if (strcmp(con->uri, "*"))
897 {
898 char scheme[HTTP_MAX_URI], /* Method/scheme */
899 userpass[HTTP_MAX_URI], /* Username:password */
900 hostname[HTTP_MAX_URI], /* Hostname */
901 resource[HTTP_MAX_URI]; /* Resource path */
902 int port; /* Port number */
903
904
905 /*
906 * Separate the URI into its components...
907 */
908
909 httpSeparateURI(HTTP_URI_CODING_MOST, con->uri,
910 scheme, sizeof(scheme),
911 userpass, sizeof(userpass),
912 hostname, sizeof(hostname), &port,
913 resource, sizeof(resource));
914
915 /*
916 * Only allow URIs with the servername, localhost, or an IP
917 * address...
918 */
919
920 if (strcmp(scheme, "file") &&
921 _cups_strcasecmp(hostname, ServerName) &&
922 _cups_strcasecmp(hostname, "localhost") &&
923 !isdigit(hostname[0]) && hostname[0] != '[')
924 {
925 /*
926 * Nope, we don't do proxies...
927 */
928
929 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad URI \"%s\" in request!",
930 con->uri);
931 cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE);
932 cupsdCloseClient(con);
933 return;
934 }
935
936 /*
937 * Copy the resource portion back into the URI; both resource and
938 * con->uri are HTTP_MAX_URI bytes in size...
939 */
940
941 strcpy(con->uri, resource);
942 }
943
944 /*
945 * Process the request...
946 */
947
948 if (!strcmp(operation, "GET"))
949 con->http.state = HTTP_GET;
950 else if (!strcmp(operation, "PUT"))
951 con->http.state = HTTP_PUT;
952 else if (!strcmp(operation, "POST"))
953 con->http.state = HTTP_POST;
954 else if (!strcmp(operation, "DELETE"))
955 con->http.state = HTTP_DELETE;
956 else if (!strcmp(operation, "TRACE"))
957 con->http.state = HTTP_TRACE;
958 else if (!strcmp(operation, "OPTIONS"))
959 con->http.state = HTTP_OPTIONS;
960 else if (!strcmp(operation, "HEAD"))
961 con->http.state = HTTP_HEAD;
962 else
963 {
964 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad operation \"%s\"!", operation);
965 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
966 cupsdCloseClient(con);
967 return;
968 }
969
970 gettimeofday(&(con->start), NULL);
971 con->operation = con->http.state;
972
973 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %s %s HTTP/%d.%d",
974 con->http.fd, operation, con->uri,
975 con->http.version / 100, con->http.version % 100);
976
977 con->http.status = HTTP_OK;
978
979 if (!cupsArrayFind(ActiveClients, con))
980 {
981 cupsArrayAdd(ActiveClients, con);
982 cupsdSetBusyState();
983 }
984
985 case HTTP_OPTIONS :
986 case HTTP_DELETE :
987 case HTTP_GET :
988 case HTTP_HEAD :
989 case HTTP_POST :
990 case HTTP_PUT :
991 case HTTP_TRACE :
992 /*
993 * Parse incoming parameters until the status changes...
994 */
995
996 while ((status = httpUpdate(HTTP(con))) == HTTP_CONTINUE)
997 if (!data_ready(con))
998 break;
999
1000 if (status != HTTP_OK && status != HTTP_CONTINUE)
1001 {
1002 if (con->http.error && con->http.error != EPIPE)
1003 cupsdLogMessage(CUPSD_LOG_DEBUG,
1004 "cupsdReadClient: %d FIELDS Closing for error %d "
1005 "(%s)", con->http.fd, con->http.error,
1006 strerror(con->http.error));
1007 else
1008 cupsdLogMessage(CUPSD_LOG_DEBUG,
1009 "cupsdReadClient: %d FIELDS Closing on EOF",
1010 con->http.fd);
1011
1012 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
1013 cupsdCloseClient(con);
1014 return;
1015 }
1016 break;
1017
1018 default :
1019 if (!data_ready(con) && recv(con->http.fd, buf, 1, MSG_PEEK) < 1)
1020 {
1021 /*
1022 * Connection closed...
1023 */
1024
1025 cupsdLogMessage(CUPSD_LOG_DEBUG,
1026 "cupsdReadClient: %d Closing on EOF", con->http.fd);
1027 cupsdCloseClient(con);
1028 return;
1029 }
1030 break; /* Anti-compiler-warning-code */
1031 }
1032
1033 /*
1034 * Handle new transfers...
1035 */
1036
1037 if (status == HTTP_OK)
1038 {
1039 if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
1040 {
1041 /*
1042 * Figure out the locale from the Accept-Language and Content-Type
1043 * fields...
1044 */
1045
1046 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
1047 ',')) != NULL)
1048 *ptr = '\0';
1049
1050 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
1051 ';')) != NULL)
1052 *ptr = '\0';
1053
1054 if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
1055 "charset=")) != NULL)
1056 {
1057 /*
1058 * Combine language and charset, and trim any extra params in the
1059 * content-type.
1060 */
1061
1062 snprintf(locale, sizeof(locale), "%s.%s",
1063 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8);
1064
1065 if ((ptr = strchr(locale, ',')) != NULL)
1066 *ptr = '\0';
1067 }
1068 else
1069 snprintf(locale, sizeof(locale), "%s.UTF-8",
1070 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
1071
1072 con->language = cupsLangGet(locale);
1073 }
1074 else
1075 con->language = cupsLangGet(DefaultLocale);
1076
1077 cupsdAuthorize(con);
1078
1079 if (!_cups_strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive",
1080 10) && KeepAlive)
1081 con->http.keep_alive = HTTP_KEEPALIVE_ON;
1082 else if (!_cups_strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "close", 5))
1083 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1084
1085 if (!con->http.fields[HTTP_FIELD_HOST][0] &&
1086 con->http.version >= HTTP_1_1)
1087 {
1088 /*
1089 * HTTP/1.1 and higher require the "Host:" field...
1090 */
1091
1092 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1093 {
1094 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing Host: field in request!");
1095 cupsdCloseClient(con);
1096 return;
1097 }
1098 }
1099 else if (!valid_host(con))
1100 {
1101 /*
1102 * Access to localhost must use "localhost" or the corresponding IPv4
1103 * or IPv6 values in the Host: field.
1104 */
1105
1106 cupsdLogMessage(CUPSD_LOG_ERROR,
1107 "Request from \"%s\" using invalid Host: field \"%s\"",
1108 con->http.hostname, con->http.fields[HTTP_FIELD_HOST]);
1109
1110 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1111 {
1112 cupsdCloseClient(con);
1113 return;
1114 }
1115 }
1116 else if (con->operation == HTTP_OPTIONS)
1117 {
1118 /*
1119 * Do OPTIONS command...
1120 */
1121
1122 if (con->best && con->best->type != CUPSD_AUTH_NONE)
1123 {
1124 if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE))
1125 {
1126 cupsdCloseClient(con);
1127 return;
1128 }
1129 }
1130
1131 if (!_cups_strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
1132 con->http.tls == NULL)
1133 {
1134 #ifdef HAVE_SSL
1135 /*
1136 * Do encryption stuff...
1137 */
1138
1139 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
1140 {
1141 cupsdCloseClient(con);
1142 return;
1143 }
1144
1145 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1146 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1147 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1148 httpPrintf(HTTP(con), "\r\n");
1149
1150 if (cupsdFlushHeader(con) < 0)
1151 {
1152 cupsdCloseClient(con);
1153 return;
1154 }
1155
1156 if (!encrypt_client(con))
1157 {
1158 cupsdCloseClient(con);
1159 return;
1160 }
1161 #else
1162 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
1163 {
1164 cupsdCloseClient(con);
1165 return;
1166 }
1167 #endif /* HAVE_SSL */
1168 }
1169
1170 if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE))
1171 {
1172 cupsdCloseClient(con);
1173 return;
1174 }
1175
1176 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
1177 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1178 httpPrintf(HTTP(con), "\r\n");
1179
1180 if (cupsdFlushHeader(con) < 0)
1181 {
1182 cupsdCloseClient(con);
1183 return;
1184 }
1185 }
1186 else if (!is_path_absolute(con->uri))
1187 {
1188 /*
1189 * Protect against malicious users!
1190 */
1191
1192 cupsdLogMessage(CUPSD_LOG_ERROR,
1193 "Request for non-absolute resource \"%s\"!", con->uri);
1194
1195 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
1196 {
1197 cupsdCloseClient(con);
1198 return;
1199 }
1200 }
1201 else
1202 {
1203 if (!_cups_strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
1204 con->http.tls == NULL)
1205 {
1206 #ifdef HAVE_SSL
1207 /*
1208 * Do encryption stuff...
1209 */
1210
1211 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
1212 {
1213 cupsdCloseClient(con);
1214 return;
1215 }
1216
1217 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1218 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1219 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1220 httpPrintf(HTTP(con), "\r\n");
1221
1222 if (cupsdFlushHeader(con) < 0)
1223 {
1224 cupsdCloseClient(con);
1225 return;
1226 }
1227
1228 if (!encrypt_client(con))
1229 {
1230 cupsdCloseClient(con);
1231 return;
1232 }
1233 #else
1234 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
1235 {
1236 cupsdCloseClient(con);
1237 return;
1238 }
1239 #endif /* HAVE_SSL */
1240 }
1241
1242 if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_OK)
1243 {
1244 cupsdSendError(con, status, CUPSD_AUTH_NONE);
1245 cupsdCloseClient(con);
1246 return;
1247 }
1248
1249 if (con->http.expect &&
1250 (con->operation == HTTP_POST || con->operation == HTTP_PUT))
1251 {
1252 if (con->http.expect == HTTP_CONTINUE)
1253 {
1254 /*
1255 * Send 100-continue header...
1256 */
1257
1258 if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL, CUPSD_AUTH_NONE))
1259 {
1260 cupsdCloseClient(con);
1261 return;
1262 }
1263 }
1264 else
1265 {
1266 /*
1267 * Send 417-expectation-failed header...
1268 */
1269
1270 if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL,
1271 CUPSD_AUTH_NONE))
1272 {
1273 cupsdCloseClient(con);
1274 return;
1275 }
1276
1277 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1278 httpPrintf(HTTP(con), "\r\n");
1279
1280 if (cupsdFlushHeader(con) < 0)
1281 {
1282 cupsdCloseClient(con);
1283 return;
1284 }
1285 }
1286 }
1287
1288 switch (con->http.state)
1289 {
1290 case HTTP_GET_SEND :
1291 if (!strncmp(con->uri, "/printers/", 10) &&
1292 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd"))
1293 {
1294 /*
1295 * Send PPD file - get the real printer name since printer
1296 * names are not case sensitive but filenames can be...
1297 */
1298
1299 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1300
1301 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1302 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1303 else
1304 {
1305 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1306 {
1307 cupsdCloseClient(con);
1308 return;
1309 }
1310
1311 break;
1312 }
1313 }
1314 else if ((!strncmp(con->uri, "/printers/", 10) ||
1315 !strncmp(con->uri, "/classes/", 9)) &&
1316 !strcmp(con->uri + strlen(con->uri) - 4, ".png"))
1317 {
1318 /*
1319 * Send icon file - get the real queue name since queue names are
1320 * not case sensitive but filenames can be...
1321 */
1322
1323 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".png" */
1324
1325 if (!strncmp(con->uri, "/printers/", 10))
1326 p = cupsdFindPrinter(con->uri + 10);
1327 else
1328 p = cupsdFindClass(con->uri + 9);
1329
1330 if (p)
1331 snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name);
1332 else
1333 {
1334 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1335 {
1336 cupsdCloseClient(con);
1337 return;
1338 }
1339
1340 break;
1341 }
1342 }
1343 else if (!WebInterface)
1344 {
1345 /*
1346 * Web interface is disabled. Show an appropriate message...
1347 */
1348
1349 if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE))
1350 {
1351 cupsdCloseClient(con);
1352 return;
1353 }
1354
1355 break;
1356 }
1357
1358 if ((!strncmp(con->uri, "/admin", 6) &&
1359 strncmp(con->uri, "/admin/conf/", 12) &&
1360 strncmp(con->uri, "/admin/log/", 11)) ||
1361 !strncmp(con->uri, "/printers", 9) ||
1362 !strncmp(con->uri, "/classes", 8) ||
1363 !strncmp(con->uri, "/help", 5) ||
1364 !strncmp(con->uri, "/jobs", 5))
1365 {
1366 /*
1367 * Send CGI output...
1368 */
1369
1370 if (!strncmp(con->uri, "/admin", 6))
1371 {
1372 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi",
1373 ServerBin);
1374
1375 cupsdSetString(&con->options, strchr(con->uri + 6, '?'));
1376 }
1377 else if (!strncmp(con->uri, "/printers", 9))
1378 {
1379 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1380 ServerBin);
1381
1382 if (con->uri[9] && con->uri[10])
1383 cupsdSetString(&con->options, con->uri + 9);
1384 else
1385 cupsdSetString(&con->options, NULL);
1386 }
1387 else if (!strncmp(con->uri, "/classes", 8))
1388 {
1389 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1390 ServerBin);
1391
1392 if (con->uri[8] && con->uri[9])
1393 cupsdSetString(&con->options, con->uri + 8);
1394 else
1395 cupsdSetString(&con->options, NULL);
1396 }
1397 else if (!strncmp(con->uri, "/jobs", 5))
1398 {
1399 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1400 ServerBin);
1401
1402 if (con->uri[5] && con->uri[6])
1403 cupsdSetString(&con->options, con->uri + 5);
1404 else
1405 cupsdSetString(&con->options, NULL);
1406 }
1407 else
1408 {
1409 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1410 ServerBin);
1411
1412 if (con->uri[5] && con->uri[6])
1413 cupsdSetString(&con->options, con->uri + 5);
1414 else
1415 cupsdSetString(&con->options, NULL);
1416 }
1417
1418 if (!cupsdSendCommand(con, con->command, con->options, 0))
1419 {
1420 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1421 {
1422 cupsdCloseClient(con);
1423 return;
1424 }
1425 }
1426 else
1427 cupsdLogRequest(con, HTTP_OK);
1428
1429 if (con->http.version <= HTTP_1_0)
1430 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1431 }
1432 else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
1433 (strchr(con->uri + 12, '/') ||
1434 strlen(con->uri) == 12)) ||
1435 (!strncmp(con->uri, "/admin/log/", 11) &&
1436 (strchr(con->uri + 11, '/') ||
1437 strlen(con->uri) == 11)))
1438 {
1439 /*
1440 * GET can only be done to configuration files directly under
1441 * /admin/conf...
1442 */
1443
1444 cupsdLogMessage(CUPSD_LOG_ERROR,
1445 "Request for subdirectory \"%s\"!", con->uri);
1446
1447 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
1448 {
1449 cupsdCloseClient(con);
1450 return;
1451 }
1452
1453 break;
1454 }
1455 else
1456 {
1457 /*
1458 * Serve a file...
1459 */
1460
1461 if ((filename = get_file(con, &filestats, buf,
1462 sizeof(buf))) == NULL)
1463 {
1464 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1465 {
1466 cupsdCloseClient(con);
1467 return;
1468 }
1469
1470 break;
1471 }
1472
1473 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
1474
1475 if (is_cgi(con, filename, &filestats, type))
1476 {
1477 /*
1478 * Note: con->command and con->options were set by
1479 * is_cgi()...
1480 */
1481
1482 if (!cupsdSendCommand(con, con->command, con->options, 0))
1483 {
1484 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1485 {
1486 cupsdCloseClient(con);
1487 return;
1488 }
1489 }
1490 else
1491 cupsdLogRequest(con, HTTP_OK);
1492
1493 if (con->http.version <= HTTP_1_0)
1494 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1495 break;
1496 }
1497
1498 if (!check_if_modified(con, &filestats))
1499 {
1500 if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
1501 {
1502 cupsdCloseClient(con);
1503 return;
1504 }
1505 }
1506 else
1507 {
1508 if (type == NULL)
1509 strcpy(line, "text/plain");
1510 else
1511 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1512
1513 if (!write_file(con, HTTP_OK, filename, line, &filestats))
1514 {
1515 cupsdCloseClient(con);
1516 return;
1517 }
1518 }
1519 }
1520 break;
1521
1522 case HTTP_POST_RECV :
1523 /*
1524 * See if the POST request includes a Content-Length field, and if
1525 * so check the length against any limits that are set...
1526 */
1527
1528 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1529 MaxRequestSize > 0 &&
1530 con->http.data_remaining > MaxRequestSize)
1531 {
1532 /*
1533 * Request too large...
1534 */
1535
1536 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
1537 {
1538 cupsdCloseClient(con);
1539 return;
1540 }
1541
1542 break;
1543 }
1544 else if (con->http.data_remaining < 0 ||
1545 (!con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1546 con->http.data_encoding == HTTP_ENCODE_LENGTH))
1547 {
1548 /*
1549 * Negative content lengths are invalid!
1550 */
1551
1552 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1553 {
1554 cupsdCloseClient(con);
1555 return;
1556 }
1557
1558 break;
1559 }
1560
1561 /*
1562 * See what kind of POST request this is; for IPP requests the
1563 * content-type field will be "application/ipp"...
1564 */
1565
1566 if (!strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
1567 "application/ipp"))
1568 con->request = ippNew();
1569 else if (!WebInterface)
1570 {
1571 /*
1572 * Web interface is disabled. Show an appropriate message...
1573 */
1574
1575 if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE))
1576 {
1577 cupsdCloseClient(con);
1578 return;
1579 }
1580
1581 break;
1582 }
1583 else if ((!strncmp(con->uri, "/admin", 6) &&
1584 strncmp(con->uri, "/admin/conf/", 12) &&
1585 strncmp(con->uri, "/admin/log/", 11)) ||
1586 !strncmp(con->uri, "/printers", 9) ||
1587 !strncmp(con->uri, "/classes", 8) ||
1588 !strncmp(con->uri, "/help", 5) ||
1589 !strncmp(con->uri, "/jobs", 5))
1590 {
1591 /*
1592 * CGI request...
1593 */
1594
1595 if (!strncmp(con->uri, "/admin", 6))
1596 {
1597 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi",
1598 ServerBin);
1599
1600 cupsdSetString(&con->options, strchr(con->uri + 6, '?'));
1601 }
1602 else if (!strncmp(con->uri, "/printers", 9))
1603 {
1604 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1605 ServerBin);
1606
1607 if (con->uri[9] && con->uri[10])
1608 cupsdSetString(&con->options, con->uri + 9);
1609 else
1610 cupsdSetString(&con->options, NULL);
1611 }
1612 else if (!strncmp(con->uri, "/classes", 8))
1613 {
1614 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1615 ServerBin);
1616
1617 if (con->uri[8] && con->uri[9])
1618 cupsdSetString(&con->options, con->uri + 8);
1619 else
1620 cupsdSetString(&con->options, NULL);
1621 }
1622 else if (!strncmp(con->uri, "/jobs", 5))
1623 {
1624 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1625 ServerBin);
1626
1627 if (con->uri[5] && con->uri[6])
1628 cupsdSetString(&con->options, con->uri + 5);
1629 else
1630 cupsdSetString(&con->options, NULL);
1631 }
1632 else
1633 {
1634 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1635 ServerBin);
1636
1637 if (con->uri[5] && con->uri[6])
1638 cupsdSetString(&con->options, con->uri + 5);
1639 else
1640 cupsdSetString(&con->options, NULL);
1641 }
1642
1643 if (con->http.version <= HTTP_1_0)
1644 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1645 }
1646 else
1647 {
1648 /*
1649 * POST to a file...
1650 */
1651
1652 if ((filename = get_file(con, &filestats, buf,
1653 sizeof(buf))) == NULL)
1654 {
1655 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1656 {
1657 cupsdCloseClient(con);
1658 return;
1659 }
1660
1661 break;
1662 }
1663
1664 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
1665
1666 if (!is_cgi(con, filename, &filestats, type))
1667 {
1668 /*
1669 * Only POST to CGI's...
1670 */
1671
1672 if (!cupsdSendError(con, HTTP_UNAUTHORIZED, CUPSD_AUTH_NONE))
1673 {
1674 cupsdCloseClient(con);
1675 return;
1676 }
1677 }
1678 }
1679 break;
1680
1681 case HTTP_PUT_RECV :
1682 /*
1683 * Validate the resource name...
1684 */
1685
1686 if (strncmp(con->uri, "/admin/conf/", 12) ||
1687 strchr(con->uri + 12, '/') ||
1688 strlen(con->uri) == 12)
1689 {
1690 /*
1691 * PUT can only be done to configuration files under
1692 * /admin/conf...
1693 */
1694
1695 cupsdLogMessage(CUPSD_LOG_ERROR,
1696 "Request for subdirectory \"%s\"!", con->uri);
1697
1698 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
1699 {
1700 cupsdCloseClient(con);
1701 return;
1702 }
1703
1704 break;
1705 }
1706
1707 /*
1708 * See if the PUT request includes a Content-Length field, and if
1709 * so check the length against any limits that are set...
1710 */
1711
1712 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1713 MaxRequestSize > 0 &&
1714 con->http.data_remaining > MaxRequestSize)
1715 {
1716 /*
1717 * Request too large...
1718 */
1719
1720 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
1721 {
1722 cupsdCloseClient(con);
1723 return;
1724 }
1725
1726 break;
1727 }
1728 else if (con->http.data_remaining < 0)
1729 {
1730 /*
1731 * Negative content lengths are invalid!
1732 */
1733
1734 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1735 {
1736 cupsdCloseClient(con);
1737 return;
1738 }
1739
1740 break;
1741 }
1742
1743 /*
1744 * Open a temporary file to hold the request...
1745 */
1746
1747 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
1748 request_id ++);
1749 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1750
1751 if (con->file < 0)
1752 {
1753 cupsdLogMessage(CUPSD_LOG_ERROR,
1754 "Unable to create request file %s: %s",
1755 con->filename, strerror(errno));
1756
1757 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
1758 {
1759 cupsdCloseClient(con);
1760 return;
1761 }
1762 }
1763
1764 fchmod(con->file, 0640);
1765 fchown(con->file, RunUser, Group);
1766 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1767 break;
1768
1769 case HTTP_DELETE :
1770 case HTTP_TRACE :
1771 cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE);
1772 cupsdCloseClient(con);
1773 return;
1774
1775 case HTTP_HEAD :
1776 if (!strncmp(con->uri, "/printers/", 10) &&
1777 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd"))
1778 {
1779 /*
1780 * Send PPD file - get the real printer name since printer
1781 * names are not case sensitive but filenames can be...
1782 */
1783
1784 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1785
1786 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1787 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1788 else
1789 {
1790 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1791 {
1792 cupsdCloseClient(con);
1793 return;
1794 }
1795
1796 break;
1797 }
1798 }
1799 else if (!strncmp(con->uri, "/printers/", 10) &&
1800 !strcmp(con->uri + strlen(con->uri) - 4, ".png"))
1801 {
1802 /*
1803 * Send PNG file - get the real printer name since printer
1804 * names are not case sensitive but filenames can be...
1805 */
1806
1807 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1808
1809 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1810 snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name);
1811 else
1812 {
1813 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1814 {
1815 cupsdCloseClient(con);
1816 return;
1817 }
1818
1819 break;
1820 }
1821 }
1822 else if (!WebInterface)
1823 {
1824 if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
1825 {
1826 cupsdCloseClient(con);
1827 return;
1828 }
1829
1830 if (httpPrintf(HTTP(con), "\r\n") < 0)
1831 {
1832 cupsdCloseClient(con);
1833 return;
1834 }
1835
1836 if (cupsdFlushHeader(con) < 0)
1837 {
1838 cupsdCloseClient(con);
1839 return;
1840 }
1841
1842 con->http.state = HTTP_WAITING;
1843 break;
1844 }
1845
1846 if ((!strncmp(con->uri, "/admin", 6) &&
1847 strncmp(con->uri, "/admin/conf/", 12) &&
1848 strncmp(con->uri, "/admin/log/", 11)) ||
1849 !strncmp(con->uri, "/printers", 9) ||
1850 !strncmp(con->uri, "/classes", 8) ||
1851 !strncmp(con->uri, "/help", 5) ||
1852 !strncmp(con->uri, "/jobs", 5))
1853 {
1854 /*
1855 * CGI output...
1856 */
1857
1858 if (!cupsdSendHeader(con, HTTP_OK, "text/html", CUPSD_AUTH_NONE))
1859 {
1860 cupsdCloseClient(con);
1861 return;
1862 }
1863
1864 if (httpPrintf(HTTP(con), "\r\n") < 0)
1865 {
1866 cupsdCloseClient(con);
1867 return;
1868 }
1869
1870 if (cupsdFlushHeader(con) < 0)
1871 {
1872 cupsdCloseClient(con);
1873 return;
1874 }
1875
1876 cupsdLogRequest(con, HTTP_OK);
1877 }
1878 else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
1879 (strchr(con->uri + 12, '/') ||
1880 strlen(con->uri) == 12)) ||
1881 (!strncmp(con->uri, "/admin/log/", 11) &&
1882 (strchr(con->uri + 11, '/') ||
1883 strlen(con->uri) == 11)))
1884 {
1885 /*
1886 * HEAD can only be done to configuration files under
1887 * /admin/conf...
1888 */
1889
1890 cupsdLogMessage(CUPSD_LOG_ERROR,
1891 "Request for subdirectory \"%s\"!", con->uri);
1892
1893 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
1894 {
1895 cupsdCloseClient(con);
1896 return;
1897 }
1898
1899 break;
1900 }
1901 else if ((filename = get_file(con, &filestats, buf,
1902 sizeof(buf))) == NULL)
1903 {
1904 if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html",
1905 CUPSD_AUTH_NONE))
1906 {
1907 cupsdCloseClient(con);
1908 return;
1909 }
1910
1911 cupsdLogRequest(con, HTTP_NOT_FOUND);
1912 }
1913 else if (!check_if_modified(con, &filestats))
1914 {
1915 if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
1916 {
1917 cupsdCloseClient(con);
1918 return;
1919 }
1920
1921 cupsdLogRequest(con, HTTP_NOT_MODIFIED);
1922 }
1923 else
1924 {
1925 /*
1926 * Serve a file...
1927 */
1928
1929 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
1930 if (type == NULL)
1931 strcpy(line, "text/plain");
1932 else
1933 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1934
1935 if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
1936 {
1937 cupsdCloseClient(con);
1938 return;
1939 }
1940
1941 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1942 httpGetDateString(filestats.st_mtime)) < 0)
1943 {
1944 cupsdCloseClient(con);
1945 return;
1946 }
1947
1948 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1949 (unsigned long)filestats.st_size) < 0)
1950 {
1951 cupsdCloseClient(con);
1952 return;
1953 }
1954
1955 cupsdLogRequest(con, HTTP_OK);
1956 }
1957
1958 if (httpPrintf(HTTP(con), "\r\n") < 0)
1959 {
1960 cupsdCloseClient(con);
1961 return;
1962 }
1963
1964 if (cupsdFlushHeader(con) < 0)
1965 {
1966 cupsdCloseClient(con);
1967 return;
1968 }
1969
1970 con->http.state = HTTP_WAITING;
1971 break;
1972
1973 default :
1974 break; /* Anti-compiler-warning-code */
1975 }
1976 }
1977 }
1978
1979 /*
1980 * Handle any incoming data...
1981 */
1982
1983 switch (con->http.state)
1984 {
1985 case HTTP_PUT_RECV :
1986 do
1987 {
1988 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
1989 {
1990 if (con->http.error && con->http.error != EPIPE)
1991 cupsdLogMessage(CUPSD_LOG_DEBUG,
1992 "cupsdReadClient: %d PUT_RECV Closing for error "
1993 "%d (%s)", con->http.fd, con->http.error,
1994 strerror(con->http.error));
1995 else
1996 cupsdLogMessage(CUPSD_LOG_DEBUG,
1997 "cupsdReadClient: %d PUT_RECV Closing on EOF",
1998 con->http.fd);
1999
2000 cupsdCloseClient(con);
2001 return;
2002 }
2003 else if (bytes > 0)
2004 {
2005 con->bytes += bytes;
2006
2007 if (write(con->file, line, bytes) < bytes)
2008 {
2009 cupsdLogMessage(CUPSD_LOG_ERROR,
2010 "cupsdReadClient: Unable to write %d bytes to %s: %s",
2011 bytes, con->filename, strerror(errno));
2012
2013 close(con->file);
2014 con->file = -1;
2015 unlink(con->filename);
2016 cupsdClearString(&con->filename);
2017
2018 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
2019 {
2020 cupsdCloseClient(con);
2021 return;
2022 }
2023 }
2024 }
2025 }
2026 while (con->http.state == HTTP_PUT_RECV && data_ready(con));
2027
2028 if (con->http.state == HTTP_WAITING)
2029 {
2030 /*
2031 * End of file, see how big it is...
2032 */
2033
2034 fstat(con->file, &filestats);
2035
2036 close(con->file);
2037 con->file = -1;
2038
2039 if (filestats.st_size > MaxRequestSize &&
2040 MaxRequestSize > 0)
2041 {
2042 /*
2043 * Request is too big; remove it and send an error...
2044 */
2045
2046 unlink(con->filename);
2047 cupsdClearString(&con->filename);
2048
2049 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
2050 {
2051 cupsdCloseClient(con);
2052 return;
2053 }
2054 }
2055
2056 /*
2057 * Install the configuration file...
2058 */
2059
2060 status = install_conf_file(con);
2061
2062 /*
2063 * Return the status to the client...
2064 */
2065
2066 if (!cupsdSendError(con, status, CUPSD_AUTH_NONE))
2067 {
2068 cupsdCloseClient(con);
2069 return;
2070 }
2071 }
2072 break;
2073
2074 case HTTP_POST_RECV :
2075 do
2076 {
2077 if (con->request && con->file < 0)
2078 {
2079 /*
2080 * Grab any request data from the connection...
2081 */
2082
2083 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
2084 {
2085 cupsdLogMessage(CUPSD_LOG_ERROR,
2086 "cupsdReadClient: %d IPP Read Error!",
2087 con->http.fd);
2088
2089 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
2090 cupsdCloseClient(con);
2091 return;
2092 }
2093 else if (ipp_state != IPP_DATA)
2094 {
2095 if (con->http.state == HTTP_POST_SEND)
2096 {
2097 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
2098 cupsdCloseClient(con);
2099 return;
2100 }
2101
2102 break;
2103 }
2104 else
2105 {
2106 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %d.%d %s %d",
2107 con->http.fd, con->request->request.op.version[0],
2108 con->request->request.op.version[1],
2109 ippOpString(con->request->request.op.operation_id),
2110 con->request->request.op.request_id);
2111 con->bytes += ippLength(con->request);
2112 }
2113 }
2114
2115 if (con->file < 0 && con->http.state != HTTP_POST_SEND)
2116 {
2117 /*
2118 * Create a file as needed for the request data...
2119 */
2120
2121 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
2122 request_id ++);
2123 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
2124
2125 if (con->file < 0)
2126 {
2127 cupsdLogMessage(CUPSD_LOG_ERROR,
2128 "Unable to create request file %s: %s",
2129 con->filename, strerror(errno));
2130
2131 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
2132 {
2133 cupsdCloseClient(con);
2134 return;
2135 }
2136 }
2137
2138 fchmod(con->file, 0640);
2139 fchown(con->file, RunUser, Group);
2140 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2141 }
2142
2143 if (con->http.state != HTTP_POST_SEND)
2144 {
2145 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
2146 {
2147 if (con->http.error && con->http.error != EPIPE)
2148 cupsdLogMessage(CUPSD_LOG_DEBUG,
2149 "cupsdReadClient: %d POST_SEND Closing for "
2150 "error %d (%s)", con->http.fd, con->http.error,
2151 strerror(con->http.error));
2152 else
2153 cupsdLogMessage(CUPSD_LOG_DEBUG,
2154 "cupsdReadClient: %d POST_SEND Closing on EOF",
2155 con->http.fd);
2156
2157 cupsdCloseClient(con);
2158 return;
2159 }
2160 else if (bytes > 0)
2161 {
2162 con->bytes += bytes;
2163
2164 if (write(con->file, line, bytes) < bytes)
2165 {
2166 cupsdLogMessage(CUPSD_LOG_ERROR,
2167 "cupsdReadClient: Unable to write %d bytes to "
2168 "%s: %s", bytes, con->filename,
2169 strerror(errno));
2170
2171 close(con->file);
2172 con->file = -1;
2173 unlink(con->filename);
2174 cupsdClearString(&con->filename);
2175
2176 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE,
2177 CUPSD_AUTH_NONE))
2178 {
2179 cupsdCloseClient(con);
2180 return;
2181 }
2182 }
2183 }
2184 else if (con->http.state == HTTP_POST_RECV)
2185 return;
2186 else if (con->http.state != HTTP_POST_SEND)
2187 {
2188 cupsdLogMessage(CUPSD_LOG_DEBUG,
2189 "cupsdReadClient: %d Closing on unknown HTTP "
2190 "state %d", con->http.fd, con->http.state);
2191 cupsdCloseClient(con);
2192 return;
2193 }
2194 }
2195 }
2196 while (con->http.state == HTTP_POST_RECV && data_ready(con));
2197
2198 if (con->http.state == HTTP_POST_SEND)
2199 {
2200 if (con->file >= 0)
2201 {
2202 fstat(con->file, &filestats);
2203
2204 close(con->file);
2205 con->file = -1;
2206
2207 if (filestats.st_size > MaxRequestSize &&
2208 MaxRequestSize > 0)
2209 {
2210 /*
2211 * Request is too big; remove it and send an error...
2212 */
2213
2214 unlink(con->filename);
2215 cupsdClearString(&con->filename);
2216
2217 if (con->request)
2218 {
2219 /*
2220 * Delete any IPP request data...
2221 */
2222
2223 ippDelete(con->request);
2224 con->request = NULL;
2225 }
2226
2227 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
2228 {
2229 cupsdCloseClient(con);
2230 return;
2231 }
2232 }
2233 else if (filestats.st_size == 0)
2234 {
2235 /*
2236 * Don't allow empty file...
2237 */
2238
2239 unlink(con->filename);
2240 cupsdClearString(&con->filename);
2241 }
2242
2243 if (con->command)
2244 {
2245 if (!cupsdSendCommand(con, con->command, con->options, 0))
2246 {
2247 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
2248 {
2249 cupsdCloseClient(con);
2250 return;
2251 }
2252 }
2253 else
2254 cupsdLogRequest(con, HTTP_OK);
2255 }
2256 }
2257
2258 if (con->request)
2259 {
2260 cupsdProcessIPPRequest(con);
2261
2262 if (con->filename)
2263 {
2264 unlink(con->filename);
2265 cupsdClearString(&con->filename);
2266 }
2267
2268 return;
2269 }
2270 }
2271 break;
2272
2273 default :
2274 break; /* Anti-compiler-warning-code */
2275 }
2276
2277 if (con->http.state == HTTP_WAITING)
2278 {
2279 if (!con->http.keep_alive)
2280 {
2281 cupsdLogMessage(CUPSD_LOG_DEBUG,
2282 "cupsdReadClient: %d Closing because Keep-Alive disabled",
2283 con->http.fd);
2284 cupsdCloseClient(con);
2285 }
2286 else
2287 {
2288 cupsArrayRemove(ActiveClients, con);
2289 cupsdSetBusyState();
2290 }
2291 }
2292 }
2293
2294
2295 /*
2296 * 'cupsdSendCommand()' - Send output from a command via HTTP.
2297 */
2298
2299 int /* O - 1 on success, 0 on failure */
2300 cupsdSendCommand(
2301 cupsd_client_t *con, /* I - Client connection */
2302 char *command, /* I - Command to run */
2303 char *options, /* I - Command-line options */
2304 int root) /* I - Run as root? */
2305 {
2306 int fd; /* Standard input file descriptor */
2307
2308
2309 if (con->filename)
2310 {
2311 fd = open(con->filename, O_RDONLY);
2312
2313 if (fd < 0)
2314 {
2315 cupsdLogMessage(CUPSD_LOG_ERROR,
2316 "cupsdSendCommand: %d Unable to open \"%s\" for reading: %s",
2317 con->http.fd, con->filename ? con->filename : "/dev/null",
2318 strerror(errno));
2319 return (0);
2320 }
2321
2322 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
2323 }
2324 else
2325 fd = -1;
2326
2327 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root);
2328
2329 if (fd >= 0)
2330 close(fd);
2331
2332 cupsdLogMessage(CUPSD_LOG_INFO, "Started \"%s\" (pid=%d)", command,
2333 con->pipe_pid);
2334
2335 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendCommand: %d file=%d",
2336 con->http.fd, con->file);
2337
2338 if (con->pipe_pid == 0)
2339 return (0);
2340
2341 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2342
2343 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
2344
2345 con->sent_header = 0;
2346 con->file_ready = 0;
2347 con->got_fields = 0;
2348 con->header_used = 0;
2349
2350 return (1);
2351 }
2352
2353
2354 /*
2355 * 'cupsdSendError()' - Send an error message via HTTP.
2356 */
2357
2358 int /* O - 1 if successful, 0 otherwise */
2359 cupsdSendError(cupsd_client_t *con, /* I - Connection */
2360 http_status_t code, /* I - Error code */
2361 int auth_type)/* I - Authentication type */
2362 {
2363 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2364 "cupsdSendError(con=%p(%d), code=%d, auth_type=%d", con,
2365 con->http.fd, code, auth_type);
2366
2367 #ifdef HAVE_SSL
2368 /*
2369 * Force client to upgrade for authentication if that is how the
2370 * server is configured...
2371 */
2372
2373 if (code == HTTP_UNAUTHORIZED &&
2374 DefaultEncryption == HTTP_ENCRYPT_REQUIRED &&
2375 _cups_strcasecmp(con->http.hostname, "localhost") &&
2376 !con->http.tls)
2377 {
2378 code = HTTP_UPGRADE_REQUIRED;
2379 }
2380 #endif /* HAVE_SSL */
2381
2382 /*
2383 * Put the request in the access_log file...
2384 */
2385
2386 cupsdLogRequest(con, code);
2387
2388 /*
2389 * To work around bugs in some proxies, don't use Keep-Alive for some
2390 * error messages...
2391 *
2392 * Kerberos authentication doesn't work without Keep-Alive, so
2393 * never disable it in that case.
2394 */
2395
2396 if (code >= HTTP_BAD_REQUEST && con->http.auth_type != CUPSD_AUTH_NEGOTIATE)
2397 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
2398
2399 /*
2400 * Send an error message back to the client. If the error code is a
2401 * 400 or 500 series, make sure the message contains some text, too!
2402 */
2403
2404 if (!cupsdSendHeader(con, code, NULL, auth_type))
2405 return (0);
2406
2407 #ifdef HAVE_SSL
2408 if (code == HTTP_UPGRADE_REQUIRED)
2409 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
2410 return (0);
2411
2412 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0)
2413 return (0);
2414 #endif /* HAVE_SSL */
2415
2416 if (con->http.version >= HTTP_1_1 &&
2417 con->http.keep_alive == HTTP_KEEPALIVE_OFF)
2418 {
2419 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
2420 return (0);
2421 }
2422
2423 if (code >= HTTP_BAD_REQUEST)
2424 {
2425 /*
2426 * Send a human-readable error message.
2427 */
2428
2429 char message[4096], /* Message for user */
2430 urltext[1024], /* URL redirection text */
2431 redirect[1024]; /* Redirection link */
2432 const char *text; /* Status-specific text */
2433
2434
2435 redirect[0] = '\0';
2436
2437 if (code == HTTP_UNAUTHORIZED)
2438 text = _cupsLangString(con->language,
2439 _("Enter your username and password or the "
2440 "root username and password to access this "
2441 "page. If you are using Kerberos authentication, "
2442 "make sure you have a valid Kerberos ticket."));
2443 else if (code == HTTP_UPGRADE_REQUIRED)
2444 {
2445 text = urltext;
2446
2447 snprintf(urltext, sizeof(urltext),
2448 _cupsLangString(con->language,
2449 _("You must access this page using the URL "
2450 "<A HREF=\"https://%s:%d%s\">"
2451 "https://%s:%d%s</A>.")),
2452 con->servername, con->serverport, con->uri,
2453 con->servername, con->serverport, con->uri);
2454
2455 snprintf(redirect, sizeof(redirect),
2456 "<META HTTP-EQUIV=\"Refresh\" "
2457 "CONTENT=\"3;URL=https://%s:%d%s\">\n",
2458 con->servername, con->serverport, con->uri);
2459 }
2460 else if (code == HTTP_WEBIF_DISABLED)
2461 text = _cupsLangString(con->language,
2462 _("The web interface is currently disabled. Run "
2463 "\"cupsctl WebInterface=yes\" to enable it."));
2464 else
2465 text = "";
2466
2467 snprintf(message, sizeof(message),
2468 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2469 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
2470 "<HTML>\n"
2471 "<HEAD>\n"
2472 "\t<META HTTP-EQUIV=\"Content-Type\" "
2473 "CONTENT=\"text/html; charset=utf-8\">\n"
2474 "\t<TITLE>%s - " CUPS_SVERSION "</TITLE>\n"
2475 "\t<LINK REL=\"STYLESHEET\" TYPE=\"text/css\" "
2476 "HREF=\"/cups.css\">\n"
2477 "%s"
2478 "</HEAD>\n"
2479 "<BODY>\n"
2480 "<H1>%s</H1>\n"
2481 "<P>%s</P>\n"
2482 "</BODY>\n"
2483 "</HTML>\n",
2484 httpStatus(code), redirect, httpStatus(code), text);
2485
2486 if (httpPrintf(HTTP(con), "Content-Type: text/html; charset=utf-8\r\n") < 0)
2487 return (0);
2488 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
2489 (int)strlen(message)) < 0)
2490 return (0);
2491 if (httpPrintf(HTTP(con), "\r\n") < 0)
2492 return (0);
2493 if (httpPrintf(HTTP(con), "%s", message) < 0)
2494 return (0);
2495 }
2496 else if (httpPrintf(HTTP(con), "\r\n") < 0)
2497 return (0);
2498
2499 if (cupsdFlushHeader(con) < 0)
2500 return (0);
2501
2502 con->http.state = HTTP_WAITING;
2503
2504 return (1);
2505 }
2506
2507
2508 /*
2509 * 'cupsdSendHeader()' - Send an HTTP request.
2510 */
2511
2512 int /* O - 1 on success, 0 on failure */
2513 cupsdSendHeader(
2514 cupsd_client_t *con, /* I - Client to send to */
2515 http_status_t code, /* I - HTTP status code */
2516 char *type, /* I - MIME type of document */
2517 int auth_type) /* I - Type of authentication */
2518 {
2519 char auth_str[1024]; /* Authorization string */
2520 #if 0 /* def HAVE_GSSAPI */
2521 static char *gss_buf = NULL; /* Kerberos auth data buffer */
2522 static int gss_bufsize = 0; /* Size of Kerberos auth data buffer */
2523 #endif /* HAVE_GSSAPI */
2524
2525
2526 /*
2527 * Send the HTTP status header...
2528 */
2529
2530 if (code == HTTP_CONTINUE)
2531 {
2532 /*
2533 * 100-continue doesn't send any headers...
2534 */
2535
2536 return (httpPrintf(HTTP(con), "HTTP/%d.%d 100 Continue\r\n\r\n",
2537 con->http.version / 100, con->http.version % 100) > 0);
2538 }
2539 else if (code == HTTP_WEBIF_DISABLED)
2540 {
2541 /*
2542 * Treat our special "web interface is disabled" status as "200 OK" for web
2543 * browsers.
2544 */
2545
2546 code = HTTP_OK;
2547 }
2548
2549 httpFlushWrite(HTTP(con));
2550
2551 con->http.data_encoding = HTTP_ENCODE_FIELDS;
2552
2553 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
2554 con->http.version % 100, code, httpStatus(code)) < 0)
2555 return (0);
2556 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
2557 return (0);
2558 if (ServerHeader)
2559 if (httpPrintf(HTTP(con), "Server: %s\r\n", ServerHeader) < 0)
2560 return (0);
2561 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
2562 {
2563 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
2564 return (0);
2565 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n",
2566 KeepAliveTimeout) < 0)
2567 return (0);
2568 }
2569 if (code == HTTP_METHOD_NOT_ALLOWED)
2570 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n") < 0)
2571 return (0);
2572
2573 if (code == HTTP_UNAUTHORIZED)
2574 {
2575 if (auth_type == CUPSD_AUTH_NONE)
2576 {
2577 if (!con->best || con->best->type <= CUPSD_AUTH_NONE)
2578 auth_type = DefaultAuthType;
2579 else
2580 auth_type = con->best->type;
2581 }
2582
2583 auth_str[0] = '\0';
2584
2585 if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
2586 strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
2587 else if (auth_type == CUPSD_AUTH_DIGEST)
2588 snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"",
2589 con->http.hostname);
2590 #ifdef HAVE_GSSAPI
2591 else if (auth_type == CUPSD_AUTH_NEGOTIATE)
2592 {
2593 # ifdef AF_LOCAL
2594 if (_httpAddrFamily(con->http.hostaddr) == AF_LOCAL)
2595 strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
2596 else
2597 # endif /* AF_LOCAL */
2598 strlcpy(auth_str, "Negotiate", sizeof(auth_str));
2599 }
2600 #endif /* HAVE_GSSAPI */
2601
2602 if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE &&
2603 !_cups_strcasecmp(con->http.hostname, "localhost"))
2604 {
2605 /*
2606 * Add a "trc" (try root certification) parameter for local non-Kerberos
2607 * requests when the request requires system group membership - then the
2608 * client knows the root certificate can/should be used.
2609 *
2610 * Also, for Mac OS X we also look for @AUTHKEY and add an "authkey"
2611 * parameter as needed...
2612 */
2613
2614 char *name, /* Current user name */
2615 *auth_key; /* Auth key buffer */
2616 size_t auth_size; /* Size of remaining buffer */
2617
2618 auth_key = auth_str + strlen(auth_str);
2619 auth_size = sizeof(auth_str) - (auth_key - auth_str);
2620
2621 for (name = (char *)cupsArrayFirst(con->best->names);
2622 name;
2623 name = (char *)cupsArrayNext(con->best->names))
2624 {
2625 #ifdef HAVE_AUTHORIZATION_H
2626 if (!_cups_strncasecmp(name, "@AUTHKEY(", 9))
2627 {
2628 snprintf(auth_key, auth_size, ", authkey=\"%s\"", name + 9);
2629 /* end parenthesis is stripped in conf.c */
2630 break;
2631 }
2632 else
2633 #endif /* HAVE_AUTHORIZATION_H */
2634 if (!_cups_strcasecmp(name, "@SYSTEM"))
2635 {
2636 #ifdef HAVE_AUTHORIZATION_H
2637 if (SystemGroupAuthKey)
2638 snprintf(auth_key, auth_size,
2639 ", authkey=\"%s\"",
2640 SystemGroupAuthKey);
2641 else
2642 #else
2643 strlcpy(auth_key, ", trc=\"y\"", auth_size);
2644 #endif /* HAVE_AUTHORIZATION_H */
2645 break;
2646 }
2647 }
2648 }
2649
2650 if (auth_str[0])
2651 {
2652 cupsdLogMessage(CUPSD_LOG_DEBUG,
2653 "cupsdSendHeader: %d WWW-Authenticate: %s", con->http.fd,
2654 auth_str);
2655
2656 if (httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0)
2657 return (0);
2658 }
2659 }
2660
2661 if (con->language && strcmp(con->language->language, "C"))
2662 {
2663 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
2664 con->language->language) < 0)
2665 return (0);
2666 }
2667
2668 if (type)
2669 {
2670 if (!strcmp(type, "text/html"))
2671 {
2672 if (httpPrintf(HTTP(con),
2673 "Content-Type: text/html; charset=utf-8\r\n") < 0)
2674 return (0);
2675 }
2676 else if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
2677 return (0);
2678 }
2679
2680 return (1);
2681 }
2682
2683
2684 /*
2685 * 'cupsdUpdateCGI()' - Read status messages from CGI scripts and programs.
2686 */
2687
2688 void
2689 cupsdUpdateCGI(void)
2690 {
2691 char *ptr, /* Pointer to end of line in buffer */
2692 message[1024]; /* Pointer to message text */
2693 int loglevel; /* Log level for message */
2694
2695
2696 while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel,
2697 message, sizeof(message))) != NULL)
2698 {
2699 if (loglevel == CUPSD_LOG_INFO)
2700 cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
2701
2702 if (!strchr(CGIStatusBuffer->buffer, '\n'))
2703 break;
2704 }
2705
2706 if (ptr == NULL && !CGIStatusBuffer->bufused)
2707 {
2708 /*
2709 * Fatal error on pipe - should never happen!
2710 */
2711
2712 cupsdLogMessage(CUPSD_LOG_CRIT,
2713 "cupsdUpdateCGI: error reading from CGI error pipe - %s",
2714 strerror(errno));
2715 }
2716 }
2717
2718
2719 /*
2720 * 'cupsdWriteClient()' - Write data to a client as needed.
2721 */
2722
2723 void
2724 cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */
2725 {
2726 int bytes, /* Number of bytes written */
2727 field_col; /* Current column */
2728 char *bufptr, /* Pointer into buffer */
2729 *bufend; /* Pointer to end of buffer */
2730 ipp_state_t ipp_state; /* IPP state value */
2731
2732
2733 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2734 "cupsdWriteClient(con=%p(%d)) response=%p(%d), file=%d "
2735 "pipe_pid=%d state=%d",
2736 con, con->http.fd, con->response,
2737 con->response ? con->response->state : -1,
2738 con->file, con->pipe_pid, con->http.state);
2739
2740 if (con->http.state != HTTP_GET_SEND &&
2741 con->http.state != HTTP_POST_SEND)
2742 {
2743 /*
2744 * If we get called in the wrong state, then something went wrong with the
2745 * connection and we need to shut it down...
2746 */
2747
2748 cupsdLogMessage(CUPSD_LOG_DEBUG,
2749 "cupsdWriteClient: %d Closing on unknown HTTP state %d",
2750 con->http.fd, con->http.state);
2751 cupsdCloseClient(con);
2752 return;
2753 }
2754
2755 if (con->pipe_pid)
2756 {
2757 /*
2758 * Make sure we select on the CGI output...
2759 */
2760
2761 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
2762
2763 if (!con->file_ready)
2764 {
2765 /*
2766 * Try again later when there is CGI output available...
2767 */
2768
2769 cupsdRemoveSelect(con->http.fd);
2770 return;
2771 }
2772
2773 con->file_ready = 0;
2774 }
2775
2776 if (con->response && con->response->state != IPP_DATA)
2777 {
2778 ipp_state = ippWrite(HTTP(con), con->response);
2779 bytes = ipp_state != IPP_ERROR &&
2780 (con->file >= 0 || ipp_state != IPP_DATA);
2781 }
2782 else if ((bytes = read(con->file, con->header,
2783 sizeof(con->header) - con->header_used)) > 0)
2784 {
2785 con->header_used += bytes;
2786
2787 if (con->pipe_pid && !con->got_fields)
2788 {
2789 /*
2790 * Inspect the data for Content-Type and other fields.
2791 */
2792
2793 for (bufptr = con->header, bufend = con->header + con->header_used,
2794 field_col = 0;
2795 !con->got_fields && bufptr < bufend;
2796 bufptr ++)
2797 {
2798 if (*bufptr == '\n')
2799 {
2800 /*
2801 * Send line to client...
2802 */
2803
2804 if (bufptr > con->header && bufptr[-1] == '\r')
2805 bufptr[-1] = '\0';
2806 *bufptr++ = '\0';
2807
2808 cupsdLogMessage(CUPSD_LOG_DEBUG, "Script header: %s", con->header);
2809
2810 if (!con->sent_header)
2811 {
2812 /*
2813 * Handle redirection and CGI status codes...
2814 */
2815
2816 if (!_cups_strncasecmp(con->header, "Location:", 9))
2817 {
2818 if (!cupsdSendHeader(con, HTTP_SEE_OTHER, NULL, CUPSD_AUTH_NONE))
2819 {
2820 cupsdCloseClient(con);
2821 return;
2822 }
2823
2824 con->sent_header = 2;
2825
2826 if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0)
2827 return;
2828 }
2829 else if (!_cups_strncasecmp(con->header, "Status:", 7))
2830 {
2831 cupsdSendError(con, (http_status_t)atoi(con->header + 7),
2832 CUPSD_AUTH_NONE);
2833 con->sent_header = 2;
2834 }
2835 else
2836 {
2837 if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE))
2838 {
2839 cupsdCloseClient(con);
2840 return;
2841 }
2842
2843 con->sent_header = 1;
2844
2845 if (con->http.version == HTTP_1_1)
2846 {
2847 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
2848 return;
2849 }
2850 }
2851 }
2852
2853 if (_cups_strncasecmp(con->header, "Status:", 7))
2854 httpPrintf(HTTP(con), "%s\r\n", con->header);
2855
2856 /*
2857 * Update buffer...
2858 */
2859
2860 con->header_used -= bufptr - con->header;
2861
2862 if (con->header_used > 0)
2863 memmove(con->header, bufptr, con->header_used);
2864
2865 bufptr = con->header - 1;
2866
2867 /*
2868 * See if the line was empty...
2869 */
2870
2871 if (field_col == 0)
2872 {
2873 con->got_fields = 1;
2874
2875 if (cupsdFlushHeader(con) < 0)
2876 {
2877 cupsdCloseClient(con);
2878 return;
2879 }
2880
2881 if (con->http.version == HTTP_1_1)
2882 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
2883 }
2884 else
2885 field_col = 0;
2886 }
2887 else if (*bufptr != '\r')
2888 field_col ++;
2889 }
2890
2891 if (!con->got_fields)
2892 {
2893 con->http.activity = time(NULL);
2894 return;
2895 }
2896 }
2897
2898 if (con->header_used > 0)
2899 {
2900 if (httpWrite2(HTTP(con), con->header, con->header_used) < 0)
2901 {
2902 cupsdLogMessage(CUPSD_LOG_DEBUG,
2903 "cupsdWriteClient: %d Closing for error %d (%s)",
2904 con->http.fd, con->http.error,
2905 strerror(con->http.error));
2906 cupsdCloseClient(con);
2907 return;
2908 }
2909
2910 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
2911 httpFlushWrite(HTTP(con));
2912
2913 con->bytes += con->header_used;
2914
2915 if (con->http.state == HTTP_WAITING)
2916 bytes = 0;
2917 else
2918 bytes = con->header_used;
2919
2920 con->header_used = 0;
2921 }
2922 }
2923
2924 if (bytes <= 0 ||
2925 (con->http.state != HTTP_GET_SEND && con->http.state != HTTP_POST_SEND))
2926 {
2927 if (!con->sent_header && con->pipe_pid)
2928 cupsdSendError(con, HTTP_SERVER_ERROR, CUPSD_AUTH_NONE);
2929 else
2930 {
2931 cupsdLogRequest(con, HTTP_OK);
2932
2933 httpFlushWrite(HTTP(con));
2934
2935 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED && con->sent_header == 1)
2936 {
2937 if (httpWrite2(HTTP(con), "", 0) < 0)
2938 {
2939 cupsdLogMessage(CUPSD_LOG_DEBUG,
2940 "cupsdWriteClient: %d Closing for error %d (%s)",
2941 con->http.fd, con->http.error,
2942 strerror(con->http.error));
2943 cupsdCloseClient(con);
2944 return;
2945 }
2946 }
2947 }
2948
2949 con->http.state = HTTP_WAITING;
2950
2951 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
2952
2953 if (con->file >= 0)
2954 {
2955 cupsdRemoveSelect(con->file);
2956
2957 if (con->pipe_pid)
2958 cupsdEndProcess(con->pipe_pid, 0);
2959
2960 close(con->file);
2961 con->file = -1;
2962 con->pipe_pid = 0;
2963 }
2964
2965 if (con->filename)
2966 {
2967 unlink(con->filename);
2968 cupsdClearString(&con->filename);
2969 }
2970
2971 if (con->request)
2972 {
2973 ippDelete(con->request);
2974 con->request = NULL;
2975 }
2976
2977 if (con->response)
2978 {
2979 ippDelete(con->response);
2980 con->response = NULL;
2981 }
2982
2983 cupsdClearString(&con->command);
2984 cupsdClearString(&con->options);
2985 cupsdClearString(&con->query_string);
2986
2987 if (!con->http.keep_alive)
2988 {
2989 cupsdLogMessage(CUPSD_LOG_DEBUG,
2990 "cupsdWriteClient: %d Closing because Keep-Alive disabled",
2991 con->http.fd);
2992 cupsdCloseClient(con);
2993 return;
2994 }
2995 else
2996 {
2997 cupsArrayRemove(ActiveClients, con);
2998 cupsdSetBusyState();
2999 }
3000 }
3001
3002 con->http.activity = time(NULL);
3003 }
3004
3005
3006 /*
3007 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
3008 */
3009
3010 static int /* O - 1 if modified since */
3011 check_if_modified(
3012 cupsd_client_t *con, /* I - Client connection */
3013 struct stat *filestats) /* I - File information */
3014 {
3015 char *ptr; /* Pointer into field */
3016 time_t date; /* Time/date value */
3017 off_t size; /* Size/length value */
3018
3019
3020 size = 0;
3021 date = 0;
3022 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
3023
3024 if (*ptr == '\0')
3025 return (1);
3026
3027 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3028 "check_if_modified(con=%p(%d), "
3029 "filestats=%p(" CUPS_LLFMT ", %d)) If-Modified-Since=\"%s\"",
3030 con, con->http.fd, filestats, CUPS_LLCAST filestats->st_size,
3031 (int)filestats->st_mtime, ptr);
3032
3033 while (*ptr != '\0')
3034 {
3035 while (isspace(*ptr) || *ptr == ';')
3036 ptr ++;
3037
3038 if (_cups_strncasecmp(ptr, "length=", 7) == 0)
3039 {
3040 ptr += 7;
3041 size = strtoll(ptr, NULL, 10);
3042
3043 while (isdigit(*ptr))
3044 ptr ++;
3045 }
3046 else if (isalpha(*ptr))
3047 {
3048 date = httpGetDateTime(ptr);
3049 while (*ptr != '\0' && *ptr != ';')
3050 ptr ++;
3051 }
3052 else
3053 ptr ++;
3054 }
3055
3056 return ((size != filestats->st_size && size != 0) ||
3057 (date < filestats->st_mtime && date != 0) ||
3058 (size == 0 && date == 0));
3059 }
3060
3061
3062 /*
3063 * 'compare_clients()' - Compare two client connections.
3064 */
3065
3066 static int /* O - Result of comparison */
3067 compare_clients(cupsd_client_t *a, /* I - First client */
3068 cupsd_client_t *b, /* I - Second client */
3069 void *data) /* I - User data (not used) */
3070 {
3071 (void)data;
3072
3073 if (a == b)
3074 return (0);
3075 else if (a < b)
3076 return (-1);
3077 else
3078 return (1);
3079 }
3080
3081
3082 /*
3083 * 'data_ready()' - Check whether data is available from a client.
3084 */
3085
3086 static int /* O - 1 if data is ready, 0 otherwise */
3087 data_ready(cupsd_client_t *con) /* I - Client */
3088 {
3089 if (con->http.used > 0)
3090 return (1);
3091 #ifdef HAVE_SSL
3092 else if (con->http.tls)
3093 {
3094 # ifdef HAVE_LIBSSL
3095 if (SSL_pending((SSL *)(con->http.tls)))
3096 return (1);
3097 # elif defined(HAVE_GNUTLS)
3098 if (gnutls_record_check_pending(con->http.tls))
3099 return (1);
3100 # elif defined(HAVE_CDSASSL)
3101 size_t bytes; /* Bytes that are available */
3102
3103 if (!SSLGetBufferedReadSize(con->http.tls, &bytes) && bytes > 0)
3104 return (1);
3105 # endif /* HAVE_LIBSSL */
3106 }
3107 #endif /* HAVE_SSL */
3108
3109 return (0);
3110 }
3111
3112
3113 #ifdef HAVE_SSL
3114 /*
3115 * 'encrypt_client()' - Enable encryption for the client...
3116 */
3117
3118 static int /* O - 1 on success, 0 on error */
3119 encrypt_client(cupsd_client_t *con) /* I - Client to encrypt */
3120 {
3121 # ifdef HAVE_LIBSSL
3122 SSL_CTX *context; /* Context for encryption */
3123 BIO *bio; /* BIO data */
3124 unsigned long error; /* Error code */
3125
3126
3127 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3128 con->http.fd);
3129
3130 /*
3131 * Verify that we have a certificate...
3132 */
3133
3134 if (access(ServerKey, 0) || access(ServerCertificate, 0))
3135 {
3136 /*
3137 * Nope, make a self-signed certificate...
3138 */
3139
3140 if (!make_certificate(con))
3141 return (0);
3142 }
3143
3144 /*
3145 * Create the SSL context and accept the connection...
3146 */
3147
3148 context = SSL_CTX_new(SSLv23_server_method());
3149
3150 SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
3151 if (SSLOptions & CUPSD_SSL_NOEMPTY)
3152 SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
3153 SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
3154 SSL_CTX_use_certificate_chain_file(context, ServerCertificate);
3155
3156 bio = BIO_new(_httpBIOMethods());
3157 BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con));
3158
3159 con->http.tls = SSL_new(context);
3160 SSL_set_bio(con->http.tls, bio, bio);
3161
3162 if (SSL_accept(con->http.tls) != 1)
3163 {
3164 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to encrypt connection from %s!",
3165 con->http.hostname);
3166
3167 while ((error = ERR_get_error()) != 0)
3168 cupsdLogMessage(CUPSD_LOG_ERROR, "%s", ERR_error_string(error, NULL));
3169
3170 SSL_CTX_free(context);
3171 SSL_free(con->http.tls);
3172 con->http.tls = NULL;
3173 return (0);
3174 }
3175
3176 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3177 con->http.hostname);
3178
3179 return (1);
3180
3181 # elif defined(HAVE_GNUTLS)
3182 int error; /* Error code */
3183 gnutls_certificate_server_credentials *credentials;
3184 /* TLS credentials */
3185
3186
3187 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3188 con->http.fd);
3189
3190 /*
3191 * Verify that we have a certificate...
3192 */
3193
3194 if (access(ServerKey, 0) || access(ServerCertificate, 0))
3195 {
3196 /*
3197 * Nope, make a self-signed certificate...
3198 */
3199
3200 if (!make_certificate(con))
3201 return (0);
3202 }
3203
3204 /*
3205 * Create the SSL object and perform the SSL handshake...
3206 */
3207
3208 credentials = (gnutls_certificate_server_credentials *)
3209 malloc(sizeof(gnutls_certificate_server_credentials));
3210 if (credentials == NULL)
3211 {
3212 cupsdLogMessage(CUPSD_LOG_ERROR,
3213 "Unable to encrypt connection from %s - %s",
3214 con->http.hostname, strerror(errno));
3215
3216 return (0);
3217 }
3218
3219 gnutls_certificate_allocate_credentials(credentials);
3220 gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
3221 ServerKey, GNUTLS_X509_FMT_PEM);
3222
3223 gnutls_init(&con->http.tls), GNUTLS_SERVER);
3224 gnutls_set_default_priority(con->http.tls);
3225 gnutls_credentials_set(con->http.tls, GNUTLS_CRD_CERTIFICATE, *credentials);
3226 gnutls_transport_set_ptr(con->http.tls, (gnutls_transport_ptr)HTTP(con));
3227 gnutls_transport_set_pull_function(con->http.tls, _httpReadGNUTLS);
3228 gnutls_transport_set_push_function(con->http.tls, _httpWriteGNUTLS);
3229
3230 error = gnutls_handshake(con->http.tls);
3231
3232 if (error != GNUTLS_E_SUCCESS)
3233 {
3234 cupsdLogMessage(CUPSD_LOG_ERROR,
3235 "Unable to encrypt connection from %s - %s",
3236 con->http.hostname, gnutls_strerror(error));
3237
3238 gnutls_deinit(con->http.tls);
3239 gnutls_certificate_free_credentials(*credentials);
3240 con->http.tls = NULL;
3241 free(credentials);
3242 return (0);
3243 }
3244
3245 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3246 con->http.hostname);
3247
3248 con->http.tls_credentials = credentials;
3249 return (1);
3250
3251 # elif defined(HAVE_CDSASSL)
3252 OSStatus error = 0; /* Error code */
3253 CFArrayRef peerCerts; /* Peer certificates */
3254
3255
3256 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3257 con->http.fd);
3258
3259 con->http.tls_credentials = get_cdsa_certificate(con);
3260
3261 if (!con->http.tls_credentials)
3262 {
3263 /*
3264 * No keychain (yet), make a self-signed certificate...
3265 */
3266
3267 if (make_certificate(con))
3268 con->http.tls_credentials = get_cdsa_certificate(con);
3269 }
3270
3271 if (!con->http.tls_credentials)
3272 {
3273 cupsdLogMessage(CUPSD_LOG_ERROR,
3274 "Could not find signing key in keychain \"%s\"",
3275 ServerCertificate);
3276 error = errSSLBadCert; /* errSSLBadConfiguration is a better choice, but not available on 10.2.x */
3277 }
3278
3279 if (!error)
3280 error = SSLNewContext(true, &con->http.tls);
3281
3282 if (!error)
3283 error = SSLSetIOFuncs(con->http.tls, _httpReadCDSA, _httpWriteCDSA);
3284
3285 if (!error)
3286 error = SSLSetProtocolVersionEnabled(con->http.tls, kSSLProtocol2, false);
3287
3288 if (!error)
3289 error = SSLSetConnection(con->http.tls, HTTP(con));
3290
3291 if (!error)
3292 error = SSLSetAllowsExpiredCerts(con->http.tls, true);
3293
3294 if (!error)
3295 error = SSLSetAllowsAnyRoot(con->http.tls, true);
3296
3297 if (!error)
3298 error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
3299
3300 if (!error)
3301 {
3302 /*
3303 * Perform SSL/TLS handshake
3304 */
3305
3306 while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
3307 usleep(1000);
3308 }
3309
3310 if (error)
3311 {
3312 cupsdLogMessage(CUPSD_LOG_ERROR,
3313 "Unable to encrypt connection from %s - %s (%d)",
3314 con->http.hostname, cssmErrorString(error), (int)error);
3315
3316 con->http.error = error;
3317 con->http.status = HTTP_ERROR;
3318
3319 if (con->http.tls)
3320 {
3321 SSLDisposeContext(con->http.tls);
3322 con->http.tls = NULL;
3323 }
3324
3325 if (con->http.tls_credentials)
3326 {
3327 CFRelease(con->http.tls_credentials);
3328 con->http.tls_credentials = NULL;
3329 }
3330
3331 return (0);
3332 }
3333
3334 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3335 con->http.hostname);
3336
3337 if (!SSLCopyPeerCertificates(con->http.tls, &peerCerts) && peerCerts)
3338 {
3339 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates!",
3340 (int)CFArrayGetCount(peerCerts));
3341 CFRelease(peerCerts);
3342 }
3343 else
3344 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates!");
3345
3346 return (1);
3347
3348 # endif /* HAVE_LIBSSL */
3349 }
3350 #endif /* HAVE_SSL */
3351
3352
3353 #ifdef HAVE_CDSASSL
3354 /*
3355 * 'get_cdsa_certificate()' - Get a SSL/TLS certificate from the System keychain.
3356 */
3357
3358 static CFArrayRef /* O - Array of certificates */
3359 get_cdsa_certificate(
3360 cupsd_client_t *con) /* I - Client connection */
3361 {
3362 OSStatus err; /* Error info */
3363 SecKeychainRef keychain = NULL;/* Keychain reference */
3364 SecIdentitySearchRef search = NULL; /* Search reference */
3365 SecIdentityRef identity = NULL;/* Identity */
3366 CFArrayRef certificates = NULL;
3367 /* Certificate array */
3368 # if HAVE_SECPOLICYCREATESSL
3369 SecPolicyRef policy = NULL; /* Policy ref */
3370 CFStringRef servername = NULL;
3371 /* Server name */
3372 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
3373 char localname[1024];/* Local hostname */
3374 # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3375 SecPolicyRef policy = NULL; /* Policy ref */
3376 SecPolicySearchRef policy_search = NULL;
3377 /* Policy search ref */
3378 CSSM_DATA options; /* Policy options */
3379 CSSM_APPLE_TP_SSL_OPTIONS
3380 ssl_options; /* SSL Option for hostname */
3381 char localname[1024];/* Local hostname */
3382 # endif /* HAVE_SECPOLICYCREATESSL */
3383
3384
3385 cupsdLogMessage(CUPSD_LOG_DEBUG,
3386 "get_cdsa_certificate: Looking for certs for \"%s\"...",
3387 con->servername);
3388
3389 if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
3390 {
3391 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
3392 ServerCertificate, cssmErrorString(err), (int)err);
3393 goto cleanup;
3394 }
3395
3396 # if HAVE_SECPOLICYCREATESSL
3397 servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
3398 kCFStringEncodingUTF8);
3399
3400 policy = SecPolicyCreateSSL(1, servername);
3401
3402 if (servername)
3403 CFRelease(servername);
3404
3405 if (!policy)
3406 {
3407 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
3408 goto cleanup;
3409 }
3410
3411 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3412 &kCFTypeDictionaryKeyCallBacks,
3413 &kCFTypeDictionaryValueCallBacks)))
3414 {
3415 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary");
3416 goto cleanup;
3417 }
3418
3419 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
3420 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
3421 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
3422 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
3423
3424 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
3425
3426 if (err && DNSSDHostName)
3427 {
3428 /*
3429 * Search for the connection server name failed; try the DNS-SD .local
3430 * hostname instead...
3431 */
3432
3433 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
3434
3435 cupsdLogMessage(CUPSD_LOG_DEBUG,
3436 "get_cdsa_certificate: Looking for certs for \"%s\"...",
3437 localname);
3438
3439 servername = CFStringCreateWithCString(kCFAllocatorDefault, localname,
3440 kCFStringEncodingUTF8);
3441
3442 CFRelease(policy);
3443
3444 policy = SecPolicyCreateSSL(1, servername);
3445
3446 if (servername)
3447 CFRelease(servername);
3448
3449 if (!policy)
3450 {
3451 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
3452 goto cleanup;
3453 }
3454
3455 CFDictionarySetValue(query, kSecMatchPolicy, policy);
3456
3457 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
3458 }
3459
3460 if (err)
3461 {
3462 cupsdLogMessage(CUPSD_LOG_DEBUG,
3463 "Cannot find signing key in keychain \"%s\": %s (%d)",
3464 ServerCertificate, cssmErrorString(err), (int)err);
3465 goto cleanup;
3466 }
3467
3468 # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3469 /*
3470 * Use a policy to search for valid certificates whose common name matches the
3471 * servername...
3472 */
3473
3474 if (SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_SSL,
3475 NULL, &policy_search))
3476 {
3477 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create a policy search reference");
3478 goto cleanup;
3479 }
3480
3481 if (SecPolicySearchCopyNext(policy_search, &policy))
3482 {
3483 cupsdLogMessage(CUPSD_LOG_ERROR,
3484 "Cannot find a policy to use for searching");
3485 goto cleanup;
3486 }
3487
3488 memset(&ssl_options, 0, sizeof(ssl_options));
3489 ssl_options.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
3490 ssl_options.ServerName = con->servername;
3491 ssl_options.ServerNameLen = strlen(con->servername);
3492
3493 options.Data = (uint8 *)&ssl_options;
3494 options.Length = sizeof(ssl_options);
3495
3496 if (SecPolicySetValue(policy, &options))
3497 {
3498 cupsdLogMessage(CUPSD_LOG_ERROR,
3499 "Cannot set policy value to use for searching");
3500 goto cleanup;
3501 }
3502
3503 if ((err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
3504 keychain, FALSE, &search)))
3505 {
3506 cupsdLogMessage(CUPSD_LOG_ERROR,
3507 "Cannot create identity search reference: %s (%d)",
3508 cssmErrorString(err), (int)err);
3509 goto cleanup;
3510 }
3511
3512 err = SecIdentitySearchCopyNext(search, &identity);
3513
3514 if (err && DNSSDHostName)
3515 {
3516 /*
3517 * Search for the connection server name failed; try the DNS-SD .local
3518 * hostname instead...
3519 */
3520
3521 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
3522
3523 ssl_options.ServerName = localname;
3524 ssl_options.ServerNameLen = strlen(localname);
3525
3526 cupsdLogMessage(CUPSD_LOG_DEBUG,
3527 "get_cdsa_certificate: Looking for certs for \"%s\"...",
3528 localname);
3529
3530 if (SecPolicySetValue(policy, &options))
3531 {
3532 cupsdLogMessage(CUPSD_LOG_ERROR,
3533 "Cannot set policy value to use for searching");
3534 goto cleanup;
3535 }
3536
3537 CFRelease(search);
3538 search = NULL;
3539 if ((err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
3540 keychain, FALSE, &search)))
3541 {
3542 cupsdLogMessage(CUPSD_LOG_ERROR,
3543 "Cannot create identity search reference: %s (%d)",
3544 cssmErrorString(err), (int)err);
3545 goto cleanup;
3546 }
3547
3548 err = SecIdentitySearchCopyNext(search, &identity);
3549
3550 }
3551
3552 if (err)
3553 {
3554 cupsdLogMessage(CUPSD_LOG_DEBUG,
3555 "Cannot find signing key in keychain \"%s\": %s (%d)",
3556 ServerCertificate, cssmErrorString(err), (int)err);
3557 goto cleanup;
3558 }
3559
3560 # else
3561 /*
3562 * Assume there is exactly one SecIdentity in the keychain...
3563 */
3564
3565 if ((err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_SIGN, &search)))
3566 {
3567 cupsdLogMessage(CUPSD_LOG_DEBUG,
3568 "Cannot create identity search reference (%d)", (int)err);
3569 goto cleanup;
3570 }
3571
3572 if ((err = SecIdentitySearchCopyNext(search, &identity)))
3573 {
3574 cupsdLogMessage(CUPSD_LOG_DEBUG,
3575 "Cannot find signing key in keychain \"%s\": %s (%d)",
3576 ServerCertificate, cssmErrorString(err), (int)err);
3577 goto cleanup;
3578 }
3579 # endif /* HAVE_SECPOLICYCREATESSL */
3580
3581 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
3582 {
3583 cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure!");
3584 goto cleanup;
3585 }
3586
3587 if ((certificates = CFArrayCreate(NULL, (const void **)&identity,
3588 1, &kCFTypeArrayCallBacks)) == NULL)
3589 {
3590 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
3591 goto cleanup;
3592 }
3593
3594 cleanup :
3595
3596 if (keychain)
3597 CFRelease(keychain);
3598 if (search)
3599 CFRelease(search);
3600 if (identity)
3601 CFRelease(identity);
3602
3603 # if HAVE_SECPOLICYCREATESSL
3604 if (policy)
3605 CFRelease(policy);
3606 if (query)
3607 CFRelease(query);
3608 # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3609 if (policy)
3610 CFRelease(policy);
3611 if (policy_search)
3612 CFRelease(policy_search);
3613 # endif /* HAVE_SECPOLICYCREATESSL */
3614
3615 return (certificates);
3616 }
3617 #endif /* HAVE_CDSASSL */
3618
3619
3620 /*
3621 * 'get_file()' - Get a filename and state info.
3622 */
3623
3624 static char * /* O - Real filename */
3625 get_file(cupsd_client_t *con, /* I - Client connection */
3626 struct stat *filestats, /* O - File information */
3627 char *filename, /* IO - Filename buffer */
3628 int len) /* I - Buffer length */
3629 {
3630 int status; /* Status of filesystem calls */
3631 char *ptr; /* Pointer info filename */
3632 int plen; /* Remaining length after pointer */
3633 char language[7]; /* Language subdirectory, if any */
3634
3635
3636 /*
3637 * Figure out the real filename...
3638 */
3639
3640 language[0] = '\0';
3641
3642 if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/'))
3643 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
3644 else if (!strncmp(con->uri, "/icons/", 7) && !strchr(con->uri + 7, '/'))
3645 {
3646 snprintf(filename, len, "%s/%s", CacheDir, con->uri + 7);
3647 if (access(filename, F_OK) < 0)
3648 snprintf(filename, len, "%s/images/generic.png", DocumentRoot);
3649 }
3650 else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/'))
3651 snprintf(filename, len, "%s/rss/%s", CacheDir, con->uri + 5);
3652 else if (!strncmp(con->uri, "/admin/conf/", 12))
3653 snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
3654 else if (!strncmp(con->uri, "/admin/log/", 11))
3655 {
3656 if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/')
3657 strlcpy(filename, AccessLog, len);
3658 else if (!strncmp(con->uri + 11, "error_log", 9) && ErrorLog[0] == '/')
3659 strlcpy(filename, ErrorLog, len);
3660 else if (!strncmp(con->uri + 11, "page_log", 8) && PageLog[0] == '/')
3661 strlcpy(filename, PageLog, len);
3662 else
3663 return (NULL);
3664 }
3665 else if (con->language)
3666 {
3667 snprintf(language, sizeof(language), "/%s", con->language->language);
3668 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3669 }
3670 else
3671 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
3672
3673 if ((ptr = strchr(filename, '?')) != NULL)
3674 *ptr = '\0';
3675
3676 /*
3677 * Grab the status for this language; if there isn't a language-specific file
3678 * then fallback to the default one...
3679 */
3680
3681 if ((status = stat(filename, filestats)) != 0 && language[0] &&
3682 strncmp(con->uri, "/icons/", 7) &&
3683 strncmp(con->uri, "/ppd/", 5) &&
3684 strncmp(con->uri, "/rss/", 5) &&
3685 strncmp(con->uri, "/admin/conf/", 12) &&
3686 strncmp(con->uri, "/admin/log/", 11))
3687 {
3688 /*
3689 * Drop the country code...
3690 */
3691
3692 language[3] = '\0';
3693 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3694
3695 if ((ptr = strchr(filename, '?')) != NULL)
3696 *ptr = '\0';
3697
3698 if ((status = stat(filename, filestats)) != 0)
3699 {
3700 /*
3701 * Drop the language prefix and try the root directory...
3702 */
3703
3704 language[0] = '\0';
3705 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
3706
3707 if ((ptr = strchr(filename, '?')) != NULL)
3708 *ptr = '\0';
3709
3710 status = stat(filename, filestats);
3711 }
3712 }
3713
3714 /*
3715 * If we're found a directory, get the index.html file instead...
3716 */
3717
3718 if (!status && S_ISDIR(filestats->st_mode))
3719 {
3720 /*
3721 * Make sure the URI ends with a slash...
3722 */
3723
3724 if (con->uri[strlen(con->uri) - 1] != '/')
3725 strlcat(con->uri, "/", sizeof(con->uri));
3726
3727 /*
3728 * Find the directory index file, trying every language...
3729 */
3730
3731 do
3732 {
3733 if (status && language[0])
3734 {
3735 /*
3736 * Try a different language subset...
3737 */
3738
3739 if (language[3])
3740 language[0] = '\0'; /* Strip country code */
3741 else
3742 language[0] = '\0'; /* Strip language */
3743 }
3744
3745 /*
3746 * Look for the index file...
3747 */
3748
3749 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3750
3751 if ((ptr = strchr(filename, '?')) != NULL)
3752 *ptr = '\0';
3753
3754 ptr = filename + strlen(filename);
3755 plen = len - (ptr - filename);
3756
3757 strlcpy(ptr, "index.html", plen);
3758 status = stat(filename, filestats);
3759
3760 #ifdef HAVE_JAVA
3761 if (status)
3762 {
3763 strlcpy(ptr, "index.class", plen);
3764 status = stat(filename, filestats);
3765 }
3766 #endif /* HAVE_JAVA */
3767
3768 #ifdef HAVE_PERL
3769 if (status)
3770 {
3771 strlcpy(ptr, "index.pl", plen);
3772 status = stat(filename, filestats);
3773 }
3774 #endif /* HAVE_PERL */
3775
3776 #ifdef HAVE_PHP
3777 if (status)
3778 {
3779 strlcpy(ptr, "index.php", plen);
3780 status = stat(filename, filestats);
3781 }
3782 #endif /* HAVE_PHP */
3783
3784 #ifdef HAVE_PYTHON
3785 if (status)
3786 {
3787 strlcpy(ptr, "index.pyc", plen);
3788 status = stat(filename, filestats);
3789 }
3790
3791 if (status)
3792 {
3793 strlcpy(ptr, "index.py", plen);
3794 status = stat(filename, filestats);
3795 }
3796 #endif /* HAVE_PYTHON */
3797
3798 }
3799 while (status && language[0]);
3800 }
3801
3802 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3803 "get_file(con=%p(%d), filestats=%p, filename=%p, len=%d) = "
3804 "%s", con, con->http.fd, filestats, filename, len,
3805 status ? "(null)" : filename);
3806
3807 if (status)
3808 return (NULL);
3809 else
3810 return (filename);
3811 }
3812
3813
3814 /*
3815 * 'install_conf_file()' - Install a configuration file.
3816 */
3817
3818 static http_status_t /* O - Status */
3819 install_conf_file(cupsd_client_t *con) /* I - Connection */
3820 {
3821 char filename[1024]; /* Configuration filename */
3822 mode_t mode; /* Permissions */
3823 cups_file_t *in, /* Input file */
3824 *out; /* Output file */
3825 char buffer[16384]; /* Copy buffer */
3826 ssize_t bytes; /* Number of bytes */
3827
3828
3829 /*
3830 * Open the request file...
3831 */
3832
3833 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
3834 {
3835 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open request file \"%s\": %s",
3836 con->filename, strerror(errno));
3837 return (HTTP_SERVER_ERROR);
3838 }
3839
3840 /*
3841 * Open the new config file...
3842 */
3843
3844 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri + 11);
3845 if (!strcmp(con->uri, "/admin/conf/printers.conf"))
3846 mode = ConfigFilePerm & 0600;
3847 else
3848 mode = ConfigFilePerm;
3849
3850 if ((out = cupsdCreateConfFile(filename, mode)) == NULL)
3851 {
3852 cupsFileClose(in);
3853 return (HTTP_SERVER_ERROR);
3854 }
3855
3856 cupsdLogMessage(CUPSD_LOG_INFO, "Installing config file \"%s\"...", filename);
3857
3858 /*
3859 * Copy from the request to the new config file...
3860 */
3861
3862 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
3863 if (cupsFileWrite(out, buffer, bytes) < bytes)
3864 {
3865 cupsdLogMessage(CUPSD_LOG_ERROR,
3866 "Unable to copy to config file \"%s\": %s",
3867 filename, strerror(errno));
3868
3869 cupsFileClose(in);
3870 cupsFileClose(out);
3871
3872 snprintf(filename, sizeof(filename), "%s%s.N", ServerRoot, con->uri + 11);
3873 cupsdRemoveFile(filename);
3874
3875 return (HTTP_SERVER_ERROR);
3876 }
3877
3878 /*
3879 * Close the files...
3880 */
3881
3882 cupsFileClose(in);
3883
3884 if (cupsdCloseCreatedConfFile(out, filename))
3885 return (HTTP_SERVER_ERROR);
3886
3887 /*
3888 * Remove the request file...
3889 */
3890
3891 cupsdRemoveFile(con->filename);
3892 cupsdClearString(&con->filename);
3893
3894 /*
3895 * If the cupsd.conf file was updated, set the NeedReload flag...
3896 */
3897
3898 if (!strcmp(con->uri, "/admin/conf/cupsd.conf"))
3899 NeedReload = RELOAD_CUPSD;
3900 else
3901 NeedReload = RELOAD_ALL;
3902
3903 ReloadTime = time(NULL);
3904
3905 /*
3906 * Return that the file was created successfully...
3907 */
3908
3909 return (HTTP_CREATED);
3910 }
3911
3912
3913 /*
3914 * 'is_cgi()' - Is the resource a CGI script/program?
3915 */
3916
3917 static int /* O - 1 = CGI, 0 = file */
3918 is_cgi(cupsd_client_t *con, /* I - Client connection */
3919 const char *filename, /* I - Real filename */
3920 struct stat *filestats, /* I - File information */
3921 mime_type_t *type) /* I - MIME type */
3922 {
3923 const char *options; /* Options on URL */
3924
3925
3926 /*
3927 * Get the options, if any...
3928 */
3929
3930 if ((options = strchr(con->uri, '?')) != NULL)
3931 {
3932 options ++;
3933 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", options);
3934 }
3935
3936 /*
3937 * Check for known types...
3938 */
3939
3940 if (!type || _cups_strcasecmp(type->super, "application"))
3941 {
3942 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3943 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
3944 "type=%s/%s) = 0", con, con->http.fd, filename, filestats,
3945 type ? type->super : "unknown",
3946 type ? type->type : "unknown");
3947 return (0);
3948 }
3949
3950 if (!_cups_strcasecmp(type->type, "x-httpd-cgi") &&
3951 (filestats->st_mode & 0111))
3952 {
3953 /*
3954 * "application/x-httpd-cgi" is a CGI script.
3955 */
3956
3957 cupsdSetString(&con->command, filename);
3958
3959 if (options)
3960 cupsdSetStringf(&con->options, " %s", options);
3961
3962 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3963 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
3964 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
3965 type->super, type->type);
3966 return (1);
3967 }
3968 #ifdef HAVE_JAVA
3969 else if (!_cups_strcasecmp(type->type, "x-httpd-java"))
3970 {
3971 /*
3972 * "application/x-httpd-java" is a Java servlet.
3973 */
3974
3975 cupsdSetString(&con->command, CUPS_JAVA);
3976
3977 if (options)
3978 cupsdSetStringf(&con->options, " %s %s", filename, options);
3979 else
3980 cupsdSetStringf(&con->options, " %s", filename);
3981
3982 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3983 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
3984 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
3985 type->super, type->type);
3986 return (1);
3987 }
3988 #endif /* HAVE_JAVA */
3989 #ifdef HAVE_PERL
3990 else if (!_cups_strcasecmp(type->type, "x-httpd-perl"))
3991 {
3992 /*
3993 * "application/x-httpd-perl" is a Perl page.
3994 */
3995
3996 cupsdSetString(&con->command, CUPS_PERL);
3997
3998 if (options)
3999 cupsdSetStringf(&con->options, " %s %s", filename, options);
4000 else
4001 cupsdSetStringf(&con->options, " %s", filename);
4002
4003 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4004 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4005 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4006 type->super, type->type);
4007 return (1);
4008 }
4009 #endif /* HAVE_PERL */
4010 #ifdef HAVE_PHP
4011 else if (!_cups_strcasecmp(type->type, "x-httpd-php"))
4012 {
4013 /*
4014 * "application/x-httpd-php" is a PHP page.
4015 */
4016
4017 cupsdSetString(&con->command, CUPS_PHP);
4018
4019 if (options)
4020 cupsdSetStringf(&con->options, " %s %s", filename, options);
4021 else
4022 cupsdSetStringf(&con->options, " %s", filename);
4023
4024 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4025 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4026 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4027 type->super, type->type);
4028 return (1);
4029 }
4030 #endif /* HAVE_PHP */
4031 #ifdef HAVE_PYTHON
4032 else if (!_cups_strcasecmp(type->type, "x-httpd-python"))
4033 {
4034 /*
4035 * "application/x-httpd-python" is a Python page.
4036 */
4037
4038 cupsdSetString(&con->command, CUPS_PYTHON);
4039
4040 if (options)
4041 cupsdSetStringf(&con->options, " %s %s", filename, options);
4042 else
4043 cupsdSetStringf(&con->options, " %s", filename);
4044
4045 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4046 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4047 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4048 type->super, type->type);
4049 return (1);
4050 }
4051 #endif /* HAVE_PYTHON */
4052
4053 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4054 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4055 "type=%s/%s) = 0", con, con->http.fd, filename, filestats,
4056 type->super, type->type);
4057 return (0);
4058 }
4059
4060
4061 /*
4062 * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. "..").
4063 */
4064
4065 static int /* O - 0 if relative, 1 if absolute */
4066 is_path_absolute(const char *path) /* I - Input path */
4067 {
4068 /*
4069 * Check for a leading slash...
4070 */
4071
4072 if (path[0] != '/')
4073 return (0);
4074
4075 /*
4076 * Check for "/.." in the path...
4077 */
4078
4079 while ((path = strstr(path, "/..")) != NULL)
4080 {
4081 if (!path[3] || path[3] == '/')
4082 return (0);
4083
4084 path ++;
4085 }
4086
4087 /*
4088 * If we haven't found any relative paths, return 1 indicating an
4089 * absolute path...
4090 */
4091
4092 return (1);
4093 }
4094
4095
4096 #ifdef HAVE_SSL
4097 /*
4098 * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
4099 */
4100
4101 static int /* O - 1 on success, 0 on failure */
4102 make_certificate(cupsd_client_t *con) /* I - Client connection */
4103 {
4104 #if defined(HAVE_LIBSSL) && defined(HAVE_WAITPID)
4105 int pid, /* Process ID of command */
4106 status; /* Status of command */
4107 char command[1024], /* Command */
4108 *argv[12], /* Command-line arguments */
4109 *envp[MAX_ENV + 1], /* Environment variables */
4110 infofile[1024], /* Type-in information for cert */
4111 seedfile[1024]; /* Random number seed file */
4112 int envc, /* Number of environment variables */
4113 bytes; /* Bytes written */
4114 cups_file_t *fp; /* Seed/info file */
4115 int infofd; /* Info file descriptor */
4116
4117
4118 /*
4119 * Run the "openssl" command to seed the random number generator and
4120 * generate a self-signed certificate that is good for 10 years:
4121 *
4122 * openssl rand -rand seedfile 1
4123 *
4124 * openssl req -new -x509 -keyout ServerKey \
4125 * -out ServerCertificate -days 3650 -nodes
4126 *
4127 * The seeding step is crucial in ensuring that the openssl command
4128 * does not block on systems without sufficient entropy...
4129 */
4130
4131 if (!cupsFileFind("openssl", getenv("PATH"), 1, command, sizeof(command)))
4132 {
4133 cupsdLogMessage(CUPSD_LOG_ERROR,
4134 "No SSL certificate and openssl command not found!");
4135 return (0);
4136 }
4137
4138 if (access("/dev/urandom", 0))
4139 {
4140 /*
4141 * If the system doesn't provide /dev/urandom, then any random source
4142 * will probably be blocking-style, so generate some random data to
4143 * use as a seed for the certificate. Note that we have already
4144 * seeded the random number generator in cupsdInitCerts()...
4145 */
4146
4147 cupsdLogMessage(CUPSD_LOG_INFO,
4148 "Seeding the random number generator...");
4149
4150 /*
4151 * Write the seed file...
4152 */
4153
4154 if ((fp = cupsTempFile2(seedfile, sizeof(seedfile))) == NULL)
4155 {
4156 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create seed file %s - %s",
4157 seedfile, strerror(errno));
4158 return (0);
4159 }
4160
4161 for (bytes = 0; bytes < 262144; bytes ++)
4162 cupsFilePutChar(fp, random());
4163
4164 cupsFileClose(fp);
4165
4166 /*
4167 * Run the openssl command to seed its random number generator...
4168 */
4169
4170 argv[0] = "openssl";
4171 argv[1] = "rand";
4172 argv[2] = "-rand";
4173 argv[3] = seedfile;
4174 argv[4] = "1";
4175 argv[5] = NULL;
4176
4177 envc = cupsdLoadEnv(envp, MAX_ENV);
4178 envp[envc] = NULL;
4179
4180 if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, NULL,
4181 NULL, &pid))
4182 {
4183 unlink(seedfile);
4184 return (0);
4185 }
4186
4187 while (waitpid(pid, &status, 0) < 0)
4188 if (errno != EINTR)
4189 {
4190 status = 1;
4191 break;
4192 }
4193
4194 cupsdFinishProcess(pid, command, sizeof(command), NULL);
4195
4196 /*
4197 * Remove the seed file, as it is no longer needed...
4198 */
4199
4200 unlink(seedfile);
4201
4202 if (status)
4203 {
4204 if (WIFEXITED(status))
4205 cupsdLogMessage(CUPSD_LOG_ERROR,
4206 "Unable to seed random number generator - "
4207 "the openssl command stopped with status %d!",
4208 WEXITSTATUS(status));
4209 else
4210 cupsdLogMessage(CUPSD_LOG_ERROR,
4211 "Unable to seed random number generator - "
4212 "the openssl command crashed on signal %d!",
4213 WTERMSIG(status));
4214
4215 return (0);
4216 }
4217 }
4218
4219 /*
4220 * Create a file with the certificate information fields...
4221 *
4222 * Note: This assumes that the default questions are asked by the openssl
4223 * command...
4224 */
4225
4226 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
4227 {
4228 cupsdLogMessage(CUPSD_LOG_ERROR,
4229 "Unable to create certificate information file %s - %s",
4230 infofile, strerror(errno));
4231 return (0);
4232 }
4233
4234 cupsFilePrintf(fp, ".\n.\n.\n%s\n.\n%s\n%s\n",
4235 ServerName, ServerName, ServerAdmin);
4236 cupsFileClose(fp);
4237
4238 cupsdLogMessage(CUPSD_LOG_INFO,
4239 "Generating SSL server key and certificate...");
4240
4241 argv[0] = "openssl";
4242 argv[1] = "req";
4243 argv[2] = "-new";
4244 argv[3] = "-x509";
4245 argv[4] = "-keyout";
4246 argv[5] = ServerKey;
4247 argv[6] = "-out";
4248 argv[7] = ServerCertificate;
4249 argv[8] = "-days";
4250 argv[9] = "3650";
4251 argv[10] = "-nodes";
4252 argv[11] = NULL;
4253
4254 cupsdLoadEnv(envp, MAX_ENV);
4255
4256 infofd = open(infofile, O_RDONLY);
4257
4258 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
4259 NULL, &pid))
4260 {
4261 close(infofd);
4262 unlink(infofile);
4263 return (0);
4264 }
4265
4266 close(infofd);
4267 unlink(infofile);
4268
4269 while (waitpid(pid, &status, 0) < 0)
4270 if (errno != EINTR)
4271 {
4272 status = 1;
4273 break;
4274 }
4275
4276 cupsdFinishProcess(pid, command, sizeof(command), NULL);
4277
4278 if (status)
4279 {
4280 if (WIFEXITED(status))
4281 cupsdLogMessage(CUPSD_LOG_ERROR,
4282 "Unable to create SSL server key and certificate - "
4283 "the openssl command stopped with status %d!",
4284 WEXITSTATUS(status));
4285 else
4286 cupsdLogMessage(CUPSD_LOG_ERROR,
4287 "Unable to create SSL server key and certificate - "
4288 "the openssl command crashed on signal %d!",
4289 WTERMSIG(status));
4290 }
4291 else
4292 {
4293 cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
4294 ServerKey);
4295 cupsdLogMessage(CUPSD_LOG_INFO,
4296 "Created SSL server certificate file \"%s\"...",
4297 ServerCertificate);
4298 }
4299
4300 return (!status);
4301
4302 #elif defined(HAVE_GNUTLS)
4303 gnutls_x509_crt crt; /* Self-signed certificate */
4304 gnutls_x509_privkey key; /* Encryption key */
4305 cups_lang_t *language; /* Default language info */
4306 cups_file_t *fp; /* Key/cert file */
4307 unsigned char buffer[8192]; /* Buffer for x509 data */
4308 size_t bytes; /* Number of bytes of data */
4309 unsigned char serial[4]; /* Serial number buffer */
4310 time_t curtime; /* Current time */
4311 int result; /* Result of GNU TLS calls */
4312
4313
4314 /*
4315 * Create the encryption key...
4316 */
4317
4318 cupsdLogMessage(CUPSD_LOG_INFO, "Generating SSL server key...");
4319
4320 gnutls_x509_privkey_init(&key);
4321 gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
4322
4323 /*
4324 * Save it...
4325 */
4326
4327 bytes = sizeof(buffer);
4328
4329 if ((result = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM,
4330 buffer, &bytes)) < 0)
4331 {
4332 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to export SSL server key - %s",
4333 gnutls_strerror(result));
4334 gnutls_x509_privkey_deinit(key);
4335 return (0);
4336 }
4337 else if ((fp = cupsFileOpen(ServerKey, "w")) != NULL)
4338 {
4339 cupsFileWrite(fp, (char *)buffer, bytes);
4340 cupsFileClose(fp);
4341
4342 cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
4343 ServerKey);
4344 }
4345 else
4346 {
4347 cupsdLogMessage(CUPSD_LOG_ERROR,
4348 "Unable to create SSL server key file \"%s\" - %s",
4349 ServerKey, strerror(errno));
4350 gnutls_x509_privkey_deinit(key);
4351 return (0);
4352 }
4353
4354 /*
4355 * Create the self-signed certificate...
4356 */
4357
4358 cupsdLogMessage(CUPSD_LOG_INFO, "Generating self-signed SSL certificate...");
4359
4360 language = cupsLangDefault();
4361 curtime = time(NULL);
4362 serial[0] = curtime >> 24;
4363 serial[1] = curtime >> 16;
4364 serial[2] = curtime >> 8;
4365 serial[3] = curtime;
4366
4367 gnutls_x509_crt_init(&crt);
4368 if (strlen(language->language) == 5)
4369 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
4370 language->language + 3, 2);
4371 else
4372 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
4373 "US", 2);
4374 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0,
4375 ServerName, strlen(ServerName));
4376 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
4377 ServerName, strlen(ServerName));
4378 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
4379 0, "Unknown", 7);
4380 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0,
4381 "Unknown", 7);
4382 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0,
4383 "Unknown", 7);
4384 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0,
4385 ServerAdmin, strlen(ServerAdmin));
4386 gnutls_x509_crt_set_key(crt, key);
4387 gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
4388 gnutls_x509_crt_set_activation_time(crt, curtime);
4389 gnutls_x509_crt_set_expiration_time(crt, curtime + 10 * 365 * 86400);
4390 gnutls_x509_crt_set_ca_status(crt, 0);
4391 gnutls_x509_crt_set_subject_alternative_name(crt, GNUTLS_SAN_DNSNAME,
4392 ServerName);
4393 gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
4394 gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_KEY_ENCIPHERMENT);
4395 gnutls_x509_crt_set_version(crt, 3);
4396
4397 bytes = sizeof(buffer);
4398 if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0)
4399 gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes);
4400
4401 gnutls_x509_crt_sign(crt, crt, key);
4402
4403 /*
4404 * Save it...
4405 */
4406
4407 bytes = sizeof(buffer);
4408 if ((result = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM,
4409 buffer, &bytes)) < 0)
4410 cupsdLogMessage(CUPSD_LOG_ERROR,
4411 "Unable to export SSL server certificate - %s",
4412 gnutls_strerror(result));
4413 else if ((fp = cupsFileOpen(ServerCertificate, "w")) != NULL)
4414 {
4415 cupsFileWrite(fp, (char *)buffer, bytes);
4416 cupsFileClose(fp);
4417
4418 cupsdLogMessage(CUPSD_LOG_INFO,
4419 "Created SSL server certificate file \"%s\"...",
4420 ServerCertificate);
4421 }
4422 else
4423 cupsdLogMessage(CUPSD_LOG_ERROR,
4424 "Unable to create SSL server certificate file \"%s\" - %s",
4425 ServerCertificate, strerror(errno));
4426
4427 /*
4428 * Cleanup...
4429 */
4430
4431 gnutls_x509_crt_deinit(crt);
4432 gnutls_x509_privkey_deinit(key);
4433
4434 return (1);
4435
4436 #elif defined(HAVE_CDSASSL) && defined(HAVE_WAITPID)
4437 int pid, /* Process ID of command */
4438 status; /* Status of command */
4439 char command[1024], /* Command */
4440 *argv[4], /* Command-line arguments */
4441 *envp[MAX_ENV + 1], /* Environment variables */
4442 keychain[1024], /* Keychain argument */
4443 infofile[1024], /* Type-in information for cert */
4444 localname[1024], /* Local hostname */
4445 *servername; /* Name of server in cert */
4446 cups_file_t *fp; /* Seed/info file */
4447 int infofd; /* Info file descriptor */
4448
4449
4450 if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
4451 {
4452 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
4453 servername = localname;
4454 }
4455 else
4456 servername = con->servername;
4457
4458 /*
4459 * Run the "certtool" command to generate a self-signed certificate...
4460 */
4461
4462 if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
4463 {
4464 cupsdLogMessage(CUPSD_LOG_ERROR,
4465 "No SSL certificate and certtool command not found!");
4466 return (0);
4467 }
4468
4469 /*
4470 * Create a file with the certificate information fields...
4471 *
4472 * Note: This assumes that the default questions are asked by the certtool
4473 * command...
4474 */
4475
4476 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
4477 {
4478 cupsdLogMessage(CUPSD_LOG_ERROR,
4479 "Unable to create certificate information file %s - %s",
4480 infofile, strerror(errno));
4481 return (0);
4482 }
4483
4484 cupsFilePrintf(fp, "%s\nr\n\ny\nb\ns\ny\n%s\n\n\n\n\n%s\ny\n",
4485 servername, servername, ServerAdmin);
4486 cupsFileClose(fp);
4487
4488 cupsdLogMessage(CUPSD_LOG_INFO,
4489 "Generating SSL server key and certificate...");
4490
4491 snprintf(keychain, sizeof(keychain), "k=%s", ServerCertificate);
4492
4493 argv[0] = "certtool";
4494 argv[1] = "c";
4495 argv[2] = keychain;
4496 argv[3] = NULL;
4497
4498 cupsdLoadEnv(envp, MAX_ENV);
4499
4500 infofd = open(infofile, O_RDONLY);
4501
4502 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
4503 NULL, &pid))
4504 {
4505 close(infofd);
4506 unlink(infofile);
4507 return (0);
4508 }
4509
4510 close(infofd);
4511 unlink(infofile);
4512
4513 while (waitpid(pid, &status, 0) < 0)
4514 if (errno != EINTR)
4515 {
4516 status = 1;
4517 break;
4518 }
4519
4520 cupsdFinishProcess(pid, command, sizeof(command), NULL);
4521
4522 if (status)
4523 {
4524 if (WIFEXITED(status))
4525 cupsdLogMessage(CUPSD_LOG_ERROR,
4526 "Unable to create SSL server key and certificate - "
4527 "the certtool command stopped with status %d!",
4528 WEXITSTATUS(status));
4529 else
4530 cupsdLogMessage(CUPSD_LOG_ERROR,
4531 "Unable to create SSL server key and certificate - "
4532 "the certtool command crashed on signal %d!",
4533 WTERMSIG(status));
4534 }
4535 else
4536 {
4537 cupsdLogMessage(CUPSD_LOG_INFO,
4538 "Created SSL server certificate file \"%s\"...",
4539 ServerCertificate);
4540 }
4541
4542 return (!status);
4543
4544 #else
4545 return (0);
4546 #endif /* HAVE_LIBSSL && HAVE_WAITPID */
4547 }
4548 #endif /* HAVE_SSL */
4549
4550
4551 /*
4552 * 'pipe_command()' - Pipe the output of a command to the remote client.
4553 */
4554
4555 static int /* O - Process ID */
4556 pipe_command(cupsd_client_t *con, /* I - Client connection */
4557 int infile, /* I - Standard input for command */
4558 int *outfile, /* O - Standard output for command */
4559 char *command, /* I - Command to run */
4560 char *options, /* I - Options for command */
4561 int root) /* I - Run as root? */
4562 {
4563 int i; /* Looping var */
4564 int pid; /* Process ID */
4565 char *commptr, /* Command string pointer */
4566 commch; /* Command string character */
4567 char *uriptr; /* URI string pointer */
4568 int fds[2]; /* Pipe FDs */
4569 int argc; /* Number of arguments */
4570 int envc; /* Number of environment variables */
4571 char argbuf[10240], /* Argument buffer */
4572 *argv[100], /* Argument strings */
4573 *envp[MAX_ENV + 20]; /* Environment variables */
4574 char auth_type[256], /* AUTH_TYPE environment variable */
4575 content_length[1024], /* CONTENT_LENGTH environment variable */
4576 content_type[1024], /* CONTENT_TYPE environment variable */
4577 http_cookie[32768], /* HTTP_COOKIE environment variable */
4578 http_referer[1024], /* HTTP_REFERER environment variable */
4579 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
4580 lang[1024], /* LANG environment variable */
4581 path_info[1024], /* PATH_INFO environment variable */
4582 remote_addr[1024], /* REMOTE_ADDR environment variable */
4583 remote_host[1024], /* REMOTE_HOST environment variable */
4584 remote_user[1024], /* REMOTE_USER environment variable */
4585 script_filename[1024], /* SCRIPT_FILENAME environment variable */
4586 script_name[1024], /* SCRIPT_NAME environment variable */
4587 server_name[1024], /* SERVER_NAME environment variable */
4588 server_port[1024]; /* SERVER_PORT environment variable */
4589 ipp_attribute_t *attr; /* attributes-natural-language attribute */
4590 void *ccache = NULL; /* Kerberos credentials */
4591
4592
4593 /*
4594 * Parse a copy of the options string, which is of the form:
4595 *
4596 * argument+argument+argument
4597 * ?argument+argument+argument
4598 * param=value&param=value
4599 * ?param=value&param=value
4600 * /name?argument+argument+argument
4601 * /name?param=value&param=value
4602 *
4603 * If the string contains an "=" character after the initial name,
4604 * then we treat it as a HTTP GET form request and make a copy of
4605 * the remaining string for the environment variable.
4606 *
4607 * The string is always parsed out as command-line arguments, to
4608 * be consistent with Apache...
4609 */
4610
4611 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4612 "pipe_command(con=%p(%d), infile=%d, outfile=%p, "
4613 "command=\"%s\", options=\"%s\", root=%d)",
4614 con, con->http.fd, infile, outfile, command,
4615 options ? options : "(null)", root);
4616
4617 argv[0] = command;
4618
4619 if (options)
4620 {
4621 commptr = options;
4622 if (*commptr == ' ')
4623 commptr ++;
4624 strlcpy(argbuf, commptr, sizeof(argbuf));
4625 }
4626 else
4627 argbuf[0] = '\0';
4628
4629 if (argbuf[0] == '/')
4630 {
4631 /*
4632 * Found some trailing path information, set PATH_INFO...
4633 */
4634
4635 if ((commptr = strchr(argbuf, '?')) == NULL)
4636 commptr = argbuf + strlen(argbuf);
4637
4638 commch = *commptr;
4639 *commptr = '\0';
4640 snprintf(path_info, sizeof(path_info), "PATH_INFO=%s", argbuf);
4641 *commptr = commch;
4642 }
4643 else
4644 {
4645 commptr = argbuf;
4646 path_info[0] = '\0';
4647
4648 if (*commptr == ' ')
4649 commptr ++;
4650 }
4651
4652 if (*commptr == '?' && con->operation == HTTP_GET && !con->query_string)
4653 {
4654 commptr ++;
4655 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", commptr);
4656 }
4657
4658 argc = 1;
4659
4660 if (*commptr)
4661 {
4662 argv[argc ++] = commptr;
4663
4664 for (; *commptr && argc < 99; commptr ++)
4665 {
4666 /*
4667 * Break arguments whenever we see a + or space...
4668 */
4669
4670 if (*commptr == ' ' || *commptr == '+')
4671 {
4672 while (*commptr == ' ' || *commptr == '+')
4673 *commptr++ = '\0';
4674
4675 /*
4676 * If we don't have a blank string, save it as another argument...
4677 */
4678
4679 if (*commptr)
4680 {
4681 argv[argc] = commptr;
4682 argc ++;
4683 }
4684 else
4685 break;
4686 }
4687 else if (*commptr == '%' && isxdigit(commptr[1] & 255) &&
4688 isxdigit(commptr[2] & 255))
4689 {
4690 /*
4691 * Convert the %xx notation to the individual character.
4692 */
4693
4694 if (commptr[1] >= '0' && commptr[1] <= '9')
4695 *commptr = (commptr[1] - '0') << 4;
4696 else
4697 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
4698
4699 if (commptr[2] >= '0' && commptr[2] <= '9')
4700 *commptr |= commptr[2] - '0';
4701 else
4702 *commptr |= tolower(commptr[2]) - 'a' + 10;
4703
4704 _cups_strcpy(commptr + 1, commptr + 3);
4705
4706 /*
4707 * Check for a %00 and break if that is the case...
4708 */
4709
4710 if (!*commptr)
4711 break;
4712 }
4713 }
4714 }
4715
4716 argv[argc] = NULL;
4717
4718 /*
4719 * Setup the environment variables as needed...
4720 */
4721
4722 if (con->username[0])
4723 {
4724 snprintf(auth_type, sizeof(auth_type), "AUTH_TYPE=%s",
4725 httpGetField(HTTP(con), HTTP_FIELD_AUTHORIZATION));
4726
4727 if ((uriptr = strchr(auth_type + 10, ' ')) != NULL)
4728 *uriptr = '\0';
4729 }
4730 else
4731 auth_type[0] = '\0';
4732
4733 if (con->request &&
4734 (attr = ippFindAttribute(con->request, "attributes-natural-language",
4735 IPP_TAG_LANGUAGE)) != NULL)
4736 {
4737 switch (strlen(attr->values[0].string.text))
4738 {
4739 default :
4740 /*
4741 * This is an unknown or badly formatted language code; use
4742 * the POSIX locale...
4743 */
4744
4745 strcpy(lang, "LANG=C");
4746 break;
4747
4748 case 2 :
4749 /*
4750 * Just the language code (ll)...
4751 */
4752
4753 snprintf(lang, sizeof(lang), "LANG=%s.UTF8",
4754 attr->values[0].string.text);
4755 break;
4756
4757 case 5 :
4758 /*
4759 * Language and country code (ll-cc)...
4760 */
4761
4762 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF8",
4763 attr->values[0].string.text[0],
4764 attr->values[0].string.text[1],
4765 toupper(attr->values[0].string.text[3] & 255),
4766 toupper(attr->values[0].string.text[4] & 255));
4767 break;
4768 }
4769 }
4770 else if (con->language)
4771 snprintf(lang, sizeof(lang), "LANG=%s.UTF8", con->language->language);
4772 else
4773 strcpy(lang, "LANG=C");
4774
4775 strcpy(remote_addr, "REMOTE_ADDR=");
4776 httpAddrString(con->http.hostaddr, remote_addr + 12,
4777 sizeof(remote_addr) - 12);
4778
4779 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s",
4780 con->http.hostname);
4781
4782 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
4783 if ((uriptr = strchr(script_name, '?')) != NULL)
4784 *uriptr = '\0';
4785
4786 snprintf(script_filename, sizeof(script_filename), "SCRIPT_FILENAME=%s%s",
4787 DocumentRoot, script_name + 12);
4788
4789 sprintf(server_port, "SERVER_PORT=%d", con->serverport);
4790
4791 if (con->http.fields[HTTP_FIELD_HOST][0])
4792 {
4793 char *nameptr; /* Pointer to ":port" */
4794
4795 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
4796 con->http.fields[HTTP_FIELD_HOST]);
4797 if ((nameptr = strrchr(server_name, ':')) != NULL && !strchr(nameptr, ']'))
4798 *nameptr = '\0'; /* Strip trailing ":port" */
4799 }
4800 else
4801 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
4802 con->servername);
4803
4804 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
4805
4806 if (auth_type[0])
4807 envp[envc ++] = auth_type;
4808
4809 envp[envc ++] = lang;
4810 envp[envc ++] = "REDIRECT_STATUS=1";
4811 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
4812 envp[envc ++] = server_name;
4813 envp[envc ++] = server_port;
4814 envp[envc ++] = remote_addr;
4815 envp[envc ++] = remote_host;
4816 envp[envc ++] = script_name;
4817 envp[envc ++] = script_filename;
4818
4819 if (path_info[0])
4820 envp[envc ++] = path_info;
4821
4822 if (con->username[0])
4823 {
4824 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
4825
4826 envp[envc ++] = remote_user;
4827 }
4828
4829 if (con->http.version == HTTP_1_1)
4830 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
4831 else if (con->http.version == HTTP_1_0)
4832 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
4833 else
4834 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
4835
4836 if (con->http.cookie)
4837 {
4838 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
4839 con->http.cookie);
4840 envp[envc ++] = http_cookie;
4841 }
4842
4843 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
4844 {
4845 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
4846 con->http.fields[HTTP_FIELD_USER_AGENT]);
4847 envp[envc ++] = http_user_agent;
4848 }
4849
4850 if (con->http.fields[HTTP_FIELD_REFERER][0])
4851 {
4852 snprintf(http_referer, sizeof(http_referer), "HTTP_REFERER=%s",
4853 con->http.fields[HTTP_FIELD_REFERER]);
4854 envp[envc ++] = http_referer;
4855 }
4856
4857 if (con->operation == HTTP_GET)
4858 {
4859 envp[envc ++] = "REQUEST_METHOD=GET";
4860
4861 if (con->query_string)
4862 {
4863 /*
4864 * Add GET form variables after ?...
4865 */
4866
4867 envp[envc ++] = con->query_string;
4868 }
4869 else
4870 envp[envc ++] = "QUERY_STRING=";
4871 }
4872 else
4873 {
4874 sprintf(content_length, "CONTENT_LENGTH=" CUPS_LLFMT,
4875 CUPS_LLCAST con->bytes);
4876 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
4877 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
4878
4879 envp[envc ++] = "REQUEST_METHOD=POST";
4880 envp[envc ++] = content_length;
4881 envp[envc ++] = content_type;
4882 }
4883
4884 /*
4885 * Tell the CGI if we are using encryption...
4886 */
4887
4888 if (con->http.tls)
4889 envp[envc ++] = "HTTPS=ON";
4890
4891 /*
4892 * Terminate the environment array...
4893 */
4894
4895 envp[envc] = NULL;
4896
4897 if (LogLevel >= CUPSD_LOG_DEBUG)
4898 {
4899 for (i = 0; i < argc; i ++)
4900 cupsdLogMessage(CUPSD_LOG_DEBUG,
4901 "[CGI] argv[%d] = \"%s\"", i, argv[i]);
4902 for (i = 0; i < envc; i ++)
4903 cupsdLogMessage(CUPSD_LOG_DEBUG,
4904 "[CGI] envp[%d] = \"%s\"", i, envp[i]);
4905 }
4906
4907 /*
4908 * Create a pipe for the output...
4909 */
4910
4911 if (cupsdOpenPipe(fds))
4912 {
4913 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to create pipe for %s - %s",
4914 argv[0], strerror(errno));
4915 return (0);
4916 }
4917
4918 /*
4919 * Then execute the command...
4920 */
4921
4922 if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1],
4923 -1, -1, root, DefaultProfile, NULL, &pid) < 0)
4924 {
4925 /*
4926 * Error - can't fork!
4927 */
4928
4929 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to start %s - %s", argv[0],
4930 strerror(errno));
4931
4932 cupsdClosePipe(fds);
4933 pid = 0;
4934 }
4935 else
4936 {
4937 /*
4938 * Fork successful - return the PID...
4939 */
4940
4941 if (con->username[0])
4942 cupsdAddCert(pid, con->username, ccache);
4943
4944 cupsdLogMessage(CUPSD_LOG_DEBUG, "[CGI] Started %s (PID %d)", command, pid);
4945
4946 *outfile = fds[0];
4947 close(fds[1]);
4948 }
4949
4950 return (pid);
4951 }
4952
4953
4954 /*
4955 * 'valid_host()' - Is the Host: field valid?
4956 */
4957
4958 static int /* O - 1 if valid, 0 if not */
4959 valid_host(cupsd_client_t *con) /* I - Client connection */
4960 {
4961 cupsd_alias_t *a; /* Current alias */
4962 cupsd_netif_t *netif; /* Current network interface */
4963 const char *host, /* Host field */
4964 *end; /* End character */
4965
4966
4967 host = con->http.fields[HTTP_FIELD_HOST];
4968
4969 if (httpAddrLocalhost(con->http.hostaddr))
4970 {
4971 /*
4972 * Only allow "localhost" or the equivalent IPv4 or IPv6 numerical
4973 * addresses when accessing CUPS via the loopback interface...
4974 */
4975
4976 return (!_cups_strcasecmp(host, "localhost") ||
4977 !_cups_strncasecmp(host, "localhost:", 10) ||
4978 !_cups_strcasecmp(host, "localhost.") ||
4979 !_cups_strncasecmp(host, "localhost.:", 11) ||
4980 #ifdef __linux
4981 !_cups_strcasecmp(host, "localhost.localdomain") ||
4982 !_cups_strncasecmp(host, "localhost.localdomain:", 22) ||
4983 #endif /* __linux */
4984 !strcmp(host, "127.0.0.1") ||
4985 !strncmp(host, "127.0.0.1:", 10) ||
4986 !strcmp(host, "[::1]") ||
4987 !strncmp(host, "[::1]:", 6));
4988 }
4989
4990 #ifdef HAVE_DNSSD
4991 /*
4992 * Check if the hostname is something.local (Bonjour); if so, allow it.
4993 */
4994
4995 if ((end = strrchr(host, '.')) != NULL &&
4996 (!_cups_strcasecmp(end, ".local") || !_cups_strncasecmp(end, ".local:", 7) ||
4997 !_cups_strcasecmp(end, ".local.") || !_cups_strncasecmp(end, ".local.:", 8)))
4998 return (1);
4999 #endif /* HAVE_DNSSD */
5000
5001 /*
5002 * Check if the hostname is an IP address...
5003 */
5004
5005 if (isdigit(*host & 255) || *host == '[')
5006 {
5007 /*
5008 * Possible IPv4/IPv6 address...
5009 */
5010
5011 char temp[1024], /* Temporary string */
5012 *ptr; /* Pointer into temporary string */
5013 http_addrlist_t *addrlist; /* List of addresses */
5014
5015
5016 strlcpy(temp, host, sizeof(temp));
5017 if ((ptr = strrchr(temp, ':')) != NULL && !strchr(ptr, ']'))
5018 *ptr = '\0'; /* Strip :port from host value */
5019
5020 if ((addrlist = httpAddrGetList(temp, AF_UNSPEC, NULL)) != NULL)
5021 {
5022 /*
5023 * Good IPv4/IPv6 address...
5024 */
5025
5026 httpAddrFreeList(addrlist);
5027 return (1);
5028 }
5029 }
5030
5031 /*
5032 * Check for (alias) name matches...
5033 */
5034
5035 for (a = (cupsd_alias_t *)cupsArrayFirst(ServerAlias);
5036 a;
5037 a = (cupsd_alias_t *)cupsArrayNext(ServerAlias))
5038 {
5039 /*
5040 * "ServerAlias *" allows all host values through...
5041 */
5042
5043 if (!strcmp(a->name, "*"))
5044 return (1);
5045
5046 if (!_cups_strncasecmp(host, a->name, a->namelen))
5047 {
5048 /*
5049 * Prefix matches; check the character at the end - it must be ":", ".",
5050 * ".:", or nul...
5051 */
5052
5053 end = host + a->namelen;
5054
5055 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5056 return (1);
5057 }
5058 }
5059
5060 #ifdef HAVE_DNSSD
5061 for (a = (cupsd_alias_t *)cupsArrayFirst(DNSSDAlias);
5062 a;
5063 a = (cupsd_alias_t *)cupsArrayNext(DNSSDAlias))
5064 {
5065 /*
5066 * "ServerAlias *" allows all host values through...
5067 */
5068
5069 if (!strcmp(a->name, "*"))
5070 return (1);
5071
5072 if (!_cups_strncasecmp(host, a->name, a->namelen))
5073 {
5074 /*
5075 * Prefix matches; check the character at the end - it must be ":", ".",
5076 * ".:", or nul...
5077 */
5078
5079 end = host + a->namelen;
5080
5081 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5082 return (1);
5083 }
5084 }
5085 #endif /* HAVE_DNSSD */
5086
5087 /*
5088 * Check for interface hostname matches...
5089 */
5090
5091 for (netif = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
5092 netif;
5093 netif = (cupsd_netif_t *)cupsArrayNext(NetIFList))
5094 {
5095 if (!_cups_strncasecmp(host, netif->hostname, netif->hostlen))
5096 {
5097 /*
5098 * Prefix matches; check the character at the end - it must be ":", ".",
5099 * ".:", or nul...
5100 */
5101
5102 end = host + netif->hostlen;
5103
5104 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5105 return (1);
5106 }
5107 }
5108
5109 return (0);
5110 }
5111
5112
5113 /*
5114 * 'write_file()' - Send a file via HTTP.
5115 */
5116
5117 static int /* O - 0 on failure, 1 on success */
5118 write_file(cupsd_client_t *con, /* I - Client connection */
5119 http_status_t code, /* I - HTTP status */
5120 char *filename, /* I - Filename */
5121 char *type, /* I - File type */
5122 struct stat *filestats) /* O - File information */
5123 {
5124 con->file = open(filename, O_RDONLY);
5125
5126 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5127 "write_file(con=%p(%d), code=%d, filename=\"%s\" (%d), "
5128 "type=\"%s\", filestats=%p)", con, con->http.fd,
5129 code, filename, con->file, type ? type : "(null)", filestats);
5130
5131 if (con->file < 0)
5132 return (0);
5133
5134 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
5135
5136 con->pipe_pid = 0;
5137
5138 if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
5139 return (0);
5140
5141 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
5142 httpGetDateString(filestats->st_mtime)) < 0)
5143 return (0);
5144 if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n",
5145 CUPS_LLCAST filestats->st_size) < 0)
5146 return (0);
5147 if (httpPrintf(HTTP(con), "\r\n") < 0)
5148 return (0);
5149
5150 if (cupsdFlushHeader(con) < 0)
5151 return (0);
5152
5153 con->http.data_encoding = HTTP_ENCODE_LENGTH;
5154 con->http.data_remaining = filestats->st_size;
5155
5156 if (con->http.data_remaining <= INT_MAX)
5157 con->http._data_remaining = con->http.data_remaining;
5158 else
5159 con->http._data_remaining = INT_MAX;
5160
5161 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
5162 (cupsd_selfunc_t)cupsdWriteClient, con);
5163
5164 return (1);
5165 }
5166
5167
5168 /*
5169 * 'write_pipe()' - Flag that data is available on the CGI pipe.
5170 */
5171
5172 static void
5173 write_pipe(cupsd_client_t *con) /* I - Client connection */
5174 {
5175 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5176 "write_pipe(con=%p(%d)) CGI output on fd %d",
5177 con, con->http.fd, con->file);
5178
5179 con->file_ready = 1;
5180
5181 cupsdRemoveSelect(con->file);
5182 cupsdAddSelect(con->http.fd, NULL, (cupsd_selfunc_t)cupsdWriteClient, con);
5183 }
5184
5185
5186 /*
5187 * End of "$Id: client.c 7950 2008-09-17 00:21:59Z mike $".
5188 */