]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/client.c
Merge changes from CUPS 1.6svn-r9939.
[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 status; /* Error code */
3183 gnutls_certificate_server_credentials *credentials;
3184 /* TLS credentials */
3185 const char *priority; /* Priority string */
3186
3187
3188 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3189 con->http.fd);
3190
3191 /*
3192 * Verify that we have a certificate...
3193 */
3194
3195 if (access(ServerKey, 0) || access(ServerCertificate, 0))
3196 {
3197 /*
3198 * Nope, make a self-signed certificate...
3199 */
3200
3201 if (!make_certificate(con))
3202 return (0);
3203 }
3204
3205 /*
3206 * Create the SSL object and perform the SSL handshake...
3207 */
3208
3209 credentials = (gnutls_certificate_server_credentials *)
3210 malloc(sizeof(gnutls_certificate_server_credentials));
3211 if (credentials == NULL)
3212 {
3213 cupsdLogMessage(CUPSD_LOG_ERROR,
3214 "Unable to encrypt connection from %s - %s",
3215 con->http.hostname, strerror(errno));
3216
3217 return (0);
3218 }
3219
3220 gnutls_certificate_allocate_credentials(credentials);
3221 gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
3222 ServerKey, GNUTLS_X509_FMT_PEM);
3223
3224 gnutls_init(&con->http.tls, GNUTLS_SERVER);
3225 gnutls_set_default_priority(con->http.tls);
3226 status = gnutls_priority_set_direct(con->http.tls,
3227 "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.0:"
3228 "+VERS-SSL3.0:%COMPAT", &priority);
3229 if (status != GNUTLS_E_SUCCESS)
3230 {
3231 cupsdLogMessage(CUPSD_LOG_ERROR,
3232 "Unable to encrypt connection from %s - %s (%s)",
3233 con->http.hostname, gnutls_strerror(status), priority);
3234
3235 gnutls_deinit(con->http.tls);
3236 gnutls_certificate_free_credentials(*credentials);
3237 con->http.tls = NULL;
3238 free(credentials);
3239 return (0);
3240 }
3241
3242 gnutls_credentials_set(con->http.tls, GNUTLS_CRD_CERTIFICATE, *credentials);
3243 gnutls_transport_set_ptr(con->http.tls, (gnutls_transport_ptr)HTTP(con));
3244 gnutls_transport_set_pull_function(con->http.tls, _httpReadGNUTLS);
3245 gnutls_transport_set_push_function(con->http.tls, _httpWriteGNUTLS);
3246
3247 while ((status = gnutls_handshake(con->http.tls)) != GNUTLS_E_SUCCESS)
3248 {
3249 if (gnutls_error_is_fatal(status))
3250 {
3251 cupsdLogMessage(CUPSD_LOG_ERROR,
3252 "Unable to encrypt connection from %s - %s",
3253 con->http.hostname, gnutls_strerror(status));
3254
3255 gnutls_deinit(con->http.tls);
3256 gnutls_certificate_free_credentials(*credentials);
3257 con->http.tls = NULL;
3258 free(credentials);
3259 return (0);
3260 }
3261 }
3262
3263 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3264 con->http.hostname);
3265
3266 con->http.tls_credentials = credentials;
3267 return (1);
3268
3269 # elif defined(HAVE_CDSASSL)
3270 OSStatus error = 0; /* Error code */
3271 CFArrayRef peerCerts; /* Peer certificates */
3272
3273
3274 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3275 con->http.fd);
3276
3277 con->http.tls_credentials = get_cdsa_certificate(con);
3278
3279 if (!con->http.tls_credentials)
3280 {
3281 /*
3282 * No keychain (yet), make a self-signed certificate...
3283 */
3284
3285 if (make_certificate(con))
3286 con->http.tls_credentials = get_cdsa_certificate(con);
3287 }
3288
3289 if (!con->http.tls_credentials)
3290 {
3291 cupsdLogMessage(CUPSD_LOG_ERROR,
3292 "Could not find signing key in keychain \"%s\"",
3293 ServerCertificate);
3294 error = errSSLBadCert; /* errSSLBadConfiguration is a better choice, but not available on 10.2.x */
3295 }
3296
3297 if (!error)
3298 error = SSLNewContext(true, &con->http.tls);
3299
3300 if (!error)
3301 error = SSLSetIOFuncs(con->http.tls, _httpReadCDSA, _httpWriteCDSA);
3302
3303 if (!error)
3304 error = SSLSetConnection(con->http.tls, HTTP(con));
3305
3306 if (!error)
3307 error = SSLSetAllowsExpiredCerts(con->http.tls, true);
3308
3309 if (!error)
3310 error = SSLSetAllowsAnyRoot(con->http.tls, true);
3311
3312 if (!error)
3313 error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
3314
3315 if (!error)
3316 {
3317 /*
3318 * Perform SSL/TLS handshake
3319 */
3320
3321 while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
3322 usleep(1000);
3323 }
3324
3325 if (error)
3326 {
3327 cupsdLogMessage(CUPSD_LOG_ERROR,
3328 "Unable to encrypt connection from %s - %s (%d)",
3329 con->http.hostname, cssmErrorString(error), (int)error);
3330
3331 con->http.error = error;
3332 con->http.status = HTTP_ERROR;
3333
3334 if (con->http.tls)
3335 {
3336 SSLDisposeContext(con->http.tls);
3337 con->http.tls = NULL;
3338 }
3339
3340 if (con->http.tls_credentials)
3341 {
3342 CFRelease(con->http.tls_credentials);
3343 con->http.tls_credentials = NULL;
3344 }
3345
3346 return (0);
3347 }
3348
3349 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3350 con->http.hostname);
3351
3352 if (!SSLCopyPeerCertificates(con->http.tls, &peerCerts) && peerCerts)
3353 {
3354 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates!",
3355 (int)CFArrayGetCount(peerCerts));
3356 CFRelease(peerCerts);
3357 }
3358 else
3359 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates!");
3360
3361 return (1);
3362
3363 # endif /* HAVE_LIBSSL */
3364 }
3365 #endif /* HAVE_SSL */
3366
3367
3368 #ifdef HAVE_CDSASSL
3369 /*
3370 * 'get_cdsa_certificate()' - Get a SSL/TLS certificate from the System keychain.
3371 */
3372
3373 static CFArrayRef /* O - Array of certificates */
3374 get_cdsa_certificate(
3375 cupsd_client_t *con) /* I - Client connection */
3376 {
3377 OSStatus err; /* Error info */
3378 SecKeychainRef keychain = NULL;/* Keychain reference */
3379 SecIdentitySearchRef search = NULL; /* Search reference */
3380 SecIdentityRef identity = NULL;/* Identity */
3381 CFArrayRef certificates = NULL;
3382 /* Certificate array */
3383 # if HAVE_SECPOLICYCREATESSL
3384 SecPolicyRef policy = NULL; /* Policy ref */
3385 CFStringRef servername = NULL;
3386 /* Server name */
3387 CFMutableDictionaryRef query = NULL; /* Query qualifiers */
3388 char localname[1024];/* Local hostname */
3389 # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3390 SecPolicyRef policy = NULL; /* Policy ref */
3391 SecPolicySearchRef policy_search = NULL;
3392 /* Policy search ref */
3393 CSSM_DATA options; /* Policy options */
3394 CSSM_APPLE_TP_SSL_OPTIONS
3395 ssl_options; /* SSL Option for hostname */
3396 char localname[1024];/* Local hostname */
3397 # endif /* HAVE_SECPOLICYCREATESSL */
3398
3399
3400 cupsdLogMessage(CUPSD_LOG_DEBUG,
3401 "get_cdsa_certificate: Looking for certs for \"%s\"...",
3402 con->servername);
3403
3404 if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
3405 {
3406 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
3407 ServerCertificate, cssmErrorString(err), (int)err);
3408 goto cleanup;
3409 }
3410
3411 # if HAVE_SECPOLICYCREATESSL
3412 servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
3413 kCFStringEncodingUTF8);
3414
3415 policy = SecPolicyCreateSSL(1, servername);
3416
3417 if (servername)
3418 CFRelease(servername);
3419
3420 if (!policy)
3421 {
3422 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
3423 goto cleanup;
3424 }
3425
3426 if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
3427 &kCFTypeDictionaryKeyCallBacks,
3428 &kCFTypeDictionaryValueCallBacks)))
3429 {
3430 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary");
3431 goto cleanup;
3432 }
3433
3434 CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
3435 CFDictionaryAddValue(query, kSecMatchPolicy, policy);
3436 CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
3437 CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
3438
3439 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
3440
3441 if (err && DNSSDHostName)
3442 {
3443 /*
3444 * Search for the connection server name failed; try the DNS-SD .local
3445 * hostname instead...
3446 */
3447
3448 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
3449
3450 cupsdLogMessage(CUPSD_LOG_DEBUG,
3451 "get_cdsa_certificate: Looking for certs for \"%s\"...",
3452 localname);
3453
3454 servername = CFStringCreateWithCString(kCFAllocatorDefault, localname,
3455 kCFStringEncodingUTF8);
3456
3457 CFRelease(policy);
3458
3459 policy = SecPolicyCreateSSL(1, servername);
3460
3461 if (servername)
3462 CFRelease(servername);
3463
3464 if (!policy)
3465 {
3466 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
3467 goto cleanup;
3468 }
3469
3470 CFDictionarySetValue(query, kSecMatchPolicy, policy);
3471
3472 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
3473 }
3474
3475 if (err)
3476 {
3477 cupsdLogMessage(CUPSD_LOG_DEBUG,
3478 "Cannot find signing key in keychain \"%s\": %s (%d)",
3479 ServerCertificate, cssmErrorString(err), (int)err);
3480 goto cleanup;
3481 }
3482
3483 # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3484 /*
3485 * Use a policy to search for valid certificates whose common name matches the
3486 * servername...
3487 */
3488
3489 if (SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_SSL,
3490 NULL, &policy_search))
3491 {
3492 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create a policy search reference");
3493 goto cleanup;
3494 }
3495
3496 if (SecPolicySearchCopyNext(policy_search, &policy))
3497 {
3498 cupsdLogMessage(CUPSD_LOG_ERROR,
3499 "Cannot find a policy to use for searching");
3500 goto cleanup;
3501 }
3502
3503 memset(&ssl_options, 0, sizeof(ssl_options));
3504 ssl_options.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
3505 ssl_options.ServerName = con->servername;
3506 ssl_options.ServerNameLen = strlen(con->servername);
3507
3508 options.Data = (uint8 *)&ssl_options;
3509 options.Length = sizeof(ssl_options);
3510
3511 if (SecPolicySetValue(policy, &options))
3512 {
3513 cupsdLogMessage(CUPSD_LOG_ERROR,
3514 "Cannot set policy value to use for searching");
3515 goto cleanup;
3516 }
3517
3518 if ((err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
3519 keychain, FALSE, &search)))
3520 {
3521 cupsdLogMessage(CUPSD_LOG_ERROR,
3522 "Cannot create identity search reference: %s (%d)",
3523 cssmErrorString(err), (int)err);
3524 goto cleanup;
3525 }
3526
3527 err = SecIdentitySearchCopyNext(search, &identity);
3528
3529 if (err && DNSSDHostName)
3530 {
3531 /*
3532 * Search for the connection server name failed; try the DNS-SD .local
3533 * hostname instead...
3534 */
3535
3536 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
3537
3538 ssl_options.ServerName = localname;
3539 ssl_options.ServerNameLen = strlen(localname);
3540
3541 cupsdLogMessage(CUPSD_LOG_DEBUG,
3542 "get_cdsa_certificate: Looking for certs for \"%s\"...",
3543 localname);
3544
3545 if (SecPolicySetValue(policy, &options))
3546 {
3547 cupsdLogMessage(CUPSD_LOG_ERROR,
3548 "Cannot set policy value to use for searching");
3549 goto cleanup;
3550 }
3551
3552 CFRelease(search);
3553 search = NULL;
3554 if ((err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
3555 keychain, FALSE, &search)))
3556 {
3557 cupsdLogMessage(CUPSD_LOG_ERROR,
3558 "Cannot create identity search reference: %s (%d)",
3559 cssmErrorString(err), (int)err);
3560 goto cleanup;
3561 }
3562
3563 err = SecIdentitySearchCopyNext(search, &identity);
3564
3565 }
3566
3567 if (err)
3568 {
3569 cupsdLogMessage(CUPSD_LOG_DEBUG,
3570 "Cannot find signing key in keychain \"%s\": %s (%d)",
3571 ServerCertificate, cssmErrorString(err), (int)err);
3572 goto cleanup;
3573 }
3574
3575 # else
3576 /*
3577 * Assume there is exactly one SecIdentity in the keychain...
3578 */
3579
3580 if ((err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_SIGN, &search)))
3581 {
3582 cupsdLogMessage(CUPSD_LOG_DEBUG,
3583 "Cannot create identity search reference (%d)", (int)err);
3584 goto cleanup;
3585 }
3586
3587 if ((err = SecIdentitySearchCopyNext(search, &identity)))
3588 {
3589 cupsdLogMessage(CUPSD_LOG_DEBUG,
3590 "Cannot find signing key in keychain \"%s\": %s (%d)",
3591 ServerCertificate, cssmErrorString(err), (int)err);
3592 goto cleanup;
3593 }
3594 # endif /* HAVE_SECPOLICYCREATESSL */
3595
3596 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
3597 {
3598 cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure!");
3599 goto cleanup;
3600 }
3601
3602 if ((certificates = CFArrayCreate(NULL, (const void **)&identity,
3603 1, &kCFTypeArrayCallBacks)) == NULL)
3604 {
3605 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
3606 goto cleanup;
3607 }
3608
3609 cleanup :
3610
3611 if (keychain)
3612 CFRelease(keychain);
3613 if (search)
3614 CFRelease(search);
3615 if (identity)
3616 CFRelease(identity);
3617
3618 # if HAVE_SECPOLICYCREATESSL
3619 if (policy)
3620 CFRelease(policy);
3621 if (query)
3622 CFRelease(query);
3623 # elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3624 if (policy)
3625 CFRelease(policy);
3626 if (policy_search)
3627 CFRelease(policy_search);
3628 # endif /* HAVE_SECPOLICYCREATESSL */
3629
3630 return (certificates);
3631 }
3632 #endif /* HAVE_CDSASSL */
3633
3634
3635 /*
3636 * 'get_file()' - Get a filename and state info.
3637 */
3638
3639 static char * /* O - Real filename */
3640 get_file(cupsd_client_t *con, /* I - Client connection */
3641 struct stat *filestats, /* O - File information */
3642 char *filename, /* IO - Filename buffer */
3643 int len) /* I - Buffer length */
3644 {
3645 int status; /* Status of filesystem calls */
3646 char *ptr; /* Pointer info filename */
3647 int plen; /* Remaining length after pointer */
3648 char language[7]; /* Language subdirectory, if any */
3649
3650
3651 /*
3652 * Figure out the real filename...
3653 */
3654
3655 language[0] = '\0';
3656
3657 if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/'))
3658 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
3659 else if (!strncmp(con->uri, "/icons/", 7) && !strchr(con->uri + 7, '/'))
3660 {
3661 snprintf(filename, len, "%s/%s", CacheDir, con->uri + 7);
3662 if (access(filename, F_OK) < 0)
3663 snprintf(filename, len, "%s/images/generic.png", DocumentRoot);
3664 }
3665 else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/'))
3666 snprintf(filename, len, "%s/rss/%s", CacheDir, con->uri + 5);
3667 else if (!strncmp(con->uri, "/admin/conf/", 12))
3668 snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
3669 else if (!strncmp(con->uri, "/admin/log/", 11))
3670 {
3671 if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/')
3672 strlcpy(filename, AccessLog, len);
3673 else if (!strncmp(con->uri + 11, "error_log", 9) && ErrorLog[0] == '/')
3674 strlcpy(filename, ErrorLog, len);
3675 else if (!strncmp(con->uri + 11, "page_log", 8) && PageLog[0] == '/')
3676 strlcpy(filename, PageLog, len);
3677 else
3678 return (NULL);
3679 }
3680 else if (con->language)
3681 {
3682 snprintf(language, sizeof(language), "/%s", con->language->language);
3683 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3684 }
3685 else
3686 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
3687
3688 if ((ptr = strchr(filename, '?')) != NULL)
3689 *ptr = '\0';
3690
3691 /*
3692 * Grab the status for this language; if there isn't a language-specific file
3693 * then fallback to the default one...
3694 */
3695
3696 if ((status = stat(filename, filestats)) != 0 && language[0] &&
3697 strncmp(con->uri, "/icons/", 7) &&
3698 strncmp(con->uri, "/ppd/", 5) &&
3699 strncmp(con->uri, "/rss/", 5) &&
3700 strncmp(con->uri, "/admin/conf/", 12) &&
3701 strncmp(con->uri, "/admin/log/", 11))
3702 {
3703 /*
3704 * Drop the country code...
3705 */
3706
3707 language[3] = '\0';
3708 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3709
3710 if ((ptr = strchr(filename, '?')) != NULL)
3711 *ptr = '\0';
3712
3713 if ((status = stat(filename, filestats)) != 0)
3714 {
3715 /*
3716 * Drop the language prefix and try the root directory...
3717 */
3718
3719 language[0] = '\0';
3720 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
3721
3722 if ((ptr = strchr(filename, '?')) != NULL)
3723 *ptr = '\0';
3724
3725 status = stat(filename, filestats);
3726 }
3727 }
3728
3729 /*
3730 * If we're found a directory, get the index.html file instead...
3731 */
3732
3733 if (!status && S_ISDIR(filestats->st_mode))
3734 {
3735 /*
3736 * Make sure the URI ends with a slash...
3737 */
3738
3739 if (con->uri[strlen(con->uri) - 1] != '/')
3740 strlcat(con->uri, "/", sizeof(con->uri));
3741
3742 /*
3743 * Find the directory index file, trying every language...
3744 */
3745
3746 do
3747 {
3748 if (status && language[0])
3749 {
3750 /*
3751 * Try a different language subset...
3752 */
3753
3754 if (language[3])
3755 language[0] = '\0'; /* Strip country code */
3756 else
3757 language[0] = '\0'; /* Strip language */
3758 }
3759
3760 /*
3761 * Look for the index file...
3762 */
3763
3764 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3765
3766 if ((ptr = strchr(filename, '?')) != NULL)
3767 *ptr = '\0';
3768
3769 ptr = filename + strlen(filename);
3770 plen = len - (ptr - filename);
3771
3772 strlcpy(ptr, "index.html", plen);
3773 status = stat(filename, filestats);
3774
3775 #ifdef HAVE_JAVA
3776 if (status)
3777 {
3778 strlcpy(ptr, "index.class", plen);
3779 status = stat(filename, filestats);
3780 }
3781 #endif /* HAVE_JAVA */
3782
3783 #ifdef HAVE_PERL
3784 if (status)
3785 {
3786 strlcpy(ptr, "index.pl", plen);
3787 status = stat(filename, filestats);
3788 }
3789 #endif /* HAVE_PERL */
3790
3791 #ifdef HAVE_PHP
3792 if (status)
3793 {
3794 strlcpy(ptr, "index.php", plen);
3795 status = stat(filename, filestats);
3796 }
3797 #endif /* HAVE_PHP */
3798
3799 #ifdef HAVE_PYTHON
3800 if (status)
3801 {
3802 strlcpy(ptr, "index.pyc", plen);
3803 status = stat(filename, filestats);
3804 }
3805
3806 if (status)
3807 {
3808 strlcpy(ptr, "index.py", plen);
3809 status = stat(filename, filestats);
3810 }
3811 #endif /* HAVE_PYTHON */
3812
3813 }
3814 while (status && language[0]);
3815 }
3816
3817 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3818 "get_file(con=%p(%d), filestats=%p, filename=%p, len=%d) = "
3819 "%s", con, con->http.fd, filestats, filename, len,
3820 status ? "(null)" : filename);
3821
3822 if (status)
3823 return (NULL);
3824 else
3825 return (filename);
3826 }
3827
3828
3829 /*
3830 * 'install_conf_file()' - Install a configuration file.
3831 */
3832
3833 static http_status_t /* O - Status */
3834 install_conf_file(cupsd_client_t *con) /* I - Connection */
3835 {
3836 char filename[1024]; /* Configuration filename */
3837 mode_t mode; /* Permissions */
3838 cups_file_t *in, /* Input file */
3839 *out; /* Output file */
3840 char buffer[16384]; /* Copy buffer */
3841 ssize_t bytes; /* Number of bytes */
3842
3843
3844 /*
3845 * Open the request file...
3846 */
3847
3848 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
3849 {
3850 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open request file \"%s\": %s",
3851 con->filename, strerror(errno));
3852 return (HTTP_SERVER_ERROR);
3853 }
3854
3855 /*
3856 * Open the new config file...
3857 */
3858
3859 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri + 11);
3860 if (!strcmp(con->uri, "/admin/conf/printers.conf"))
3861 mode = ConfigFilePerm & 0600;
3862 else
3863 mode = ConfigFilePerm;
3864
3865 if ((out = cupsdCreateConfFile(filename, mode)) == NULL)
3866 {
3867 cupsFileClose(in);
3868 return (HTTP_SERVER_ERROR);
3869 }
3870
3871 cupsdLogMessage(CUPSD_LOG_INFO, "Installing config file \"%s\"...", filename);
3872
3873 /*
3874 * Copy from the request to the new config file...
3875 */
3876
3877 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
3878 if (cupsFileWrite(out, buffer, bytes) < bytes)
3879 {
3880 cupsdLogMessage(CUPSD_LOG_ERROR,
3881 "Unable to copy to config file \"%s\": %s",
3882 filename, strerror(errno));
3883
3884 cupsFileClose(in);
3885 cupsFileClose(out);
3886
3887 snprintf(filename, sizeof(filename), "%s%s.N", ServerRoot, con->uri + 11);
3888 cupsdRemoveFile(filename);
3889
3890 return (HTTP_SERVER_ERROR);
3891 }
3892
3893 /*
3894 * Close the files...
3895 */
3896
3897 cupsFileClose(in);
3898
3899 if (cupsdCloseCreatedConfFile(out, filename))
3900 return (HTTP_SERVER_ERROR);
3901
3902 /*
3903 * Remove the request file...
3904 */
3905
3906 cupsdRemoveFile(con->filename);
3907 cupsdClearString(&con->filename);
3908
3909 /*
3910 * If the cupsd.conf file was updated, set the NeedReload flag...
3911 */
3912
3913 if (!strcmp(con->uri, "/admin/conf/cupsd.conf"))
3914 NeedReload = RELOAD_CUPSD;
3915 else
3916 NeedReload = RELOAD_ALL;
3917
3918 ReloadTime = time(NULL);
3919
3920 /*
3921 * Return that the file was created successfully...
3922 */
3923
3924 return (HTTP_CREATED);
3925 }
3926
3927
3928 /*
3929 * 'is_cgi()' - Is the resource a CGI script/program?
3930 */
3931
3932 static int /* O - 1 = CGI, 0 = file */
3933 is_cgi(cupsd_client_t *con, /* I - Client connection */
3934 const char *filename, /* I - Real filename */
3935 struct stat *filestats, /* I - File information */
3936 mime_type_t *type) /* I - MIME type */
3937 {
3938 const char *options; /* Options on URL */
3939
3940
3941 /*
3942 * Get the options, if any...
3943 */
3944
3945 if ((options = strchr(con->uri, '?')) != NULL)
3946 {
3947 options ++;
3948 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", options);
3949 }
3950
3951 /*
3952 * Check for known types...
3953 */
3954
3955 if (!type || _cups_strcasecmp(type->super, "application"))
3956 {
3957 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3958 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
3959 "type=%s/%s) = 0", con, con->http.fd, filename, filestats,
3960 type ? type->super : "unknown",
3961 type ? type->type : "unknown");
3962 return (0);
3963 }
3964
3965 if (!_cups_strcasecmp(type->type, "x-httpd-cgi") &&
3966 (filestats->st_mode & 0111))
3967 {
3968 /*
3969 * "application/x-httpd-cgi" is a CGI script.
3970 */
3971
3972 cupsdSetString(&con->command, filename);
3973
3974 if (options)
3975 cupsdSetStringf(&con->options, " %s", options);
3976
3977 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3978 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
3979 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
3980 type->super, type->type);
3981 return (1);
3982 }
3983 #ifdef HAVE_JAVA
3984 else if (!_cups_strcasecmp(type->type, "x-httpd-java"))
3985 {
3986 /*
3987 * "application/x-httpd-java" is a Java servlet.
3988 */
3989
3990 cupsdSetString(&con->command, CUPS_JAVA);
3991
3992 if (options)
3993 cupsdSetStringf(&con->options, " %s %s", filename, options);
3994 else
3995 cupsdSetStringf(&con->options, " %s", filename);
3996
3997 cupsdLogMessage(CUPSD_LOG_DEBUG2,
3998 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
3999 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4000 type->super, type->type);
4001 return (1);
4002 }
4003 #endif /* HAVE_JAVA */
4004 #ifdef HAVE_PERL
4005 else if (!_cups_strcasecmp(type->type, "x-httpd-perl"))
4006 {
4007 /*
4008 * "application/x-httpd-perl" is a Perl page.
4009 */
4010
4011 cupsdSetString(&con->command, CUPS_PERL);
4012
4013 if (options)
4014 cupsdSetStringf(&con->options, " %s %s", filename, options);
4015 else
4016 cupsdSetStringf(&con->options, " %s", filename);
4017
4018 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4019 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4020 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4021 type->super, type->type);
4022 return (1);
4023 }
4024 #endif /* HAVE_PERL */
4025 #ifdef HAVE_PHP
4026 else if (!_cups_strcasecmp(type->type, "x-httpd-php"))
4027 {
4028 /*
4029 * "application/x-httpd-php" is a PHP page.
4030 */
4031
4032 cupsdSetString(&con->command, CUPS_PHP);
4033
4034 if (options)
4035 cupsdSetStringf(&con->options, " %s %s", filename, options);
4036 else
4037 cupsdSetStringf(&con->options, " %s", filename);
4038
4039 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4040 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4041 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4042 type->super, type->type);
4043 return (1);
4044 }
4045 #endif /* HAVE_PHP */
4046 #ifdef HAVE_PYTHON
4047 else if (!_cups_strcasecmp(type->type, "x-httpd-python"))
4048 {
4049 /*
4050 * "application/x-httpd-python" is a Python page.
4051 */
4052
4053 cupsdSetString(&con->command, CUPS_PYTHON);
4054
4055 if (options)
4056 cupsdSetStringf(&con->options, " %s %s", filename, options);
4057 else
4058 cupsdSetStringf(&con->options, " %s", filename);
4059
4060 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4061 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4062 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
4063 type->super, type->type);
4064 return (1);
4065 }
4066 #endif /* HAVE_PYTHON */
4067
4068 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4069 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4070 "type=%s/%s) = 0", con, con->http.fd, filename, filestats,
4071 type->super, type->type);
4072 return (0);
4073 }
4074
4075
4076 /*
4077 * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. "..").
4078 */
4079
4080 static int /* O - 0 if relative, 1 if absolute */
4081 is_path_absolute(const char *path) /* I - Input path */
4082 {
4083 /*
4084 * Check for a leading slash...
4085 */
4086
4087 if (path[0] != '/')
4088 return (0);
4089
4090 /*
4091 * Check for "/.." in the path...
4092 */
4093
4094 while ((path = strstr(path, "/..")) != NULL)
4095 {
4096 if (!path[3] || path[3] == '/')
4097 return (0);
4098
4099 path ++;
4100 }
4101
4102 /*
4103 * If we haven't found any relative paths, return 1 indicating an
4104 * absolute path...
4105 */
4106
4107 return (1);
4108 }
4109
4110
4111 #ifdef HAVE_SSL
4112 /*
4113 * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
4114 */
4115
4116 static int /* O - 1 on success, 0 on failure */
4117 make_certificate(cupsd_client_t *con) /* I - Client connection */
4118 {
4119 #if defined(HAVE_LIBSSL) && defined(HAVE_WAITPID)
4120 int pid, /* Process ID of command */
4121 status; /* Status of command */
4122 char command[1024], /* Command */
4123 *argv[12], /* Command-line arguments */
4124 *envp[MAX_ENV + 1], /* Environment variables */
4125 infofile[1024], /* Type-in information for cert */
4126 seedfile[1024]; /* Random number seed file */
4127 int envc, /* Number of environment variables */
4128 bytes; /* Bytes written */
4129 cups_file_t *fp; /* Seed/info file */
4130 int infofd; /* Info file descriptor */
4131
4132
4133 /*
4134 * Run the "openssl" command to seed the random number generator and
4135 * generate a self-signed certificate that is good for 10 years:
4136 *
4137 * openssl rand -rand seedfile 1
4138 *
4139 * openssl req -new -x509 -keyout ServerKey \
4140 * -out ServerCertificate -days 3650 -nodes
4141 *
4142 * The seeding step is crucial in ensuring that the openssl command
4143 * does not block on systems without sufficient entropy...
4144 */
4145
4146 if (!cupsFileFind("openssl", getenv("PATH"), 1, command, sizeof(command)))
4147 {
4148 cupsdLogMessage(CUPSD_LOG_ERROR,
4149 "No SSL certificate and openssl command not found!");
4150 return (0);
4151 }
4152
4153 if (access("/dev/urandom", 0))
4154 {
4155 /*
4156 * If the system doesn't provide /dev/urandom, then any random source
4157 * will probably be blocking-style, so generate some random data to
4158 * use as a seed for the certificate. Note that we have already
4159 * seeded the random number generator in cupsdInitCerts()...
4160 */
4161
4162 cupsdLogMessage(CUPSD_LOG_INFO,
4163 "Seeding the random number generator...");
4164
4165 /*
4166 * Write the seed file...
4167 */
4168
4169 if ((fp = cupsTempFile2(seedfile, sizeof(seedfile))) == NULL)
4170 {
4171 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create seed file %s - %s",
4172 seedfile, strerror(errno));
4173 return (0);
4174 }
4175
4176 for (bytes = 0; bytes < 262144; bytes ++)
4177 cupsFilePutChar(fp, random());
4178
4179 cupsFileClose(fp);
4180
4181 /*
4182 * Run the openssl command to seed its random number generator...
4183 */
4184
4185 argv[0] = "openssl";
4186 argv[1] = "rand";
4187 argv[2] = "-rand";
4188 argv[3] = seedfile;
4189 argv[4] = "1";
4190 argv[5] = NULL;
4191
4192 envc = cupsdLoadEnv(envp, MAX_ENV);
4193 envp[envc] = NULL;
4194
4195 if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, NULL,
4196 NULL, &pid))
4197 {
4198 unlink(seedfile);
4199 return (0);
4200 }
4201
4202 while (waitpid(pid, &status, 0) < 0)
4203 if (errno != EINTR)
4204 {
4205 status = 1;
4206 break;
4207 }
4208
4209 cupsdFinishProcess(pid, command, sizeof(command), NULL);
4210
4211 /*
4212 * Remove the seed file, as it is no longer needed...
4213 */
4214
4215 unlink(seedfile);
4216
4217 if (status)
4218 {
4219 if (WIFEXITED(status))
4220 cupsdLogMessage(CUPSD_LOG_ERROR,
4221 "Unable to seed random number generator - "
4222 "the openssl command stopped with status %d!",
4223 WEXITSTATUS(status));
4224 else
4225 cupsdLogMessage(CUPSD_LOG_ERROR,
4226 "Unable to seed random number generator - "
4227 "the openssl command crashed on signal %d!",
4228 WTERMSIG(status));
4229
4230 return (0);
4231 }
4232 }
4233
4234 /*
4235 * Create a file with the certificate information fields...
4236 *
4237 * Note: This assumes that the default questions are asked by the openssl
4238 * command...
4239 */
4240
4241 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
4242 {
4243 cupsdLogMessage(CUPSD_LOG_ERROR,
4244 "Unable to create certificate information file %s - %s",
4245 infofile, strerror(errno));
4246 return (0);
4247 }
4248
4249 cupsFilePrintf(fp, ".\n.\n.\n%s\n.\n%s\n%s\n",
4250 ServerName, ServerName, ServerAdmin);
4251 cupsFileClose(fp);
4252
4253 cupsdLogMessage(CUPSD_LOG_INFO,
4254 "Generating SSL server key and certificate...");
4255
4256 argv[0] = "openssl";
4257 argv[1] = "req";
4258 argv[2] = "-new";
4259 argv[3] = "-x509";
4260 argv[4] = "-keyout";
4261 argv[5] = ServerKey;
4262 argv[6] = "-out";
4263 argv[7] = ServerCertificate;
4264 argv[8] = "-days";
4265 argv[9] = "3650";
4266 argv[10] = "-nodes";
4267 argv[11] = NULL;
4268
4269 cupsdLoadEnv(envp, MAX_ENV);
4270
4271 infofd = open(infofile, O_RDONLY);
4272
4273 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
4274 NULL, &pid))
4275 {
4276 close(infofd);
4277 unlink(infofile);
4278 return (0);
4279 }
4280
4281 close(infofd);
4282 unlink(infofile);
4283
4284 while (waitpid(pid, &status, 0) < 0)
4285 if (errno != EINTR)
4286 {
4287 status = 1;
4288 break;
4289 }
4290
4291 cupsdFinishProcess(pid, command, sizeof(command), NULL);
4292
4293 if (status)
4294 {
4295 if (WIFEXITED(status))
4296 cupsdLogMessage(CUPSD_LOG_ERROR,
4297 "Unable to create SSL server key and certificate - "
4298 "the openssl command stopped with status %d!",
4299 WEXITSTATUS(status));
4300 else
4301 cupsdLogMessage(CUPSD_LOG_ERROR,
4302 "Unable to create SSL server key and certificate - "
4303 "the openssl command crashed on signal %d!",
4304 WTERMSIG(status));
4305 }
4306 else
4307 {
4308 cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
4309 ServerKey);
4310 cupsdLogMessage(CUPSD_LOG_INFO,
4311 "Created SSL server certificate file \"%s\"...",
4312 ServerCertificate);
4313 }
4314
4315 return (!status);
4316
4317 #elif defined(HAVE_GNUTLS)
4318 gnutls_x509_crt crt; /* Self-signed certificate */
4319 gnutls_x509_privkey key; /* Encryption key */
4320 cups_lang_t *language; /* Default language info */
4321 cups_file_t *fp; /* Key/cert file */
4322 unsigned char buffer[8192]; /* Buffer for x509 data */
4323 size_t bytes; /* Number of bytes of data */
4324 unsigned char serial[4]; /* Serial number buffer */
4325 time_t curtime; /* Current time */
4326 int result; /* Result of GNU TLS calls */
4327
4328
4329 /*
4330 * Create the encryption key...
4331 */
4332
4333 cupsdLogMessage(CUPSD_LOG_INFO, "Generating SSL server key...");
4334
4335 gnutls_x509_privkey_init(&key);
4336 gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
4337
4338 /*
4339 * Save it...
4340 */
4341
4342 bytes = sizeof(buffer);
4343
4344 if ((result = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM,
4345 buffer, &bytes)) < 0)
4346 {
4347 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to export SSL server key - %s",
4348 gnutls_strerror(result));
4349 gnutls_x509_privkey_deinit(key);
4350 return (0);
4351 }
4352 else if ((fp = cupsFileOpen(ServerKey, "w")) != NULL)
4353 {
4354 cupsFileWrite(fp, (char *)buffer, bytes);
4355 cupsFileClose(fp);
4356
4357 cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
4358 ServerKey);
4359 }
4360 else
4361 {
4362 cupsdLogMessage(CUPSD_LOG_ERROR,
4363 "Unable to create SSL server key file \"%s\" - %s",
4364 ServerKey, strerror(errno));
4365 gnutls_x509_privkey_deinit(key);
4366 return (0);
4367 }
4368
4369 /*
4370 * Create the self-signed certificate...
4371 */
4372
4373 cupsdLogMessage(CUPSD_LOG_INFO, "Generating self-signed SSL certificate...");
4374
4375 language = cupsLangDefault();
4376 curtime = time(NULL);
4377 serial[0] = curtime >> 24;
4378 serial[1] = curtime >> 16;
4379 serial[2] = curtime >> 8;
4380 serial[3] = curtime;
4381
4382 gnutls_x509_crt_init(&crt);
4383 if (strlen(language->language) == 5)
4384 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
4385 language->language + 3, 2);
4386 else
4387 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0,
4388 "US", 2);
4389 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0,
4390 ServerName, strlen(ServerName));
4391 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
4392 ServerName, strlen(ServerName));
4393 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
4394 0, "Unknown", 7);
4395 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0,
4396 "Unknown", 7);
4397 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0,
4398 "Unknown", 7);
4399 gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0,
4400 ServerAdmin, strlen(ServerAdmin));
4401 gnutls_x509_crt_set_key(crt, key);
4402 gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
4403 gnutls_x509_crt_set_activation_time(crt, curtime);
4404 gnutls_x509_crt_set_expiration_time(crt, curtime + 10 * 365 * 86400);
4405 gnutls_x509_crt_set_ca_status(crt, 0);
4406 gnutls_x509_crt_set_subject_alternative_name(crt, GNUTLS_SAN_DNSNAME,
4407 ServerName);
4408 gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
4409 gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_KEY_ENCIPHERMENT);
4410 gnutls_x509_crt_set_version(crt, 3);
4411
4412 bytes = sizeof(buffer);
4413 if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0)
4414 gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes);
4415
4416 gnutls_x509_crt_sign(crt, crt, key);
4417
4418 /*
4419 * Save it...
4420 */
4421
4422 bytes = sizeof(buffer);
4423 if ((result = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM,
4424 buffer, &bytes)) < 0)
4425 cupsdLogMessage(CUPSD_LOG_ERROR,
4426 "Unable to export SSL server certificate - %s",
4427 gnutls_strerror(result));
4428 else if ((fp = cupsFileOpen(ServerCertificate, "w")) != NULL)
4429 {
4430 cupsFileWrite(fp, (char *)buffer, bytes);
4431 cupsFileClose(fp);
4432
4433 cupsdLogMessage(CUPSD_LOG_INFO,
4434 "Created SSL server certificate file \"%s\"...",
4435 ServerCertificate);
4436 }
4437 else
4438 cupsdLogMessage(CUPSD_LOG_ERROR,
4439 "Unable to create SSL server certificate file \"%s\" - %s",
4440 ServerCertificate, strerror(errno));
4441
4442 /*
4443 * Cleanup...
4444 */
4445
4446 gnutls_x509_crt_deinit(crt);
4447 gnutls_x509_privkey_deinit(key);
4448
4449 return (1);
4450
4451 #elif defined(HAVE_CDSASSL) && defined(HAVE_WAITPID)
4452 int pid, /* Process ID of command */
4453 status; /* Status of command */
4454 char command[1024], /* Command */
4455 *argv[4], /* Command-line arguments */
4456 *envp[MAX_ENV + 1], /* Environment variables */
4457 keychain[1024], /* Keychain argument */
4458 infofile[1024], /* Type-in information for cert */
4459 localname[1024], /* Local hostname */
4460 *servername; /* Name of server in cert */
4461 cups_file_t *fp; /* Seed/info file */
4462 int infofd; /* Info file descriptor */
4463
4464
4465 if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
4466 {
4467 snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
4468 servername = localname;
4469 }
4470 else
4471 servername = con->servername;
4472
4473 /*
4474 * Run the "certtool" command to generate a self-signed certificate...
4475 */
4476
4477 if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
4478 {
4479 cupsdLogMessage(CUPSD_LOG_ERROR,
4480 "No SSL certificate and certtool command not found!");
4481 return (0);
4482 }
4483
4484 /*
4485 * Create a file with the certificate information fields...
4486 *
4487 * Note: This assumes that the default questions are asked by the certtool
4488 * command...
4489 */
4490
4491 if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
4492 {
4493 cupsdLogMessage(CUPSD_LOG_ERROR,
4494 "Unable to create certificate information file %s - %s",
4495 infofile, strerror(errno));
4496 return (0);
4497 }
4498
4499 cupsFilePrintf(fp, "%s\nr\n\ny\nb\ns\ny\n%s\n\n\n\n\n%s\ny\n",
4500 servername, servername, ServerAdmin);
4501 cupsFileClose(fp);
4502
4503 cupsdLogMessage(CUPSD_LOG_INFO,
4504 "Generating SSL server key and certificate...");
4505
4506 snprintf(keychain, sizeof(keychain), "k=%s", ServerCertificate);
4507
4508 argv[0] = "certtool";
4509 argv[1] = "c";
4510 argv[2] = keychain;
4511 argv[3] = NULL;
4512
4513 cupsdLoadEnv(envp, MAX_ENV);
4514
4515 infofd = open(infofile, O_RDONLY);
4516
4517 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
4518 NULL, &pid))
4519 {
4520 close(infofd);
4521 unlink(infofile);
4522 return (0);
4523 }
4524
4525 close(infofd);
4526 unlink(infofile);
4527
4528 while (waitpid(pid, &status, 0) < 0)
4529 if (errno != EINTR)
4530 {
4531 status = 1;
4532 break;
4533 }
4534
4535 cupsdFinishProcess(pid, command, sizeof(command), NULL);
4536
4537 if (status)
4538 {
4539 if (WIFEXITED(status))
4540 cupsdLogMessage(CUPSD_LOG_ERROR,
4541 "Unable to create SSL server key and certificate - "
4542 "the certtool command stopped with status %d!",
4543 WEXITSTATUS(status));
4544 else
4545 cupsdLogMessage(CUPSD_LOG_ERROR,
4546 "Unable to create SSL server key and certificate - "
4547 "the certtool command crashed on signal %d!",
4548 WTERMSIG(status));
4549 }
4550 else
4551 {
4552 cupsdLogMessage(CUPSD_LOG_INFO,
4553 "Created SSL server certificate file \"%s\"...",
4554 ServerCertificate);
4555 }
4556
4557 return (!status);
4558
4559 #else
4560 return (0);
4561 #endif /* HAVE_LIBSSL && HAVE_WAITPID */
4562 }
4563 #endif /* HAVE_SSL */
4564
4565
4566 /*
4567 * 'pipe_command()' - Pipe the output of a command to the remote client.
4568 */
4569
4570 static int /* O - Process ID */
4571 pipe_command(cupsd_client_t *con, /* I - Client connection */
4572 int infile, /* I - Standard input for command */
4573 int *outfile, /* O - Standard output for command */
4574 char *command, /* I - Command to run */
4575 char *options, /* I - Options for command */
4576 int root) /* I - Run as root? */
4577 {
4578 int i; /* Looping var */
4579 int pid; /* Process ID */
4580 char *commptr, /* Command string pointer */
4581 commch; /* Command string character */
4582 char *uriptr; /* URI string pointer */
4583 int fds[2]; /* Pipe FDs */
4584 int argc; /* Number of arguments */
4585 int envc; /* Number of environment variables */
4586 char argbuf[10240], /* Argument buffer */
4587 *argv[100], /* Argument strings */
4588 *envp[MAX_ENV + 20]; /* Environment variables */
4589 char auth_type[256], /* AUTH_TYPE environment variable */
4590 content_length[1024], /* CONTENT_LENGTH environment variable */
4591 content_type[1024], /* CONTENT_TYPE environment variable */
4592 http_cookie[32768], /* HTTP_COOKIE environment variable */
4593 http_referer[1024], /* HTTP_REFERER environment variable */
4594 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
4595 lang[1024], /* LANG environment variable */
4596 path_info[1024], /* PATH_INFO environment variable */
4597 remote_addr[1024], /* REMOTE_ADDR environment variable */
4598 remote_host[1024], /* REMOTE_HOST environment variable */
4599 remote_user[1024], /* REMOTE_USER environment variable */
4600 script_filename[1024], /* SCRIPT_FILENAME environment variable */
4601 script_name[1024], /* SCRIPT_NAME environment variable */
4602 server_name[1024], /* SERVER_NAME environment variable */
4603 server_port[1024]; /* SERVER_PORT environment variable */
4604 ipp_attribute_t *attr; /* attributes-natural-language attribute */
4605 void *ccache = NULL; /* Kerberos credentials */
4606
4607
4608 /*
4609 * Parse a copy of the options string, which is of the form:
4610 *
4611 * argument+argument+argument
4612 * ?argument+argument+argument
4613 * param=value&param=value
4614 * ?param=value&param=value
4615 * /name?argument+argument+argument
4616 * /name?param=value&param=value
4617 *
4618 * If the string contains an "=" character after the initial name,
4619 * then we treat it as a HTTP GET form request and make a copy of
4620 * the remaining string for the environment variable.
4621 *
4622 * The string is always parsed out as command-line arguments, to
4623 * be consistent with Apache...
4624 */
4625
4626 cupsdLogMessage(CUPSD_LOG_DEBUG2,
4627 "pipe_command(con=%p(%d), infile=%d, outfile=%p, "
4628 "command=\"%s\", options=\"%s\", root=%d)",
4629 con, con->http.fd, infile, outfile, command,
4630 options ? options : "(null)", root);
4631
4632 argv[0] = command;
4633
4634 if (options)
4635 {
4636 commptr = options;
4637 if (*commptr == ' ')
4638 commptr ++;
4639 strlcpy(argbuf, commptr, sizeof(argbuf));
4640 }
4641 else
4642 argbuf[0] = '\0';
4643
4644 if (argbuf[0] == '/')
4645 {
4646 /*
4647 * Found some trailing path information, set PATH_INFO...
4648 */
4649
4650 if ((commptr = strchr(argbuf, '?')) == NULL)
4651 commptr = argbuf + strlen(argbuf);
4652
4653 commch = *commptr;
4654 *commptr = '\0';
4655 snprintf(path_info, sizeof(path_info), "PATH_INFO=%s", argbuf);
4656 *commptr = commch;
4657 }
4658 else
4659 {
4660 commptr = argbuf;
4661 path_info[0] = '\0';
4662
4663 if (*commptr == ' ')
4664 commptr ++;
4665 }
4666
4667 if (*commptr == '?' && con->operation == HTTP_GET && !con->query_string)
4668 {
4669 commptr ++;
4670 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", commptr);
4671 }
4672
4673 argc = 1;
4674
4675 if (*commptr)
4676 {
4677 argv[argc ++] = commptr;
4678
4679 for (; *commptr && argc < 99; commptr ++)
4680 {
4681 /*
4682 * Break arguments whenever we see a + or space...
4683 */
4684
4685 if (*commptr == ' ' || *commptr == '+')
4686 {
4687 while (*commptr == ' ' || *commptr == '+')
4688 *commptr++ = '\0';
4689
4690 /*
4691 * If we don't have a blank string, save it as another argument...
4692 */
4693
4694 if (*commptr)
4695 {
4696 argv[argc] = commptr;
4697 argc ++;
4698 }
4699 else
4700 break;
4701 }
4702 else if (*commptr == '%' && isxdigit(commptr[1] & 255) &&
4703 isxdigit(commptr[2] & 255))
4704 {
4705 /*
4706 * Convert the %xx notation to the individual character.
4707 */
4708
4709 if (commptr[1] >= '0' && commptr[1] <= '9')
4710 *commptr = (commptr[1] - '0') << 4;
4711 else
4712 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
4713
4714 if (commptr[2] >= '0' && commptr[2] <= '9')
4715 *commptr |= commptr[2] - '0';
4716 else
4717 *commptr |= tolower(commptr[2]) - 'a' + 10;
4718
4719 _cups_strcpy(commptr + 1, commptr + 3);
4720
4721 /*
4722 * Check for a %00 and break if that is the case...
4723 */
4724
4725 if (!*commptr)
4726 break;
4727 }
4728 }
4729 }
4730
4731 argv[argc] = NULL;
4732
4733 /*
4734 * Setup the environment variables as needed...
4735 */
4736
4737 if (con->username[0])
4738 {
4739 snprintf(auth_type, sizeof(auth_type), "AUTH_TYPE=%s",
4740 httpGetField(HTTP(con), HTTP_FIELD_AUTHORIZATION));
4741
4742 if ((uriptr = strchr(auth_type + 10, ' ')) != NULL)
4743 *uriptr = '\0';
4744 }
4745 else
4746 auth_type[0] = '\0';
4747
4748 if (con->request &&
4749 (attr = ippFindAttribute(con->request, "attributes-natural-language",
4750 IPP_TAG_LANGUAGE)) != NULL)
4751 {
4752 switch (strlen(attr->values[0].string.text))
4753 {
4754 default :
4755 /*
4756 * This is an unknown or badly formatted language code; use
4757 * the POSIX locale...
4758 */
4759
4760 strcpy(lang, "LANG=C");
4761 break;
4762
4763 case 2 :
4764 /*
4765 * Just the language code (ll)...
4766 */
4767
4768 snprintf(lang, sizeof(lang), "LANG=%s.UTF8",
4769 attr->values[0].string.text);
4770 break;
4771
4772 case 5 :
4773 /*
4774 * Language and country code (ll-cc)...
4775 */
4776
4777 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF8",
4778 attr->values[0].string.text[0],
4779 attr->values[0].string.text[1],
4780 toupper(attr->values[0].string.text[3] & 255),
4781 toupper(attr->values[0].string.text[4] & 255));
4782 break;
4783 }
4784 }
4785 else if (con->language)
4786 snprintf(lang, sizeof(lang), "LANG=%s.UTF8", con->language->language);
4787 else
4788 strcpy(lang, "LANG=C");
4789
4790 strcpy(remote_addr, "REMOTE_ADDR=");
4791 httpAddrString(con->http.hostaddr, remote_addr + 12,
4792 sizeof(remote_addr) - 12);
4793
4794 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s",
4795 con->http.hostname);
4796
4797 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
4798 if ((uriptr = strchr(script_name, '?')) != NULL)
4799 *uriptr = '\0';
4800
4801 snprintf(script_filename, sizeof(script_filename), "SCRIPT_FILENAME=%s%s",
4802 DocumentRoot, script_name + 12);
4803
4804 sprintf(server_port, "SERVER_PORT=%d", con->serverport);
4805
4806 if (con->http.fields[HTTP_FIELD_HOST][0])
4807 {
4808 char *nameptr; /* Pointer to ":port" */
4809
4810 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
4811 con->http.fields[HTTP_FIELD_HOST]);
4812 if ((nameptr = strrchr(server_name, ':')) != NULL && !strchr(nameptr, ']'))
4813 *nameptr = '\0'; /* Strip trailing ":port" */
4814 }
4815 else
4816 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
4817 con->servername);
4818
4819 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
4820
4821 if (auth_type[0])
4822 envp[envc ++] = auth_type;
4823
4824 envp[envc ++] = lang;
4825 envp[envc ++] = "REDIRECT_STATUS=1";
4826 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
4827 envp[envc ++] = server_name;
4828 envp[envc ++] = server_port;
4829 envp[envc ++] = remote_addr;
4830 envp[envc ++] = remote_host;
4831 envp[envc ++] = script_name;
4832 envp[envc ++] = script_filename;
4833
4834 if (path_info[0])
4835 envp[envc ++] = path_info;
4836
4837 if (con->username[0])
4838 {
4839 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
4840
4841 envp[envc ++] = remote_user;
4842 }
4843
4844 if (con->http.version == HTTP_1_1)
4845 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
4846 else if (con->http.version == HTTP_1_0)
4847 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
4848 else
4849 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
4850
4851 if (con->http.cookie)
4852 {
4853 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
4854 con->http.cookie);
4855 envp[envc ++] = http_cookie;
4856 }
4857
4858 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
4859 {
4860 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
4861 con->http.fields[HTTP_FIELD_USER_AGENT]);
4862 envp[envc ++] = http_user_agent;
4863 }
4864
4865 if (con->http.fields[HTTP_FIELD_REFERER][0])
4866 {
4867 snprintf(http_referer, sizeof(http_referer), "HTTP_REFERER=%s",
4868 con->http.fields[HTTP_FIELD_REFERER]);
4869 envp[envc ++] = http_referer;
4870 }
4871
4872 if (con->operation == HTTP_GET)
4873 {
4874 envp[envc ++] = "REQUEST_METHOD=GET";
4875
4876 if (con->query_string)
4877 {
4878 /*
4879 * Add GET form variables after ?...
4880 */
4881
4882 envp[envc ++] = con->query_string;
4883 }
4884 else
4885 envp[envc ++] = "QUERY_STRING=";
4886 }
4887 else
4888 {
4889 sprintf(content_length, "CONTENT_LENGTH=" CUPS_LLFMT,
4890 CUPS_LLCAST con->bytes);
4891 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
4892 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
4893
4894 envp[envc ++] = "REQUEST_METHOD=POST";
4895 envp[envc ++] = content_length;
4896 envp[envc ++] = content_type;
4897 }
4898
4899 /*
4900 * Tell the CGI if we are using encryption...
4901 */
4902
4903 if (con->http.tls)
4904 envp[envc ++] = "HTTPS=ON";
4905
4906 /*
4907 * Terminate the environment array...
4908 */
4909
4910 envp[envc] = NULL;
4911
4912 if (LogLevel >= CUPSD_LOG_DEBUG)
4913 {
4914 for (i = 0; i < argc; i ++)
4915 cupsdLogMessage(CUPSD_LOG_DEBUG,
4916 "[CGI] argv[%d] = \"%s\"", i, argv[i]);
4917 for (i = 0; i < envc; i ++)
4918 cupsdLogMessage(CUPSD_LOG_DEBUG,
4919 "[CGI] envp[%d] = \"%s\"", i, envp[i]);
4920 }
4921
4922 /*
4923 * Create a pipe for the output...
4924 */
4925
4926 if (cupsdOpenPipe(fds))
4927 {
4928 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to create pipe for %s - %s",
4929 argv[0], strerror(errno));
4930 return (0);
4931 }
4932
4933 /*
4934 * Then execute the command...
4935 */
4936
4937 if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1],
4938 -1, -1, root, DefaultProfile, NULL, &pid) < 0)
4939 {
4940 /*
4941 * Error - can't fork!
4942 */
4943
4944 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to start %s - %s", argv[0],
4945 strerror(errno));
4946
4947 cupsdClosePipe(fds);
4948 pid = 0;
4949 }
4950 else
4951 {
4952 /*
4953 * Fork successful - return the PID...
4954 */
4955
4956 if (con->username[0])
4957 cupsdAddCert(pid, con->username, ccache);
4958
4959 cupsdLogMessage(CUPSD_LOG_DEBUG, "[CGI] Started %s (PID %d)", command, pid);
4960
4961 *outfile = fds[0];
4962 close(fds[1]);
4963 }
4964
4965 return (pid);
4966 }
4967
4968
4969 /*
4970 * 'valid_host()' - Is the Host: field valid?
4971 */
4972
4973 static int /* O - 1 if valid, 0 if not */
4974 valid_host(cupsd_client_t *con) /* I - Client connection */
4975 {
4976 cupsd_alias_t *a; /* Current alias */
4977 cupsd_netif_t *netif; /* Current network interface */
4978 const char *host, /* Host field */
4979 *end; /* End character */
4980
4981
4982 host = con->http.fields[HTTP_FIELD_HOST];
4983
4984 if (httpAddrLocalhost(con->http.hostaddr))
4985 {
4986 /*
4987 * Only allow "localhost" or the equivalent IPv4 or IPv6 numerical
4988 * addresses when accessing CUPS via the loopback interface...
4989 */
4990
4991 return (!_cups_strcasecmp(host, "localhost") ||
4992 !_cups_strncasecmp(host, "localhost:", 10) ||
4993 !_cups_strcasecmp(host, "localhost.") ||
4994 !_cups_strncasecmp(host, "localhost.:", 11) ||
4995 #ifdef __linux
4996 !_cups_strcasecmp(host, "localhost.localdomain") ||
4997 !_cups_strncasecmp(host, "localhost.localdomain:", 22) ||
4998 #endif /* __linux */
4999 !strcmp(host, "127.0.0.1") ||
5000 !strncmp(host, "127.0.0.1:", 10) ||
5001 !strcmp(host, "[::1]") ||
5002 !strncmp(host, "[::1]:", 6));
5003 }
5004
5005 #ifdef HAVE_DNSSD
5006 /*
5007 * Check if the hostname is something.local (Bonjour); if so, allow it.
5008 */
5009
5010 if ((end = strrchr(host, '.')) != NULL &&
5011 (!_cups_strcasecmp(end, ".local") || !_cups_strncasecmp(end, ".local:", 7) ||
5012 !_cups_strcasecmp(end, ".local.") || !_cups_strncasecmp(end, ".local.:", 8)))
5013 return (1);
5014 #endif /* HAVE_DNSSD */
5015
5016 /*
5017 * Check if the hostname is an IP address...
5018 */
5019
5020 if (isdigit(*host & 255) || *host == '[')
5021 {
5022 /*
5023 * Possible IPv4/IPv6 address...
5024 */
5025
5026 char temp[1024], /* Temporary string */
5027 *ptr; /* Pointer into temporary string */
5028 http_addrlist_t *addrlist; /* List of addresses */
5029
5030
5031 strlcpy(temp, host, sizeof(temp));
5032 if ((ptr = strrchr(temp, ':')) != NULL && !strchr(ptr, ']'))
5033 *ptr = '\0'; /* Strip :port from host value */
5034
5035 if ((addrlist = httpAddrGetList(temp, AF_UNSPEC, NULL)) != NULL)
5036 {
5037 /*
5038 * Good IPv4/IPv6 address...
5039 */
5040
5041 httpAddrFreeList(addrlist);
5042 return (1);
5043 }
5044 }
5045
5046 /*
5047 * Check for (alias) name matches...
5048 */
5049
5050 for (a = (cupsd_alias_t *)cupsArrayFirst(ServerAlias);
5051 a;
5052 a = (cupsd_alias_t *)cupsArrayNext(ServerAlias))
5053 {
5054 /*
5055 * "ServerAlias *" allows all host values through...
5056 */
5057
5058 if (!strcmp(a->name, "*"))
5059 return (1);
5060
5061 if (!_cups_strncasecmp(host, a->name, a->namelen))
5062 {
5063 /*
5064 * Prefix matches; check the character at the end - it must be ":", ".",
5065 * ".:", or nul...
5066 */
5067
5068 end = host + a->namelen;
5069
5070 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5071 return (1);
5072 }
5073 }
5074
5075 #ifdef HAVE_DNSSD
5076 for (a = (cupsd_alias_t *)cupsArrayFirst(DNSSDAlias);
5077 a;
5078 a = (cupsd_alias_t *)cupsArrayNext(DNSSDAlias))
5079 {
5080 /*
5081 * "ServerAlias *" allows all host values through...
5082 */
5083
5084 if (!strcmp(a->name, "*"))
5085 return (1);
5086
5087 if (!_cups_strncasecmp(host, a->name, a->namelen))
5088 {
5089 /*
5090 * Prefix matches; check the character at the end - it must be ":", ".",
5091 * ".:", or nul...
5092 */
5093
5094 end = host + a->namelen;
5095
5096 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5097 return (1);
5098 }
5099 }
5100 #endif /* HAVE_DNSSD */
5101
5102 /*
5103 * Check for interface hostname matches...
5104 */
5105
5106 for (netif = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
5107 netif;
5108 netif = (cupsd_netif_t *)cupsArrayNext(NetIFList))
5109 {
5110 if (!_cups_strncasecmp(host, netif->hostname, netif->hostlen))
5111 {
5112 /*
5113 * Prefix matches; check the character at the end - it must be ":", ".",
5114 * ".:", or nul...
5115 */
5116
5117 end = host + netif->hostlen;
5118
5119 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
5120 return (1);
5121 }
5122 }
5123
5124 return (0);
5125 }
5126
5127
5128 /*
5129 * 'write_file()' - Send a file via HTTP.
5130 */
5131
5132 static int /* O - 0 on failure, 1 on success */
5133 write_file(cupsd_client_t *con, /* I - Client connection */
5134 http_status_t code, /* I - HTTP status */
5135 char *filename, /* I - Filename */
5136 char *type, /* I - File type */
5137 struct stat *filestats) /* O - File information */
5138 {
5139 con->file = open(filename, O_RDONLY);
5140
5141 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5142 "write_file(con=%p(%d), code=%d, filename=\"%s\" (%d), "
5143 "type=\"%s\", filestats=%p)", con, con->http.fd,
5144 code, filename, con->file, type ? type : "(null)", filestats);
5145
5146 if (con->file < 0)
5147 return (0);
5148
5149 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
5150
5151 con->pipe_pid = 0;
5152
5153 if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
5154 return (0);
5155
5156 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
5157 httpGetDateString(filestats->st_mtime)) < 0)
5158 return (0);
5159 if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n",
5160 CUPS_LLCAST filestats->st_size) < 0)
5161 return (0);
5162 if (httpPrintf(HTTP(con), "\r\n") < 0)
5163 return (0);
5164
5165 if (cupsdFlushHeader(con) < 0)
5166 return (0);
5167
5168 con->http.data_encoding = HTTP_ENCODE_LENGTH;
5169 con->http.data_remaining = filestats->st_size;
5170
5171 if (con->http.data_remaining <= INT_MAX)
5172 con->http._data_remaining = con->http.data_remaining;
5173 else
5174 con->http._data_remaining = INT_MAX;
5175
5176 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
5177 (cupsd_selfunc_t)cupsdWriteClient, con);
5178
5179 return (1);
5180 }
5181
5182
5183 /*
5184 * 'write_pipe()' - Flag that data is available on the CGI pipe.
5185 */
5186
5187 static void
5188 write_pipe(cupsd_client_t *con) /* I - Client connection */
5189 {
5190 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5191 "write_pipe(con=%p(%d)) CGI output on fd %d",
5192 con, con->http.fd, con->file);
5193
5194 con->file_ready = 1;
5195
5196 cupsdRemoveSelect(con->file);
5197 cupsdAddSelect(con->http.fd, NULL, (cupsd_selfunc_t)cupsdWriteClient, con);
5198 }
5199
5200
5201 /*
5202 * End of "$Id: client.c 7950 2008-09-17 00:21:59Z mike $".
5203 */