]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/client.c
Merge changes from CUPS 1.6svn-r9939.
[thirdparty/cups.git] / scheduler / client.c
CommitLineData
ef416fc2 1/*
b19ccc9e 2 * "$Id: client.c 7950 2008-09-17 00:21:59Z mike $"
ef416fc2 3 *
10d09e33 4 * Client routines for the CUPS scheduler.
ef416fc2 5 *
0268488e 6 * Copyright 2007-2011 by Apple Inc.
f7deaa1a 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.
ef416fc2 11 *
12 * These coded instructions, statements, and computer programs are the
bc44d920 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/".
ef416fc2 17 *
18 * Contents:
19 *
94da7e34
MS
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.
e07d4801 44 * valid_host() - Is the Host: field valid?
94da7e34
MS
45 * write_file() - Send a file via HTTP.
46 * write_pipe() - Flag that data is available on the CGI pipe.
ef416fc2 47 */
48
49/*
50 * Include necessary headers...
51 */
52
ef416fc2 53#include "cupsd.h"
54
749b1e90
MS
55#ifdef HAVE_TCPD_H
56# include <tcpd.h>
57#endif /* HAVE_TCPD_H */
58
ef416fc2 59
60/*
61 * Local functions...
62 */
63
64static int check_if_modified(cupsd_client_t *con,
65 struct stat *filestats);
3dfe78b3
MS
66static int compare_clients(cupsd_client_t *a, cupsd_client_t *b,
67 void *data);
ee571f26 68static int data_ready(cupsd_client_t *con);
80ca4592 69#ifdef HAVE_SSL
e1d6a774 70static int encrypt_client(cupsd_client_t *con);
80ca4592 71#endif /* HAVE_SSL */
ef416fc2 72#ifdef HAVE_CDSASSL
b86bc4cf 73static CFArrayRef get_cdsa_certificate(cupsd_client_t *con);
ef416fc2 74#endif /* HAVE_CDSASSL */
f7deaa1a 75static char *get_file(cupsd_client_t *con, struct stat *filestats,
ef416fc2 76 char *filename, int len);
77static http_status_t install_conf_file(cupsd_client_t *con);
e1d6a774 78static int is_cgi(cupsd_client_t *con, const char *filename,
79 struct stat *filestats, mime_type_t *type);
ef416fc2 80static int is_path_absolute(const char *path);
8ca02f3c 81#ifdef HAVE_SSL
b86bc4cf 82static int make_certificate(cupsd_client_t *con);
8ca02f3c 83#endif /* HAVE_SSL */
ef416fc2 84static int pipe_command(cupsd_client_t *con, int infile, int *outfile,
85 char *command, char *options, int root);
e07d4801 86static int valid_host(cupsd_client_t *con);
a74454a7 87static int write_file(cupsd_client_t *con, http_status_t code,
88 char *filename, char *type,
89 struct stat *filestats);
f899b121 90static void write_pipe(cupsd_client_t *con);
ef416fc2 91
92
93/*
94 * 'cupsdAcceptClient()' - Accept a new client.
95 */
96
97void
98cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
99{
ef416fc2 100 int count; /* Count of connections on a host */
101 int val; /* Parameter value */
bd7854cb 102 cupsd_client_t *con, /* New client pointer */
103 *tempcon; /* Temporary client pointer */
ef416fc2 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 */
749b1e90
MS
110#ifdef HAVE_TCPD_H
111 struct request_info wrap_req; /* TCP wrappers request information */
112#endif /* HAVE_TCPD_H */
ef416fc2 113
114
115 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1 116 "cupsdAcceptClient(lis=%p(%d)) Clients=%d",
bd7854cb 117 lis, lis->fd, cupsArrayCount(Clients));
ef416fc2 118
119 /*
120 * Make sure we don't have a full set of clients already...
121 */
122
bd7854cb 123 if (cupsArrayCount(Clients) == MaxClients)
ef416fc2 124 return;
125
126 /*
127 * Get a pointer to the next available client...
128 */
129
bd7854cb 130 if (!Clients)
131 Clients = cupsArrayNew(NULL, NULL);
132
133 if (!Clients)
91c84a35
MS
134 {
135 cupsdLogMessage(CUPSD_LOG_ERROR,
3dfe78b3
MS
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!");
91c84a35 148 cupsdPauseListening();
bd7854cb 149 return;
91c84a35 150 }
bd7854cb 151
91c84a35
MS
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 }
ef416fc2 158
ef416fc2 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 {
76cd9e37
MS
172 if (errno == ENFILE || errno == EMFILE)
173 cupsdPauseListening();
174
ef416fc2 175 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to accept client connection - %s.",
176 strerror(errno));
bd7854cb 177 free(con);
76cd9e37 178
ef416fc2 179 return;
180 }
181
22c9029b
MS
182 /*
183 * Save the connected port number...
184 */
ef416fc2 185
22c9029b 186 _httpAddrSetPort(con->http.hostaddr, _httpAddrPort(&(lis->address)));
ef416fc2 187
22c9029b
MS
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 */
ef416fc2 193
22c9029b
MS
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;
ef416fc2 199#endif /* AF_INET6 */
ef416fc2 200
201 /*
202 * Check the number of clients on the same address...
203 */
204
bd7854cb 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))
ef416fc2 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,
bd7854cb 221 "Possible DoS attack - more than %d clients connecting "
222 "from %s!",
749b1e90
MS
223 MaxClientsPerHost,
224 httpAddrString(con->http.hostaddr, con->http.hostname,
225 sizeof(con->http.hostname)));
ef416fc2 226 }
227
228#ifdef WIN32
229 closesocket(con->http.fd);
230#else
231 close(con->http.fd);
232#endif /* WIN32 */
233
bd7854cb 234 free(con);
ef416fc2 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
d1c13e16 257 if (HostNameLookups)
ef416fc2 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
ef416fc2 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);
bd7854cb 283
284 free(con);
ef416fc2 285 return;
286 }
287
288 if (HostNameLookups == 2)
289 {
290 /*
291 * Do double lookups as needed...
292 */
293
749b1e90
MS
294 if ((addrlist = httpAddrGetList(con->http.hostname, AF_UNSPEC, NULL))
295 != NULL)
ef416fc2 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
ef416fc2 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);
bd7854cb 326 free(con);
ef416fc2 327 return;
328 }
329 }
330
749b1e90
MS
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 {
749b1e90
MS
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
ef416fc2 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 */
22c9029b 361 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv%d)",
ef416fc2 362 con->http.fd, con->http.hostname,
22c9029b
MS
363 _httpAddrPort(con->http.hostaddr),
364 _httpAddrFamily(con->http.hostaddr) == AF_INET ? 4 : 6);
ef416fc2 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 }
22c9029b
MS
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 */
ef416fc2 386 else
387 {
22c9029b
MS
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));
ef416fc2 392 else
22c9029b 393 httpAddrString(&temp, con->servername, sizeof(con->servername));
b19ccc9e 394
22c9029b 395 con->serverport = _httpAddrPort(&(lis->address));
ef416fc2 396 }
397
bd7854cb 398 cupsArrayAdd(Clients, con);
399
ef416fc2 400 /*
b9faaae1
MS
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...
ef416fc2 404 */
405
406 val = 1;
f7deaa1a 407 setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
ef416fc2 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 /*
b9faaae1 416 * Add the socket to the server select.
ef416fc2 417 */
418
f7deaa1a 419 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
ef416fc2 420
ef416fc2 421 /*
422 * Temporarily suspend accept()'s until we lose a client...
423 */
424
bd7854cb 425 if (cupsArrayCount(Clients) == MaxClients)
ef416fc2 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
411affcf 441 if (!encrypt_client(con))
442 cupsdCloseClient(con);
ef416fc2 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
454void
455cupsdCloseAllClients(void)
456{
bd7854cb 457 cupsd_client_t *con; /* Current client */
458
459
b9faaae1
MS
460 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d",
461 cupsArrayCount(Clients));
462
bd7854cb 463 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
464 con;
465 con = (cupsd_client_t *)cupsArrayNext(Clients))
49d87452
MS
466 if (cupsdCloseClient(con))
467 cupsdCloseClient(con);
ef416fc2 468}
469
470
471/*
472 * 'cupsdCloseClient()' - Close a remote client.
473 */
474
475int /* O - 1 if partial close, 0 if fully closed */
476cupsdCloseClient(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 */
ef416fc2 481 unsigned long error; /* Error code */
482#elif defined(HAVE_GNUTLS)
89d46774 483 int error; /* Error code */
ef416fc2 484 gnutls_certificate_server_credentials *credentials;
485 /* TLS credentials */
89d46774 486# elif defined(HAVE_CDSASSL)
ef416fc2 487#endif /* HAVE_LIBSSL */
488
489
f11a948a 490 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCloseClient: %d", con->http.fd);
ef416fc2 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
7cf5915e 510 context = SSL_get_SSL_CTX(con->http.tls);
ef416fc2 511
7cf5915e 512 switch (SSL_shutdown(con->http.tls))
ef416fc2 513 {
514 case 1 :
c934a06c
MS
515 cupsdLogMessage(CUPSD_LOG_DEBUG,
516 "SSL shutdown successful!");
ef416fc2 517 break;
518 case -1 :
519 cupsdLogMessage(CUPSD_LOG_ERROR,
c934a06c 520 "Fatal error during SSL shutdown!");
ef416fc2 521 default :
522 while ((error = ERR_get_error()) != 0)
c934a06c 523 cupsdLogMessage(CUPSD_LOG_ERROR, "SSL shutdown failed: %s",
ef416fc2 524 ERR_error_string(error, NULL));
525 break;
526 }
527
528 SSL_CTX_free(context);
7cf5915e 529 SSL_free(con->http.tls);
ef416fc2 530
531# elif defined(HAVE_GNUTLS)
7cf5915e 532 credentials = (gnutls_certificate_server_credentials *)(con->http.tls_credentials);
ef416fc2 533
7cf5915e 534 error = gnutls_bye(con->http.tls, GNUTLS_SHUT_WR);
ef416fc2 535 switch (error)
536 {
537 case GNUTLS_E_SUCCESS:
c934a06c
MS
538 cupsdLogMessage(CUPSD_LOG_DEBUG,
539 "SSL shutdown successful!");
ef416fc2 540 break;
541 default:
542 cupsdLogMessage(CUPSD_LOG_ERROR,
c934a06c 543 "SSL shutdown failed: %s", gnutls_strerror(error));
ef416fc2 544 break;
545 }
546
7cf5915e 547 gnutls_deinit(con->http.tls);
ef416fc2 548 gnutls_certificate_free_credentials(*credentials);
549 free(credentials);
ef416fc2 550
551# elif defined(HAVE_CDSASSL)
7cf5915e 552 while (SSLClose(con->http.tls) == errSSLWouldBlock)
b423cd4c 553 usleep(1000);
554
7cf5915e 555 SSLDisposeContext(con->http.tls);
89d46774 556
7cf5915e
MS
557 if (con->http.tls_credentials)
558 CFRelease(con->http.tls_credentials);
89d46774 559
ef416fc2 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
ef416fc2 572 cupsdEndProcess(con->pipe_pid, 1);
573 con->pipe_pid = 0;
574 }
575
576 if (con->file >= 0)
577 {
f7deaa1a 578 cupsdRemoveSelect(con->file);
ef416fc2 579
ef416fc2 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
3dfe78b3 588 if (con->http.fd >= 0)
ef416fc2 589 {
3dfe78b3
MS
590 cupsArrayRemove(ActiveClients, con);
591 cupsdSetBusyState();
592
ef416fc2 593 if (partial)
594 {
595 /*
596 * Only do a partial close so that the encrypted client gets everything.
597 */
598
ef416fc2 599 shutdown(con->http.fd, 0);
f7deaa1a 600 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
ef416fc2 601 }
602 else
603 {
604 /*
605 * Shut the socket down fully...
606 */
607
f7deaa1a 608 cupsdRemoveSelect(con->http.fd);
ef416fc2 609 close(con->http.fd);
ef416fc2 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));
9f5eb9be 624 httpClearFields(HTTP(con));
ef416fc2 625
626 cupsdClearString(&con->filename);
627 cupsdClearString(&con->command);
628 cupsdClearString(&con->options);
b86bc4cf 629 cupsdClearString(&con->query_string);
ef416fc2 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
f7deaa1a 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
ef416fc2 657 /*
658 * Re-enable new client connections if we are going back under the
659 * limit...
660 */
661
bd7854cb 662 if (cupsArrayCount(Clients) == MaxClients)
ef416fc2 663 cupsdResumeListening();
664
665 /*
666 * Compact the list of clients as necessary...
667 */
668
bd7854cb 669 cupsArrayRemove(Clients, con);
ef416fc2 670
bd7854cb 671 free(con);
ef416fc2 672 }
673
674 return (partial);
675}
676
677
d09495fa 678/*
679 * 'cupsdFlushHeader()' - Flush the header fields to the client.
680 */
681
07725fee 682int /* I - Bytes written or -1 on error */
d09495fa 683cupsdFlushHeader(cupsd_client_t *con) /* I - Client to flush to */
684{
07725fee 685 int bytes = httpFlushWrite(HTTP(con));
d09495fa 686
687 con->http.data_encoding = HTTP_ENCODE_LENGTH;
07725fee 688
689 return (bytes);
d09495fa 690}
691
692
ef416fc2 693/*
694 * 'cupsdReadClient()' - Read data from a client.
695 */
696
f7deaa1a 697void
ef416fc2 698cupsdReadClient(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,
b9faaae1
MS
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);
ef416fc2 732
ef416fc2 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,
b9faaae1
MS
750 "cupsdReadClient: Saw first byte %02X, auto-negotiating "
751 "SSL/TLS session...", buf[0] & 255);
ef416fc2 752
411affcf 753 if (!encrypt_client(con))
f7deaa1a 754 cupsdCloseClient(con);
411affcf 755
f7deaa1a 756 return;
ef416fc2 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 {
f11a948a
MS
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
f7deaa1a 780 cupsdCloseClient(con);
781 return;
ef416fc2 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);
b86bc4cf 814 cupsdClearString(&con->query_string);
ef416fc2 815
2abf387c 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)
ef416fc2 829 {
830 cupsLangFree(con->language);
831 con->language = NULL;
832 }
833
09a101d6 834#ifdef HAVE_GSSAPI
97c9a8d7 835 con->have_gss = 0;
07ed0e9a 836 con->gss_uid = 0;
09a101d6 837#endif /* HAVE_GSSAPI */
838
ef416fc2 839 /*
840 * Grab the request line...
841 */
842
843 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
844 {
845 case 1 :
c934a06c
MS
846 if (line[0])
847 {
848 cupsdLogMessage(CUPSD_LOG_ERROR,
c8fef167
MS
849 "Bad request line \"%s\" from %s!",
850 _httpEncodeURI(buf, line, sizeof(buf)),
c934a06c
MS
851 con->http.hostname);
852 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
853 cupsdCloseClient(con);
854 }
f7deaa1a 855 return;
ef416fc2 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,
c8fef167
MS
863 "Bad request line \"%s\" from %s!",
864 _httpEncodeURI(buf, line, sizeof(buf)),
ef416fc2 865 con->http.hostname);
5bd77a73 866 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 867 cupsdCloseClient(con);
868 return;
ef416fc2 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 {
f11a948a
MS
881 cupsdLogMessage(CUPSD_LOG_ERROR,
882 "Unsupported request line \"%s\" from %s!",
c8fef167
MS
883 _httpEncodeURI(buf, line, sizeof(buf)),
884 con->http.hostname);
5bd77a73 885 cupsdSendError(con, HTTP_NOT_SUPPORTED, CUPSD_AUTH_NONE);
f7deaa1a 886 cupsdCloseClient(con);
887 return;
ef416fc2 888 }
889 break;
890 }
891
892 /*
893 * Handle full URLs in the request line...
894 */
895
896 if (strcmp(con->uri, "*"))
897 {
e6013cfa 898 char scheme[HTTP_MAX_URI], /* Method/scheme */
ef416fc2 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
a4d04587 909 httpSeparateURI(HTTP_URI_CODING_MOST, con->uri,
e6013cfa 910 scheme, sizeof(scheme),
ef416fc2 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
e6013cfa 920 if (strcmp(scheme, "file") &&
88f9aafc
MS
921 _cups_strcasecmp(hostname, ServerName) &&
922 _cups_strcasecmp(hostname, "localhost") &&
e6013cfa 923 !isdigit(hostname[0]) && hostname[0] != '[')
ef416fc2 924 {
925 /*
926 * Nope, we don't do proxies...
927 */
928
929 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad URI \"%s\" in request!",
930 con->uri);
5bd77a73 931 cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE);
f7deaa1a 932 cupsdCloseClient(con);
933 return;
ef416fc2 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);
5bd77a73 965 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 966 cupsdCloseClient(con);
967 return;
ef416fc2 968 }
969
dfd5680b 970 gettimeofday(&(con->start), NULL);
ef416fc2 971 con->operation = con->http.state;
972
89d46774 973 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %s %s HTTP/%d.%d",
974 con->http.fd, operation, con->uri,
ef416fc2 975 con->http.version / 100, con->http.version % 100);
976
977 con->http.status = HTTP_OK;
978
49d87452
MS
979 if (!cupsArrayFind(ActiveClients, con))
980 {
981 cupsArrayAdd(ActiveClients, con);
982 cupsdSetBusyState();
983 }
3dfe78b3 984
ef416fc2 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
f7deaa1a 996 while ((status = httpUpdate(HTTP(con))) == HTTP_CONTINUE)
ee571f26 997 if (!data_ready(con))
f7deaa1a 998 break;
ef416fc2 999
1000 if (status != HTTP_OK && status != HTTP_CONTINUE)
1001 {
f11a948a
MS
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
5bd77a73 1012 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 1013 cupsdCloseClient(con);
1014 return;
ef416fc2 1015 }
1016 break;
1017
1018 default :
e6013cfa
MS
1019 if (!data_ready(con) && recv(con->http.fd, buf, 1, MSG_PEEK) < 1)
1020 {
1021 /*
1022 * Connection closed...
1023 */
1024
f11a948a
MS
1025 cupsdLogMessage(CUPSD_LOG_DEBUG,
1026 "cupsdReadClient: %d Closing on EOF", con->http.fd);
e6013cfa
MS
1027 cupsdCloseClient(con);
1028 return;
1029 }
ef416fc2 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
f11a948a
MS
1046 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
1047 ',')) != NULL)
ef416fc2 1048 *ptr = '\0';
1049
f11a948a
MS
1050 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
1051 ';')) != NULL)
ef416fc2 1052 *ptr = '\0';
1053
f11a948a
MS
1054 if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
1055 "charset=")) != NULL)
ef416fc2 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
f8b3a85b
MS
1069 snprintf(locale, sizeof(locale), "%s.UTF-8",
1070 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
d6ae789d 1071
ef416fc2 1072 con->language = cupsLangGet(locale);
1073 }
1074 else
1075 con->language = cupsLangGet(DefaultLocale);
1076
1077 cupsdAuthorize(con);
1078
88f9aafc 1079 if (!_cups_strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive",
f11a948a 1080 10) && KeepAlive)
ef416fc2 1081 con->http.keep_alive = HTTP_KEEPALIVE_ON;
88f9aafc 1082 else if (!_cups_strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "close", 5))
52f6f666 1083 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
ef416fc2 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
5bd77a73 1092 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
f7deaa1a 1093 {
f11a948a 1094 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing Host: field in request!");
f7deaa1a 1095 cupsdCloseClient(con);
1096 return;
1097 }
ef416fc2 1098 }
e07d4801 1099 else if (!valid_host(con))
e6013cfa
MS
1100 {
1101 /*
1102 * Access to localhost must use "localhost" or the corresponding IPv4
1103 * or IPv6 values in the Host: field.
1104 */
1105
f11a948a 1106 cupsdLogMessage(CUPSD_LOG_ERROR,
e6013cfa
MS
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 }
ef416fc2 1116 else if (con->operation == HTTP_OPTIONS)
1117 {
1118 /*
1119 * Do OPTIONS command...
1120 */
1121
5bd77a73 1122 if (con->best && con->best->type != CUPSD_AUTH_NONE)
ef416fc2 1123 {
5bd77a73 1124 if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1125 {
1126 cupsdCloseClient(con);
1127 return;
1128 }
ef416fc2 1129 }
1130
88f9aafc 1131 if (!_cups_strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
ef416fc2 1132 con->http.tls == NULL)
1133 {
1134#ifdef HAVE_SSL
1135 /*
1136 * Do encryption stuff...
1137 */
1138
5bd77a73 1139 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1140 {
1141 cupsdCloseClient(con);
1142 return;
1143 }
ef416fc2 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");
07725fee 1149
1150 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1151 {
1152 cupsdCloseClient(con);
1153 return;
1154 }
ef416fc2 1155
411affcf 1156 if (!encrypt_client(con))
f7deaa1a 1157 {
1158 cupsdCloseClient(con);
1159 return;
1160 }
ef416fc2 1161#else
5bd77a73 1162 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
f7deaa1a 1163 {
1164 cupsdCloseClient(con);
1165 return;
1166 }
ef416fc2 1167#endif /* HAVE_SSL */
1168 }
1169
5bd77a73 1170 if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1171 {
1172 cupsdCloseClient(con);
1173 return;
1174 }
ef416fc2 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");
07725fee 1179
1180 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1181 {
1182 cupsdCloseClient(con);
1183 return;
1184 }
ef416fc2 1185 }
1186 else if (!is_path_absolute(con->uri))
1187 {
1188 /*
1189 * Protect against malicious users!
1190 */
1191
bf3816c7
MS
1192 cupsdLogMessage(CUPSD_LOG_ERROR,
1193 "Request for non-absolute resource \"%s\"!", con->uri);
1194
5bd77a73 1195 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
f7deaa1a 1196 {
1197 cupsdCloseClient(con);
1198 return;
1199 }
ef416fc2 1200 }
1201 else
1202 {
88f9aafc 1203 if (!_cups_strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
ef416fc2 1204 con->http.tls == NULL)
1205 {
1206#ifdef HAVE_SSL
1207 /*
1208 * Do encryption stuff...
1209 */
1210
5bd77a73 1211 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1212 {
1213 cupsdCloseClient(con);
1214 return;
1215 }
ef416fc2 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");
07725fee 1221
1222 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1223 {
1224 cupsdCloseClient(con);
1225 return;
1226 }
ef416fc2 1227
411affcf 1228 if (!encrypt_client(con))
f7deaa1a 1229 {
1230 cupsdCloseClient(con);
1231 return;
1232 }
ef416fc2 1233#else
5bd77a73 1234 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
f7deaa1a 1235 {
1236 cupsdCloseClient(con);
1237 return;
1238 }
ef416fc2 1239#endif /* HAVE_SSL */
1240 }
1241
1242 if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_OK)
1243 {
5bd77a73 1244 cupsdSendError(con, status, CUPSD_AUTH_NONE);
f7deaa1a 1245 cupsdCloseClient(con);
1246 return;
ef416fc2 1247 }
1248
b423cd4c 1249 if (con->http.expect &&
1250 (con->operation == HTTP_POST || con->operation == HTTP_PUT))
ef416fc2 1251 {
b423cd4c 1252 if (con->http.expect == HTTP_CONTINUE)
1253 {
1254 /*
1255 * Send 100-continue header...
1256 */
1257
5bd77a73 1258 if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1259 {
1260 cupsdCloseClient(con);
1261 return;
1262 }
b423cd4c 1263 }
1264 else
1265 {
1266 /*
1267 * Send 417-expectation-failed header...
1268 */
1269
f11a948a
MS
1270 if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL,
1271 CUPSD_AUTH_NONE))
f7deaa1a 1272 {
1273 cupsdCloseClient(con);
1274 return;
1275 }
b423cd4c 1276
1277 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1278 httpPrintf(HTTP(con), "\r\n");
07725fee 1279
1280 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1281 {
1282 cupsdCloseClient(con);
1283 return;
1284 }
b423cd4c 1285 }
ef416fc2 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 {
5bd77a73 1305 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1306 {
1307 cupsdCloseClient(con);
1308 return;
1309 }
ef416fc2 1310
1311 break;
1312 }
1313 }
7cf5915e
MS
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 }
0268488e 1343 else if (!WebInterface)
229681c1
MS
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 }
0268488e
MS
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))
ef416fc2 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
b423cd4c 1375 cupsdSetString(&con->options, strchr(con->uri + 6, '?'));
ef416fc2 1376 }
1377 else if (!strncmp(con->uri, "/printers", 9))
1378 {
1379 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1380 ServerBin);
b423cd4c 1381
1382 if (con->uri[9] && con->uri[10])
1383 cupsdSetString(&con->options, con->uri + 9);
1384 else
1385 cupsdSetString(&con->options, NULL);
ef416fc2 1386 }
1387 else if (!strncmp(con->uri, "/classes", 8))
1388 {
1389 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1390 ServerBin);
b423cd4c 1391
1392 if (con->uri[8] && con->uri[9])
1393 cupsdSetString(&con->options, con->uri + 8);
1394 else
1395 cupsdSetString(&con->options, NULL);
ef416fc2 1396 }
1397 else if (!strncmp(con->uri, "/jobs", 5))
1398 {
1399 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1400 ServerBin);
b423cd4c 1401
1402 if (con->uri[5] && con->uri[6])
1403 cupsdSetString(&con->options, con->uri + 5);
1404 else
1405 cupsdSetString(&con->options, NULL);
ef416fc2 1406 }
1407 else
1408 {
1409 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1410 ServerBin);
ef416fc2 1411
b423cd4c 1412 if (con->uri[5] && con->uri[6])
1413 cupsdSetString(&con->options, con->uri + 5);
1414 else
1415 cupsdSetString(&con->options, NULL);
1416 }
ef416fc2 1417
1418 if (!cupsdSendCommand(con, con->command, con->options, 0))
1419 {
5bd77a73 1420 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1421 {
1422 cupsdCloseClient(con);
1423 return;
1424 }
ef416fc2 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 /*
bf3816c7 1440 * GET can only be done to configuration files directly under
ef416fc2 1441 * /admin/conf...
1442 */
1443
bf3816c7
MS
1444 cupsdLogMessage(CUPSD_LOG_ERROR,
1445 "Request for subdirectory \"%s\"!", con->uri);
1446
5bd77a73 1447 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
f7deaa1a 1448 {
1449 cupsdCloseClient(con);
1450 return;
1451 }
ef416fc2 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 {
5bd77a73 1464 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1465 {
1466 cupsdCloseClient(con);
1467 return;
1468 }
ef416fc2 1469
1470 break;
1471 }
1472
4400e98d 1473 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
ef416fc2 1474
e1d6a774 1475 if (is_cgi(con, filename, &filestats, type))
ef416fc2 1476 {
1477 /*
1478 * Note: con->command and con->options were set by
e1d6a774 1479 * is_cgi()...
ef416fc2 1480 */
1481
1482 if (!cupsdSendCommand(con, con->command, con->options, 0))
1483 {
5bd77a73 1484 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1485 {
1486 cupsdCloseClient(con);
1487 return;
1488 }
ef416fc2 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 {
5bd77a73 1500 if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
f7deaa1a 1501 {
1502 cupsdCloseClient(con);
1503 return;
1504 }
ef416fc2 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
a74454a7 1513 if (!write_file(con, HTTP_OK, filename, line, &filestats))
f7deaa1a 1514 {
1515 cupsdCloseClient(con);
1516 return;
1517 }
ef416fc2 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
ef416fc2 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
5bd77a73 1536 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 1537 {
1538 cupsdCloseClient(con);
1539 return;
1540 }
ef416fc2 1541
1542 break;
1543 }
839a51c8
MS
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))
ef416fc2 1547 {
1548 /*
1549 * Negative content lengths are invalid!
1550 */
1551
5bd77a73 1552 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
f7deaa1a 1553 {
1554 cupsdCloseClient(con);
1555 return;
1556 }
ef416fc2 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();
229681c1
MS
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 }
ef416fc2 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
b423cd4c 1600 cupsdSetString(&con->options, strchr(con->uri + 6, '?'));
ef416fc2 1601 }
1602 else if (!strncmp(con->uri, "/printers", 9))
1603 {
1604 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1605 ServerBin);
b423cd4c 1606
1607 if (con->uri[9] && con->uri[10])
1608 cupsdSetString(&con->options, con->uri + 9);
1609 else
1610 cupsdSetString(&con->options, NULL);
ef416fc2 1611 }
1612 else if (!strncmp(con->uri, "/classes", 8))
1613 {
1614 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1615 ServerBin);
b423cd4c 1616
1617 if (con->uri[8] && con->uri[9])
1618 cupsdSetString(&con->options, con->uri + 8);
1619 else
1620 cupsdSetString(&con->options, NULL);
ef416fc2 1621 }
1622 else if (!strncmp(con->uri, "/jobs", 5))
1623 {
1624 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1625 ServerBin);
b423cd4c 1626
1627 if (con->uri[5] && con->uri[6])
1628 cupsdSetString(&con->options, con->uri + 5);
1629 else
1630 cupsdSetString(&con->options, NULL);
ef416fc2 1631 }
1632 else
1633 {
1634 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1635 ServerBin);
ef416fc2 1636
b423cd4c 1637 if (con->uri[5] && con->uri[6])
1638 cupsdSetString(&con->options, con->uri + 5);
1639 else
1640 cupsdSetString(&con->options, NULL);
1641 }
ef416fc2 1642
ef416fc2 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 {
5bd77a73 1655 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1656 {
1657 cupsdCloseClient(con);
1658 return;
1659 }
ef416fc2 1660
1661 break;
1662 }
1663
4400e98d 1664 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
ef416fc2 1665
e1d6a774 1666 if (!is_cgi(con, filename, &filestats, type))
ef416fc2 1667 {
1668 /*
1669 * Only POST to CGI's...
1670 */
1671
5bd77a73 1672 if (!cupsdSendError(con, HTTP_UNAUTHORIZED, CUPSD_AUTH_NONE))
f7deaa1a 1673 {
1674 cupsdCloseClient(con);
1675 return;
1676 }
ef416fc2 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
bf3816c7
MS
1695 cupsdLogMessage(CUPSD_LOG_ERROR,
1696 "Request for subdirectory \"%s\"!", con->uri);
1697
5bd77a73 1698 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
f7deaa1a 1699 {
1700 cupsdCloseClient(con);
1701 return;
1702 }
ef416fc2 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
ef416fc2 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
5bd77a73 1720 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 1721 {
1722 cupsdCloseClient(con);
1723 return;
1724 }
ef416fc2 1725
1726 break;
1727 }
1728 else if (con->http.data_remaining < 0)
1729 {
1730 /*
1731 * Negative content lengths are invalid!
1732 */
1733
5bd77a73 1734 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
f7deaa1a 1735 {
1736 cupsdCloseClient(con);
1737 return;
1738 }
ef416fc2 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
ef416fc2 1751 if (con->file < 0)
1752 {
a4924f6c
MS
1753 cupsdLogMessage(CUPSD_LOG_ERROR,
1754 "Unable to create request file %s: %s",
1755 con->filename, strerror(errno));
1756
5bd77a73 1757 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 1758 {
1759 cupsdCloseClient(con);
1760 return;
1761 }
ef416fc2 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 :
5bd77a73 1771 cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE);
f7deaa1a 1772 cupsdCloseClient(con);
1773 return;
ef416fc2 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 {
5bd77a73 1790 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1791 {
1792 cupsdCloseClient(con);
1793 return;
1794 }
ef416fc2 1795
1796 break;
1797 }
1798 }
229681c1
MS
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 }
ef416fc2 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
5bd77a73 1858 if (!cupsdSendHeader(con, HTTP_OK, "text/html", CUPSD_AUTH_NONE))
f7deaa1a 1859 {
1860 cupsdCloseClient(con);
1861 return;
1862 }
ef416fc2 1863
1864 if (httpPrintf(HTTP(con), "\r\n") < 0)
f7deaa1a 1865 {
1866 cupsdCloseClient(con);
1867 return;
1868 }
ef416fc2 1869
07725fee 1870 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1871 {
1872 cupsdCloseClient(con);
1873 return;
1874 }
d09495fa 1875
ef416fc2 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
bf3816c7
MS
1890 cupsdLogMessage(CUPSD_LOG_ERROR,
1891 "Request for subdirectory \"%s\"!", con->uri);
1892
5bd77a73 1893 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
f7deaa1a 1894 {
1895 cupsdCloseClient(con);
1896 return;
1897 }
ef416fc2 1898
1899 break;
1900 }
1901 else if ((filename = get_file(con, &filestats, buf,
1902 sizeof(buf))) == NULL)
1903 {
f11a948a
MS
1904 if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html",
1905 CUPSD_AUTH_NONE))
f7deaa1a 1906 {
1907 cupsdCloseClient(con);
1908 return;
1909 }
ef416fc2 1910
1911 cupsdLogRequest(con, HTTP_NOT_FOUND);
1912 }
1913 else if (!check_if_modified(con, &filestats))
1914 {
5bd77a73 1915 if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
f7deaa1a 1916 {
1917 cupsdCloseClient(con);
1918 return;
1919 }
ef416fc2 1920
1921 cupsdLogRequest(con, HTTP_NOT_MODIFIED);
1922 }
1923 else
1924 {
1925 /*
1926 * Serve a file...
1927 */
1928
4400e98d 1929 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
ef416fc2 1930 if (type == NULL)
1931 strcpy(line, "text/plain");
1932 else
1933 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1934
5bd77a73 1935 if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
f7deaa1a 1936 {
1937 cupsdCloseClient(con);
1938 return;
1939 }
ef416fc2 1940
1941 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1942 httpGetDateString(filestats.st_mtime)) < 0)
f7deaa1a 1943 {
1944 cupsdCloseClient(con);
1945 return;
1946 }
ef416fc2 1947
1948 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1949 (unsigned long)filestats.st_size) < 0)
f7deaa1a 1950 {
1951 cupsdCloseClient(con);
1952 return;
1953 }
ef416fc2 1954
1955 cupsdLogRequest(con, HTTP_OK);
1956 }
1957
1958 if (httpPrintf(HTTP(con), "\r\n") < 0)
f7deaa1a 1959 {
1960 cupsdCloseClient(con);
1961 return;
1962 }
ef416fc2 1963
07725fee 1964 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1965 {
1966 cupsdCloseClient(con);
1967 return;
1968 }
d09495fa 1969
ef416fc2 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 :
f7deaa1a 1986 do
ef416fc2 1987 {
f7deaa1a 1988 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
1989 {
f11a948a
MS
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
f7deaa1a 2000 cupsdCloseClient(con);
2001 return;
2002 }
2003 else if (bytes > 0)
2004 {
2005 con->bytes += bytes;
ef416fc2 2006
f7deaa1a 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));
ef416fc2 2012
f7deaa1a 2013 close(con->file);
2014 con->file = -1;
2015 unlink(con->filename);
2016 cupsdClearString(&con->filename);
ef416fc2 2017
5bd77a73 2018 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 2019 {
2020 cupsdCloseClient(con);
2021 return;
2022 }
2023 }
ef416fc2 2024 }
f7deaa1a 2025 }
ee571f26 2026 while (con->http.state == HTTP_PUT_RECV && data_ready(con));
ef416fc2 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
ef416fc2 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
ef416fc2 2046 unlink(con->filename);
2047 cupsdClearString(&con->filename);
2048
5bd77a73 2049 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 2050 {
2051 cupsdCloseClient(con);
2052 return;
2053 }
ef416fc2 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
5bd77a73 2066 if (!cupsdSendError(con, status, CUPSD_AUTH_NONE))
f7deaa1a 2067 {
2068 cupsdCloseClient(con);
2069 return;
2070 }
ef416fc2 2071 }
2072 break;
2073
2074 case HTTP_POST_RECV :
f7deaa1a 2075 do
ef416fc2 2076 {
f11a948a 2077 if (con->request && con->file < 0)
ef416fc2 2078 {
f7deaa1a 2079 /*
2080 * Grab any request data from the connection...
2081 */
ef416fc2 2082
f7deaa1a 2083 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
ef416fc2 2084 {
f7deaa1a 2085 cupsdLogMessage(CUPSD_LOG_ERROR,
2086 "cupsdReadClient: %d IPP Read Error!",
2087 con->http.fd);
2088
5bd77a73 2089 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 2090 cupsdCloseClient(con);
2091 return;
ef416fc2 2092 }
f7deaa1a 2093 else if (ipp_state != IPP_DATA)
2094 {
2095 if (con->http.state == HTTP_POST_SEND)
2096 {
5bd77a73 2097 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 2098 cupsdCloseClient(con);
2099 return;
2100 }
ef416fc2 2101
f7deaa1a 2102 break;
2103 }
2104 else
f11a948a
MS
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);
f7deaa1a 2111 con->bytes += ippLength(con->request);
f11a948a 2112 }
f7deaa1a 2113 }
ef416fc2 2114
f7deaa1a 2115 if (con->file < 0 && con->http.state != HTTP_POST_SEND)
2116 {
2117 /*
2118 * Create a file as needed for the request data...
2119 */
ef416fc2 2120
f11a948a
MS
2121 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
2122 request_id ++);
f7deaa1a 2123 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
ef416fc2 2124
f7deaa1a 2125 if (con->file < 0)
2126 {
a4924f6c
MS
2127 cupsdLogMessage(CUPSD_LOG_ERROR,
2128 "Unable to create request file %s: %s",
2129 con->filename, strerror(errno));
2130
5bd77a73 2131 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 2132 {
2133 cupsdCloseClient(con);
2134 return;
2135 }
2136 }
ef416fc2 2137
f7deaa1a 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 }
ef416fc2 2142
f7deaa1a 2143 if (con->http.state != HTTP_POST_SEND)
ef416fc2 2144 {
f7deaa1a 2145 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
2146 {
f11a948a
MS
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
f7deaa1a 2157 cupsdCloseClient(con);
2158 return;
2159 }
2160 else if (bytes > 0)
2161 {
2162 con->bytes += bytes;
ef416fc2 2163
f7deaa1a 2164 if (write(con->file, line, bytes) < bytes)
2165 {
2166 cupsdLogMessage(CUPSD_LOG_ERROR,
f11a948a
MS
2167 "cupsdReadClient: Unable to write %d bytes to "
2168 "%s: %s", bytes, con->filename,
2169 strerror(errno));
ef416fc2 2170
f7deaa1a 2171 close(con->file);
2172 con->file = -1;
2173 unlink(con->filename);
2174 cupsdClearString(&con->filename);
ef416fc2 2175
f11a948a
MS
2176 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE,
2177 CUPSD_AUTH_NONE))
f7deaa1a 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 {
f11a948a
MS
2188 cupsdLogMessage(CUPSD_LOG_DEBUG,
2189 "cupsdReadClient: %d Closing on unknown HTTP "
2190 "state %d", con->http.fd, con->http.state);
f7deaa1a 2191 cupsdCloseClient(con);
2192 return;
ef416fc2 2193 }
2194 }
f7deaa1a 2195 }
ee571f26 2196 while (con->http.state == HTTP_POST_RECV && data_ready(con));
ef416fc2 2197
2198 if (con->http.state == HTTP_POST_SEND)
2199 {
2200 if (con->file >= 0)
2201 {
2202 fstat(con->file, &filestats);
2203
ef416fc2 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
ef416fc2 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
5bd77a73 2227 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 2228 {
2229 cupsdCloseClient(con);
2230 return;
2231 }
ef416fc2 2232 }
f8b3a85b
MS
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 }
ef416fc2 2242
2243 if (con->command)
2244 {
2245 if (!cupsdSendCommand(con, con->command, con->options, 0))
2246 {
5bd77a73 2247 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 2248 {
2249 cupsdCloseClient(con);
2250 return;
2251 }
ef416fc2 2252 }
2253 else
2254 cupsdLogRequest(con, HTTP_OK);
2255 }
2256 }
2257
2258 if (con->request)
f7deaa1a 2259 {
2260 cupsdProcessIPPRequest(con);
bc44d920 2261
2262 if (con->filename)
2263 {
bc44d920 2264 unlink(con->filename);
2265 cupsdClearString(&con->filename);
2266 }
2267
f7deaa1a 2268 return;
2269 }
ef416fc2 2270 }
2271 break;
2272
2273 default :
2274 break; /* Anti-compiler-warning-code */
2275 }
2276
3dfe78b3
MS
2277 if (con->http.state == HTTP_WAITING)
2278 {
2279 if (!con->http.keep_alive)
f11a948a
MS
2280 {
2281 cupsdLogMessage(CUPSD_LOG_DEBUG,
2282 "cupsdReadClient: %d Closing because Keep-Alive disabled",
2283 con->http.fd);
3dfe78b3 2284 cupsdCloseClient(con);
f11a948a 2285 }
3dfe78b3
MS
2286 else
2287 {
2288 cupsArrayRemove(ActiveClients, con);
2289 cupsdSetBusyState();
2290 }
2291 }
ef416fc2 2292}
2293
2294
2295/*
2296 * 'cupsdSendCommand()' - Send output from a command via HTTP.
2297 */
2298
2299int /* O - 1 on success, 0 on failure */
2300cupsdSendCommand(
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)
ed486911 2310 {
ef416fc2 2311 fd = open(con->filename, O_RDONLY);
ef416fc2 2312
ed486911 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 }
ef416fc2 2321
ed486911 2322 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
2323 }
2324 else
2325 fd = -1;
ef416fc2 2326
2327 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root);
2328
ed486911 2329 if (fd >= 0)
2330 close(fd);
ef416fc2 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
f899b121 2343 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
ef416fc2 2344
2345 con->sent_header = 0;
2346 con->file_ready = 0;
2347 con->got_fields = 0;
68b10830 2348 con->header_used = 0;
ef416fc2 2349
2350 return (1);
2351}
2352
2353
2354/*
2355 * 'cupsdSendError()' - Send an error message via HTTP.
2356 */
2357
2358int /* O - 1 if successful, 0 otherwise */
2359cupsdSendError(cupsd_client_t *con, /* I - Connection */
f899b121 2360 http_status_t code, /* I - Error code */
2361 int auth_type)/* I - Authentication type */
ef416fc2 2362{
b9faaae1
MS
2363 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2364 "cupsdSendError(con=%p(%d), code=%d, auth_type=%d", con,
2365 con->http.fd, code, auth_type);
2366
4744bd90 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 &&
88f9aafc 2375 _cups_strcasecmp(con->http.hostname, "localhost") &&
4744bd90 2376 !con->http.tls)
2377 {
4744bd90 2378 code = HTTP_UPGRADE_REQUIRED;
2379 }
2380#endif /* HAVE_SSL */
2381
ef416fc2 2382 /*
2383 * Put the request in the access_log file...
2384 */
2385
2386 cupsdLogRequest(con, code);
2387
ef416fc2 2388 /*
2389 * To work around bugs in some proxies, don't use Keep-Alive for some
2390 * error messages...
f7deaa1a 2391 *
2392 * Kerberos authentication doesn't work without Keep-Alive, so
2393 * never disable it in that case.
ef416fc2 2394 */
2395
5bd77a73 2396 if (code >= HTTP_BAD_REQUEST && con->http.auth_type != CUPSD_AUTH_NEGOTIATE)
ef416fc2 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
f899b121 2404 if (!cupsdSendHeader(con, code, NULL, auth_type))
ef416fc2 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
f7deaa1a 2416 if (con->http.version >= HTTP_1_1 &&
2417 con->http.keep_alive == HTTP_KEEPALIVE_OFF)
ef416fc2 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
80ca4592 2429 char message[4096], /* Message for user */
2430 urltext[1024], /* URL redirection text */
2431 redirect[1024]; /* Redirection link */
ef416fc2 2432 const char *text; /* Status-specific text */
2433
80ca4592 2434
2435 redirect[0] = '\0';
2436
ef416fc2 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 "
f7deaa1a 2441 "page. If you are using Kerberos authentication, "
2442 "make sure you have a valid Kerberos ticket."));
ef416fc2 2443 else if (code == HTTP_UPGRADE_REQUIRED)
80ca4592 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),
b86bc4cf 2456 "<META HTTP-EQUIV=\"Refresh\" "
2457 "CONTENT=\"3;URL=https://%s:%d%s\">\n",
80ca4592 2458 con->servername, con->serverport, con->uri);
2459 }
229681c1
MS
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."));
ef416fc2 2464 else
2465 text = "";
2466
2467 snprintf(message, sizeof(message),
745129be
MS
2468 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2469 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
ef416fc2 2470 "<HTML>\n"
2471 "<HEAD>\n"
2472 "\t<META HTTP-EQUIV=\"Content-Type\" "
2473 "CONTENT=\"text/html; charset=utf-8\">\n"
229681c1 2474 "\t<TITLE>%s - " CUPS_SVERSION "</TITLE>\n"
ef416fc2 2475 "\t<LINK REL=\"STYLESHEET\" TYPE=\"text/css\" "
2476 "HREF=\"/cups.css\">\n"
80ca4592 2477 "%s"
ef416fc2 2478 "</HEAD>\n"
2479 "<BODY>\n"
229681c1 2480 "<H1>%s</H1>\n"
ef416fc2 2481 "<P>%s</P>\n"
2482 "</BODY>\n"
2483 "</HTML>\n",
229681c1 2484 httpStatus(code), redirect, httpStatus(code), text);
ef416fc2 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
07725fee 2499 if (cupsdFlushHeader(con) < 0)
2500 return (0);
d09495fa 2501
ef416fc2 2502 con->http.state = HTTP_WAITING;
2503
2504 return (1);
2505}
2506
2507
ef416fc2 2508/*
2509 * 'cupsdSendHeader()' - Send an HTTP request.
2510 */
2511
2512int /* O - 1 on success, 0 on failure */
f899b121 2513cupsdSendHeader(
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 */
ef416fc2 2518{
5a738aea 2519 char auth_str[1024]; /* Authorization string */
07ed0e9a 2520#if 0 /* def HAVE_GSSAPI */
5a738aea
MS
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 */
f7deaa1a 2524
2525
4744bd90 2526 /*
2527 * Send the HTTP status header...
2528 */
2529
b423cd4c 2530 if (code == HTTP_CONTINUE)
2531 {
4744bd90 2532 /*
2533 * 100-continue doesn't send any headers...
2534 */
2535
07725fee 2536 return (httpPrintf(HTTP(con), "HTTP/%d.%d 100 Continue\r\n\r\n",
2537 con->http.version / 100, con->http.version % 100) > 0);
b423cd4c 2538 }
229681c1
MS
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 }
b423cd4c 2548
07725fee 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);
ef416fc2 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)
e6013cfa 2570 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n") < 0)
ef416fc2 2571 return (0);
2572
2573 if (code == HTTP_UNAUTHORIZED)
2574 {
5bd77a73 2575 if (auth_type == CUPSD_AUTH_NONE)
f899b121 2576 {
5bd77a73 2577 if (!con->best || con->best->type <= CUPSD_AUTH_NONE)
f899b121 2578 auth_type = DefaultAuthType;
2579 else
2580 auth_type = con->best->type;
2581 }
f7deaa1a 2582
2583 auth_str[0] = '\0';
2584
5bd77a73 2585 if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
f7deaa1a 2586 strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
5bd77a73 2587 else if (auth_type == CUPSD_AUTH_DIGEST)
f7deaa1a 2588 snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"",
2589 con->http.hostname);
2590#ifdef HAVE_GSSAPI
eac3a0a0
MS
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 */
f7deaa1a 2598 strlcpy(auth_str, "Negotiate", sizeof(auth_str));
eac3a0a0 2599 }
f7deaa1a 2600#endif /* HAVE_GSSAPI */
2601
e07d4801 2602 if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE &&
88f9aafc 2603 !_cups_strcasecmp(con->http.hostname, "localhost"))
ef416fc2 2604 {
e07d4801
MS
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
10d09e33
MS
2614 char *name, /* Current user name */
2615 *auth_key; /* Auth key buffer */
f7deaa1a 2616 size_t auth_size; /* Size of remaining buffer */
2617
f7deaa1a 2618 auth_key = auth_str + strlen(auth_str);
2619 auth_size = sizeof(auth_str) - (auth_key - auth_str);
2620
10d09e33
MS
2621 for (name = (char *)cupsArrayFirst(con->best->names);
2622 name;
2623 name = (char *)cupsArrayNext(con->best->names))
f7deaa1a 2624 {
e07d4801 2625#ifdef HAVE_AUTHORIZATION_H
88f9aafc 2626 if (!_cups_strncasecmp(name, "@AUTHKEY(", 9))
f7deaa1a 2627 {
10d09e33 2628 snprintf(auth_key, auth_size, ", authkey=\"%s\"", name + 9);
f7deaa1a 2629 /* end parenthesis is stripped in conf.c */
2630 break;
2631 }
e07d4801
MS
2632 else
2633#endif /* HAVE_AUTHORIZATION_H */
88f9aafc 2634 if (!_cups_strcasecmp(name, "@SYSTEM"))
f7deaa1a 2635 {
e07d4801
MS
2636#ifdef HAVE_AUTHORIZATION_H
2637 if (SystemGroupAuthKey)
2638 snprintf(auth_key, auth_size,
07ed0e9a 2639 ", authkey=\"%s\"",
e07d4801
MS
2640 SystemGroupAuthKey);
2641 else
2642#else
f11a948a 2643 strlcpy(auth_key, ", trc=\"y\"", auth_size);
e07d4801 2644#endif /* HAVE_AUTHORIZATION_H */
f7deaa1a 2645 break;
2646 }
2647 }
ef416fc2 2648 }
f7deaa1a 2649
bc44d920 2650 if (auth_str[0])
2651 {
68b10830
MS
2652 cupsdLogMessage(CUPSD_LOG_DEBUG,
2653 "cupsdSendHeader: %d WWW-Authenticate: %s", con->http.fd,
bc44d920 2654 auth_str);
2655
2656 if (httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0)
2657 return (0);
2658 }
ef416fc2 2659 }
2660
e1d6a774 2661 if (con->language && strcmp(con->language->language, "C"))
ef416fc2 2662 {
2663 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
2664 con->language->language) < 0)
2665 return (0);
2666 }
2667
e1d6a774 2668 if (type)
ef416fc2 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
2688void
2689cupsdUpdateCGI(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)
f0ab5bff
MS
2698 {
2699 if (loglevel == CUPSD_LOG_INFO)
2700 cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
2701
ef416fc2 2702 if (!strchr(CGIStatusBuffer->buffer, '\n'))
2703 break;
f0ab5bff 2704 }
ef416fc2 2705
d09495fa 2706 if (ptr == NULL && !CGIStatusBuffer->bufused)
ef416fc2 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
f7deaa1a 2723void
ef416fc2 2724cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */
2725{
68b10830
MS
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 */
ef416fc2 2730 ipp_state_t ipp_state; /* IPP state value */
2731
2732
ef416fc2 2733 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1 2734 "cupsdWriteClient(con=%p(%d)) response=%p(%d), file=%d "
ef416fc2 2735 "pipe_pid=%d state=%d",
ae71f5de
MS
2736 con, con->http.fd, con->response,
2737 con->response ? con->response->state : -1,
b94498cf 2738 con->file, con->pipe_pid, con->http.state);
ef416fc2 2739
2740 if (con->http.state != HTTP_GET_SEND &&
2741 con->http.state != HTTP_POST_SEND)
8b116e60
MS
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
f11a948a
MS
2748 cupsdLogMessage(CUPSD_LOG_DEBUG,
2749 "cupsdWriteClient: %d Closing on unknown HTTP state %d",
2750 con->http.fd, con->http.state);
8b116e60 2751 cupsdCloseClient(con);
f7deaa1a 2752 return;
8b116e60 2753 }
f7deaa1a 2754
2755 if (con->pipe_pid)
2756 {
2757 /*
2758 * Make sure we select on the CGI output...
2759 */
2760
f899b121 2761 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
f7deaa1a 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 }
ef416fc2 2775
b94498cf 2776 if (con->response && con->response->state != IPP_DATA)
ef416fc2 2777 {
07725fee 2778 ipp_state = ippWrite(HTTP(con), con->response);
b94498cf 2779 bytes = ipp_state != IPP_ERROR &&
2780 (con->file >= 0 || ipp_state != IPP_DATA);
ef416fc2 2781 }
68b10830
MS
2782 else if ((bytes = read(con->file, con->header,
2783 sizeof(con->header) - con->header_used)) > 0)
ef416fc2 2784 {
68b10830
MS
2785 con->header_used += bytes;
2786
ef416fc2 2787 if (con->pipe_pid && !con->got_fields)
2788 {
2789 /*
2790 * Inspect the data for Content-Type and other fields.
2791 */
2792
68b10830
MS
2793 for (bufptr = con->header, bufend = con->header + con->header_used,
2794 field_col = 0;
2795 !con->got_fields && bufptr < bufend;
2796 bufptr ++)
d1c13e16 2797 {
ef416fc2 2798 if (*bufptr == '\n')
2799 {
2800 /*
2801 * Send line to client...
2802 */
2803
68b10830 2804 if (bufptr > con->header && bufptr[-1] == '\r')
ef416fc2 2805 bufptr[-1] = '\0';
2806 *bufptr++ = '\0';
2807
68b10830 2808 cupsdLogMessage(CUPSD_LOG_DEBUG, "Script header: %s", con->header);
ef416fc2 2809
2810 if (!con->sent_header)
2811 {
2812 /*
2813 * Handle redirection and CGI status codes...
2814 */
2815
88f9aafc 2816 if (!_cups_strncasecmp(con->header, "Location:", 9))
d6ae789d 2817 {
321d8d57
MS
2818 if (!cupsdSendHeader(con, HTTP_SEE_OTHER, NULL, CUPSD_AUTH_NONE))
2819 {
2820 cupsdCloseClient(con);
2821 return;
2822 }
2823
07725fee 2824 con->sent_header = 2;
2825
d6ae789d 2826 if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0)
f7deaa1a 2827 return;
d6ae789d 2828 }
88f9aafc 2829 else if (!_cups_strncasecmp(con->header, "Status:", 7))
07725fee 2830 {
68b10830
MS
2831 cupsdSendError(con, (http_status_t)atoi(con->header + 7),
2832 CUPSD_AUTH_NONE);
07725fee 2833 con->sent_header = 2;
2834 }
ef416fc2 2835 else
4744bd90 2836 {
321d8d57
MS
2837 if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE))
2838 {
2839 cupsdCloseClient(con);
2840 return;
2841 }
2842
07725fee 2843 con->sent_header = 1;
ef416fc2 2844
4744bd90 2845 if (con->http.version == HTTP_1_1)
2846 {
4744bd90 2847 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
f7deaa1a 2848 return;
4744bd90 2849 }
2850 }
ef416fc2 2851 }
2852
88f9aafc 2853 if (_cups_strncasecmp(con->header, "Status:", 7))
68b10830 2854 httpPrintf(HTTP(con), "%s\r\n", con->header);
ef416fc2 2855
ef416fc2 2856 /*
2857 * Update buffer...
2858 */
2859
68b10830 2860 con->header_used -= bufptr - con->header;
d1c13e16 2861
68b10830
MS
2862 if (con->header_used > 0)
2863 memmove(con->header, bufptr, con->header_used);
d1c13e16 2864
68b10830 2865 bufptr = con->header - 1;
ef416fc2 2866
2867 /*
2868 * See if the line was empty...
2869 */
2870
68b10830 2871 if (field_col == 0)
d09495fa 2872 {
ef416fc2 2873 con->got_fields = 1;
d09495fa 2874
07725fee 2875 if (cupsdFlushHeader(con) < 0)
2876 {
2877 cupsdCloseClient(con);
f7deaa1a 2878 return;
07725fee 2879 }
d09495fa 2880
2881 if (con->http.version == HTTP_1_1)
2882 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
2883 }
ef416fc2 2884 else
68b10830 2885 field_col = 0;
ef416fc2 2886 }
2887 else if (*bufptr != '\r')
68b10830 2888 field_col ++;
d1c13e16 2889 }
ef416fc2 2890
68b10830 2891 if (!con->got_fields)
47879b8b 2892 {
ef416fc2 2893 con->http.activity = time(NULL);
47879b8b
MS
2894 return;
2895 }
ef416fc2 2896 }
2897
68b10830 2898 if (con->header_used > 0)
ef416fc2 2899 {
68b10830 2900 if (httpWrite2(HTTP(con), con->header, con->header_used) < 0)
4744bd90 2901 {
f11a948a
MS
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));
4744bd90 2906 cupsdCloseClient(con);
f7deaa1a 2907 return;
4744bd90 2908 }
ef416fc2 2909
01ce6322
MS
2910 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
2911 httpFlushWrite(HTTP(con));
ae71f5de 2912
68b10830 2913 con->bytes += con->header_used;
ef416fc2 2914
4744bd90 2915 if (con->http.state == HTTP_WAITING)
2916 bytes = 0;
68b10830
MS
2917 else
2918 bytes = con->header_used;
2919
2920 con->header_used = 0;
4744bd90 2921 }
ef416fc2 2922 }
2923
8b116e60
MS
2924 if (bytes <= 0 ||
2925 (con->http.state != HTTP_GET_SEND && con->http.state != HTTP_POST_SEND))
ef416fc2 2926 {
38e73f87 2927 if (!con->sent_header && con->pipe_pid)
94da7e34
MS
2928 cupsdSendError(con, HTTP_SERVER_ERROR, CUPSD_AUTH_NONE);
2929 else
2930 {
2931 cupsdLogRequest(con, HTTP_OK);
ef416fc2 2932
94da7e34 2933 httpFlushWrite(HTTP(con));
ef416fc2 2934
94da7e34 2935 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED && con->sent_header == 1)
ef416fc2 2936 {
94da7e34
MS
2937 if (httpWrite2(HTTP(con), "", 0) < 0)
2938 {
f11a948a
MS
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));
94da7e34
MS
2943 cupsdCloseClient(con);
2944 return;
2945 }
ef416fc2 2946 }
2947 }
2948
2949 con->http.state = HTTP_WAITING;
2950
f7deaa1a 2951 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
ef416fc2 2952
2953 if (con->file >= 0)
2954 {
f7deaa1a 2955 cupsdRemoveSelect(con->file);
ef416fc2 2956
2957 if (con->pipe_pid)
2958 cupsdEndProcess(con->pipe_pid, 0);
2959
ef416fc2 2960 close(con->file);
2961 con->file = -1;
2962 con->pipe_pid = 0;
2963 }
2964
2965 if (con->filename)
2966 {
ef416fc2 2967 unlink(con->filename);
2968 cupsdClearString(&con->filename);
2969 }
2970
2abf387c 2971 if (con->request)
ef416fc2 2972 {
2973 ippDelete(con->request);
2974 con->request = NULL;
2975 }
2976
2abf387c 2977 if (con->response)
ef416fc2 2978 {
2979 ippDelete(con->response);
2980 con->response = NULL;
2981 }
2982
2983 cupsdClearString(&con->command);
2984 cupsdClearString(&con->options);
b86bc4cf 2985 cupsdClearString(&con->query_string);
ef416fc2 2986
2987 if (!con->http.keep_alive)
2988 {
f11a948a
MS
2989 cupsdLogMessage(CUPSD_LOG_DEBUG,
2990 "cupsdWriteClient: %d Closing because Keep-Alive disabled",
2991 con->http.fd);
ef416fc2 2992 cupsdCloseClient(con);
f7deaa1a 2993 return;
ef416fc2 2994 }
f11a948a
MS
2995 else
2996 {
2997 cupsArrayRemove(ActiveClients, con);
2998 cupsdSetBusyState();
2999 }
ef416fc2 3000 }
3001
3002 con->http.activity = time(NULL);
f7deaa1a 3003}
ef416fc2 3004
f7deaa1a 3005
e1d6a774 3006/*
3007 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
3008 */
3009
3010static int /* O - 1 if modified since */
3011check_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,
b9faaae1
MS
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);
e1d6a774 3032
3033 while (*ptr != '\0')
3034 {
3035 while (isspace(*ptr) || *ptr == ';')
3036 ptr ++;
3037
88f9aafc 3038 if (_cups_strncasecmp(ptr, "length=", 7) == 0)
e1d6a774 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 }
e53920b9 3052 else
3053 ptr ++;
e1d6a774 3054 }
3055
e1d6a774 3056 return ((size != filestats->st_size && size != 0) ||
3057 (date < filestats->st_mtime && date != 0) ||
3058 (size == 0 && date == 0));
3059}
3060
3061
3dfe78b3
MS
3062/*
3063 * 'compare_clients()' - Compare two client connections.
3064 */
3065
3066static int /* O - Result of comparison */
3067compare_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
ee571f26
MS
3082/*
3083 * 'data_ready()' - Check whether data is available from a client.
3084 */
3085
3086static int /* O - 1 if data is ready, 0 otherwise */
3087data_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)
7cf5915e 3098 if (gnutls_record_check_pending(con->http.tls))
ee571f26
MS
3099 return (1);
3100# elif defined(HAVE_CDSASSL)
3101 size_t bytes; /* Bytes that are available */
3102
7cf5915e 3103 if (!SSLGetBufferedReadSize(con->http.tls, &bytes) && bytes > 0)
ee571f26
MS
3104 return (1);
3105# endif /* HAVE_LIBSSL */
3106 }
3107#endif /* HAVE_SSL */
3108
3109 return (0);
3110}
3111
3112
80ca4592 3113#ifdef HAVE_SSL
e1d6a774 3114/*
3115 * 'encrypt_client()' - Enable encryption for the client...
3116 */
3117
3118static int /* O - 1 on success, 0 on error */
3119encrypt_client(cupsd_client_t *con) /* I - Client to encrypt */
3120{
80ca4592 3121# ifdef HAVE_LIBSSL
e1d6a774 3122 SSL_CTX *context; /* Context for encryption */
411affcf 3123 BIO *bio; /* BIO data */
e1d6a774 3124 unsigned long error; /* Error code */
3125
3126
b9faaae1
MS
3127 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3128 con->http.fd);
3129
8ca02f3c 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
b86bc4cf 3140 if (!make_certificate(con))
8ca02f3c 3141 return (0);
3142 }
3143
e1d6a774 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 */
b19ccc9e 3151 if (SSLOptions & CUPSD_SSL_NOEMPTY)
58dc1933 3152 SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
e1d6a774 3153 SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
58dc1933 3154 SSL_CTX_use_certificate_chain_file(context, ServerCertificate);
e1d6a774 3155
411affcf 3156 bio = BIO_new(_httpBIOMethods());
3157 BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con));
3158
7cf5915e
MS
3159 con->http.tls = SSL_new(context);
3160 SSL_set_bio(con->http.tls, bio, bio);
e1d6a774 3161
7cf5915e 3162 if (SSL_accept(con->http.tls) != 1)
e1d6a774 3163 {
b9faaae1 3164 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to encrypt connection from %s!",
e1d6a774 3165 con->http.hostname);
3166
3167 while ((error = ERR_get_error()) != 0)
b9faaae1 3168 cupsdLogMessage(CUPSD_LOG_ERROR, "%s", ERR_error_string(error, NULL));
e1d6a774 3169
3170 SSL_CTX_free(context);
7cf5915e
MS
3171 SSL_free(con->http.tls);
3172 con->http.tls = NULL;
e1d6a774 3173 return (0);
3174 }
3175
b9faaae1
MS
3176 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3177 con->http.hostname);
e1d6a774 3178
e1d6a774 3179 return (1);
f7deaa1a 3180
80ca4592 3181# elif defined(HAVE_GNUTLS)
a4845881 3182 int status; /* Error code */
e1d6a774 3183 gnutls_certificate_server_credentials *credentials;
3184 /* TLS credentials */
a4845881 3185 const char *priority; /* Priority string */
e1d6a774 3186
3187
b9faaae1
MS
3188 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3189 con->http.fd);
3190
e1d6a774 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
b86bc4cf 3201 if (!make_certificate(con))
8ca02f3c 3202 return (0);
e1d6a774 3203 }
3204
3205 /*
3206 * Create the SSL object and perform the SSL handshake...
3207 */
3208
e1d6a774 3209 credentials = (gnutls_certificate_server_credentials *)
3210 malloc(sizeof(gnutls_certificate_server_credentials));
3211 if (credentials == NULL)
3212 {
3213 cupsdLogMessage(CUPSD_LOG_ERROR,
b9faaae1
MS
3214 "Unable to encrypt connection from %s - %s",
3215 con->http.hostname, strerror(errno));
e1d6a774 3216
e1d6a774 3217 return (0);
3218 }
3219
3220 gnutls_certificate_allocate_credentials(credentials);
f7deaa1a 3221 gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
e1d6a774 3222 ServerKey, GNUTLS_X509_FMT_PEM);
3223
f99f3698 3224 gnutls_init(&con->http.tls, GNUTLS_SERVER);
7cf5915e 3225 gnutls_set_default_priority(con->http.tls);
a4845881
MS
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)
e1d6a774 3230 {
3231 cupsdLogMessage(CUPSD_LOG_ERROR,
a4845881
MS
3232 "Unable to encrypt connection from %s - %s (%s)",
3233 con->http.hostname, gnutls_strerror(status), priority);
e1d6a774 3234
7cf5915e 3235 gnutls_deinit(con->http.tls);
e1d6a774 3236 gnutls_certificate_free_credentials(*credentials);
7cf5915e 3237 con->http.tls = NULL;
e1d6a774 3238 free(credentials);
3239 return (0);
3240 }
3241
a4845881
MS
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
b9faaae1
MS
3263 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3264 con->http.hostname);
e1d6a774 3265
7cf5915e 3266 con->http.tls_credentials = credentials;
e1d6a774 3267 return (1);
3268
80ca4592 3269# elif defined(HAVE_CDSASSL)
e60ec91f
MS
3270 OSStatus error = 0; /* Error code */
3271 CFArrayRef peerCerts; /* Peer certificates */
e1d6a774 3272
3273
b9faaae1
MS
3274 cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
3275 con->http.fd);
3276
7cf5915e 3277 con->http.tls_credentials = get_cdsa_certificate(con);
e1d6a774 3278
7cf5915e 3279 if (!con->http.tls_credentials)
8ca02f3c 3280 {
3281 /*
3282 * No keychain (yet), make a self-signed certificate...
3283 */
3284
b86bc4cf 3285 if (make_certificate(con))
7cf5915e 3286 con->http.tls_credentials = get_cdsa_certificate(con);
8ca02f3c 3287 }
3288
7cf5915e 3289 if (!con->http.tls_credentials)
e1d6a774 3290 {
3291 cupsdLogMessage(CUPSD_LOG_ERROR,
b9faaae1
MS
3292 "Could not find signing key in keychain \"%s\"",
3293 ServerCertificate);
e1d6a774 3294 error = errSSLBadCert; /* errSSLBadConfiguration is a better choice, but not available on 10.2.x */
3295 }
3296
3297 if (!error)
7cf5915e 3298 error = SSLNewContext(true, &con->http.tls);
e1d6a774 3299
3300 if (!error)
7cf5915e 3301 error = SSLSetIOFuncs(con->http.tls, _httpReadCDSA, _httpWriteCDSA);
89d46774 3302
e1d6a774 3303 if (!error)
7cf5915e 3304 error = SSLSetConnection(con->http.tls, HTTP(con));
e1d6a774 3305
3306 if (!error)
7cf5915e 3307 error = SSLSetAllowsExpiredCerts(con->http.tls, true);
e1d6a774 3308
3309 if (!error)
7cf5915e 3310 error = SSLSetAllowsAnyRoot(con->http.tls, true);
ef416fc2 3311
89d46774 3312 if (!error)
7cf5915e 3313 error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
ef416fc2 3314
e1d6a774 3315 if (!error)
3316 {
3317 /*
3318 * Perform SSL/TLS handshake
3319 */
ef416fc2 3320
7cf5915e 3321 while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
e1d6a774 3322 usleep(1000);
3323 }
ef416fc2 3324
e1d6a774 3325 if (error)
3326 {
3327 cupsdLogMessage(CUPSD_LOG_ERROR,
b9faaae1
MS
3328 "Unable to encrypt connection from %s - %s (%d)",
3329 con->http.hostname, cssmErrorString(error), (int)error);
ef416fc2 3330
e1d6a774 3331 con->http.error = error;
3332 con->http.status = HTTP_ERROR;
ef416fc2 3333
7cf5915e
MS
3334 if (con->http.tls)
3335 {
3336 SSLDisposeContext(con->http.tls);
3337 con->http.tls = NULL;
3338 }
89d46774 3339
7cf5915e
MS
3340 if (con->http.tls_credentials)
3341 {
3342 CFRelease(con->http.tls_credentials);
3343 con->http.tls_credentials = NULL;
3344 }
ef416fc2 3345
e1d6a774 3346 return (0);
ef416fc2 3347 }
3348
b9faaae1
MS
3349 cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
3350 con->http.hostname);
ef416fc2 3351
e60ec91f 3352 if (!SSLCopyPeerCertificates(con->http.tls, &peerCerts) && peerCerts)
7cf5915e
MS
3353 {
3354 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates!",
3355 (int)CFArrayGetCount(peerCerts));
22c9029b 3356 CFRelease(peerCerts);
7cf5915e
MS
3357 }
3358 else
3359 cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates!");
3360
e1d6a774 3361 return (1);
3362
80ca4592 3363# endif /* HAVE_LIBSSL */
ef416fc2 3364}
80ca4592 3365#endif /* HAVE_SSL */
ef416fc2 3366
3367
3368#ifdef HAVE_CDSASSL
3369/*
b86bc4cf 3370 * 'get_cdsa_certificate()' - Get a SSL/TLS certificate from the System keychain.
ef416fc2 3371 */
3372
7cf5915e 3373static CFArrayRef /* O - Array of certificates */
4a4b4f99 3374get_cdsa_certificate(
7cf5915e 3375 cupsd_client_t *con) /* I - Client connection */
ef416fc2 3376{
3377 OSStatus err; /* Error info */
c7017ecc
MS
3378 SecKeychainRef keychain = NULL;/* Keychain reference */
3379 SecIdentitySearchRef search = NULL; /* Search reference */
3380 SecIdentityRef identity = NULL;/* Identity */
b86bc4cf 3381 CFArrayRef certificates = NULL;
3382 /* Certificate array */
c7017ecc
MS
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
ef416fc2 3399
c7017ecc
MS
3400 cupsdLogMessage(CUPSD_LOG_DEBUG,
3401 "get_cdsa_certificate: Looking for certs for \"%s\"...",
3402 con->servername);
ef416fc2 3403
b86bc4cf 3404 if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
3405 {
b9faaae1
MS
3406 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
3407 ServerCertificate, cssmErrorString(err), (int)err);
c7017ecc
MS
3408 goto cleanup;
3409 }
3410
3411# if HAVE_SECPOLICYCREATESSL
3412 servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
3413 kCFStringEncodingUTF8);
3414
e60ec91f
MS
3415 policy = SecPolicyCreateSSL(1, servername);
3416
3417 if (servername)
3418 CFRelease(servername);
3419
3420 if (!policy)
c7017ecc
MS
3421 {
3422 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
3423 goto cleanup;
3424 }
3425
c7017ecc
MS
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);
22c9029b 3456
c7017ecc
MS
3457 CFRelease(policy);
3458
e60ec91f
MS
3459 policy = SecPolicyCreateSSL(1, servername);
3460
3461 if (servername)
3462 CFRelease(servername);
3463
3464 if (!policy)
c7017ecc
MS
3465 {
3466 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
3467 goto cleanup;
3468 }
3469
c7017ecc
MS
3470 CFDictionarySetValue(query, kSecMatchPolicy, policy);
3471
3472 err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
b86bc4cf 3473 }
ef416fc2 3474
c7017ecc
MS
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)
b86bc4cf 3484 /*
7cf5915e 3485 * Use a policy to search for valid certificates whose common name matches the
b86bc4cf 3486 * servername...
3487 */
3488
c7017ecc 3489 if (SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_SSL,
1f0275e3 3490 NULL, &policy_search))
ef416fc2 3491 {
b86bc4cf 3492 cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create a policy search reference");
c7017ecc 3493 goto cleanup;
b86bc4cf 3494 }
ef416fc2 3495
1f0275e3 3496 if (SecPolicySearchCopyNext(policy_search, &policy))
b86bc4cf 3497 {
b9faaae1 3498 cupsdLogMessage(CUPSD_LOG_ERROR,
b86bc4cf 3499 "Cannot find a policy to use for searching");
c7017ecc 3500 goto cleanup;
b86bc4cf 3501 }
ef416fc2 3502
b86bc4cf 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);
ef416fc2 3507
b86bc4cf 3508 options.Data = (uint8 *)&ssl_options;
3509 options.Length = sizeof(ssl_options);
3510
1f0275e3 3511 if (SecPolicySetValue(policy, &options))
b86bc4cf 3512 {
b9faaae1 3513 cupsdLogMessage(CUPSD_LOG_ERROR,
b86bc4cf 3514 "Cannot set policy value to use for searching");
c7017ecc 3515 goto cleanup;
b86bc4cf 3516 }
3517
c7017ecc
MS
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
4a4b4f99
MS
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");
c7017ecc 3549 goto cleanup;
4a4b4f99
MS
3550 }
3551
10d09e33
MS
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
c7017ecc 3563 err = SecIdentitySearchCopyNext(search, &identity);
10d09e33 3564
c7017ecc
MS
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;
4a4b4f99
MS
3573 }
3574
b86bc4cf 3575# else
3576 /*
3577 * Assume there is exactly one SecIdentity in the keychain...
3578 */
3579
c7017ecc
MS
3580 if ((err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_SIGN, &search)))
3581 {
b86bc4cf 3582 cupsdLogMessage(CUPSD_LOG_DEBUG,
c7017ecc
MS
3583 "Cannot create identity search reference (%d)", (int)err);
3584 goto cleanup;
3585 }
3586
3587 if ((err = SecIdentitySearchCopyNext(search, &identity)))
b86bc4cf 3588 {
c7017ecc
MS
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 */
ef416fc2 3595
c7017ecc
MS
3596 if (CFGetTypeID(identity) != SecIdentityGetTypeID())
3597 {
3598 cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure!");
3599 goto cleanup;
3600 }
ef416fc2 3601
c7017ecc
MS
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;
ef416fc2 3607 }
3608
c7017ecc
MS
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);
c7017ecc
MS
3623# elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
3624 if (policy)
3625 CFRelease(policy);
3626 if (policy_search)
3627 CFRelease(policy_search);
3628# endif /* HAVE_SECPOLICYCREATESSL */
b86bc4cf 3629
3630 return (certificates);
ef416fc2 3631}
3632#endif /* HAVE_CDSASSL */
3633
3634
3635/*
3636 * 'get_file()' - Get a filename and state info.
3637 */
3638
3639static char * /* O - Real filename */
3640get_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 */
db1f069b 3648 char language[7]; /* Language subdirectory, if any */
ef416fc2 3649
3650
3651 /*
f7deaa1a 3652 * Figure out the real filename...
ef416fc2 3653 */
3654
db1f069b
MS
3655 language[0] = '\0';
3656
7cf5915e 3657 if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/'))
ef416fc2 3658 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
7cf5915e
MS
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 }
f7deaa1a 3665 else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/'))
3666 snprintf(filename, len, "%s/rss/%s", CacheDir, con->uri + 5);
ef416fc2 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 {
2e4ff8af 3671 if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/')
ef416fc2 3672 strlcpy(filename, AccessLog, len);
2e4ff8af 3673 else if (!strncmp(con->uri + 11, "error_log", 9) && ErrorLog[0] == '/')
ef416fc2 3674 strlcpy(filename, ErrorLog, len);
2e4ff8af 3675 else if (!strncmp(con->uri + 11, "page_log", 8) && PageLog[0] == '/')
ef416fc2 3676 strlcpy(filename, PageLog, len);
3677 else
3678 return (NULL);
3679 }
d6ae789d 3680 else if (con->language)
db1f069b
MS
3681 {
3682 snprintf(language, sizeof(language), "/%s", con->language->language);
3683 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3684 }
ef416fc2 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
db1f069b 3696 if ((status = stat(filename, filestats)) != 0 && language[0] &&
7cf5915e 3697 strncmp(con->uri, "/icons/", 7) &&
d6ae789d 3698 strncmp(con->uri, "/ppd/", 5) &&
7cf5915e 3699 strncmp(con->uri, "/rss/", 5) &&
d6ae789d 3700 strncmp(con->uri, "/admin/conf/", 12) &&
3701 strncmp(con->uri, "/admin/log/", 11))
ef416fc2 3702 {
3703 /*
d6ae789d 3704 * Drop the country code...
ef416fc2 3705 */
3706
db1f069b
MS
3707 language[3] = '\0';
3708 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
d6ae789d 3709
3710 if ((ptr = strchr(filename, '?')) != NULL)
3711 *ptr = '\0';
3712
3713 if ((status = stat(filename, filestats)) != 0)
ef416fc2 3714 {
d6ae789d 3715 /*
3716 * Drop the language prefix and try the root directory...
3717 */
3718
db1f069b 3719 language[0] = '\0';
ef416fc2 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 {
db1f069b
MS
3735 /*
3736 * Make sure the URI ends with a slash...
3737 */
ef416fc2 3738
db1f069b
MS
3739 if (con->uri[strlen(con->uri) - 1] != '/')
3740 strlcat(con->uri, "/", sizeof(con->uri));
ef416fc2 3741
db1f069b
MS
3742 /*
3743 * Find the directory index file, trying every language...
3744 */
ef416fc2 3745
db1f069b 3746 do
ef416fc2 3747 {
db1f069b
MS
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);
ef416fc2 3773 status = stat(filename, filestats);
db1f069b
MS
3774
3775#ifdef HAVE_JAVA
3776 if (status)
3777 {
3778 strlcpy(ptr, "index.class", plen);
3779 status = stat(filename, filestats);
3780 }
ef416fc2 3781#endif /* HAVE_JAVA */
3782
3783#ifdef HAVE_PERL
db1f069b
MS
3784 if (status)
3785 {
3786 strlcpy(ptr, "index.pl", plen);
3787 status = stat(filename, filestats);
3788 }
ef416fc2 3789#endif /* HAVE_PERL */
3790
3791#ifdef HAVE_PHP
db1f069b
MS
3792 if (status)
3793 {
3794 strlcpy(ptr, "index.php", plen);
3795 status = stat(filename, filestats);
3796 }
ef416fc2 3797#endif /* HAVE_PHP */
3798
3799#ifdef HAVE_PYTHON
db1f069b
MS
3800 if (status)
3801 {
3802 strlcpy(ptr, "index.pyc", plen);
3803 status = stat(filename, filestats);
3804 }
ef416fc2 3805
db1f069b
MS
3806 if (status)
3807 {
3808 strlcpy(ptr, "index.py", plen);
3809 status = stat(filename, filestats);
3810 }
ef416fc2 3811#endif /* HAVE_PYTHON */
db1f069b
MS
3812
3813 }
3814 while (status && language[0]);
ef416fc2 3815 }
3816
b9faaae1
MS
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);
ef416fc2 3821
ef416fc2 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
3833static http_status_t /* O - Status */
3834install_conf_file(cupsd_client_t *con) /* I - Connection */
3835{
321d8d57
MS
3836 char filename[1024]; /* Configuration filename */
3837 mode_t mode; /* Permissions */
ef416fc2 3838 cups_file_t *in, /* Input file */
3839 *out; /* Output file */
321d8d57
MS
3840 char buffer[16384]; /* Copy buffer */
3841 ssize_t bytes; /* Number of bytes */
ef416fc2 3842
3843
3844 /*
321d8d57 3845 * Open the request file...
ef416fc2 3846 */
3847
321d8d57 3848 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
ef416fc2 3849 {
321d8d57
MS
3850 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open request file \"%s\": %s",
3851 con->filename, strerror(errno));
3852 return (HTTP_SERVER_ERROR);
ef416fc2 3853 }
3854
3855 /*
321d8d57 3856 * Open the new config file...
ef416fc2 3857 */
3858
321d8d57
MS
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;
ef416fc2 3864
321d8d57 3865 if ((out = cupsdCreateConfFile(filename, mode)) == NULL)
ef416fc2 3866 {
3867 cupsFileClose(in);
ef416fc2 3868 return (HTTP_SERVER_ERROR);
3869 }
3870
321d8d57 3871 cupsdLogMessage(CUPSD_LOG_INFO, "Installing config file \"%s\"...", filename);
ef416fc2 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,
321d8d57
MS
3881 "Unable to copy to config file \"%s\": %s",
3882 filename, strerror(errno));
ef416fc2 3883
3884 cupsFileClose(in);
3885 cupsFileClose(out);
321d8d57
MS
3886
3887 snprintf(filename, sizeof(filename), "%s%s.N", ServerRoot, con->uri + 11);
3888 cupsdRemoveFile(filename);
ef416fc2 3889
3890 return (HTTP_SERVER_ERROR);
3891 }
3892
3893 /*
3894 * Close the files...
3895 */
3896
3897 cupsFileClose(in);
ef416fc2 3898
321d8d57 3899 if (cupsdCloseCreatedConfFile(out, filename))
ef416fc2 3900 return (HTTP_SERVER_ERROR);
ef416fc2 3901
3902 /*
3903 * Remove the request file...
3904 */
3905
321d8d57 3906 cupsdRemoveFile(con->filename);
ef416fc2 3907 cupsdClearString(&con->filename);
3908
ef416fc2 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
e1d6a774 3928/*
3929 * 'is_cgi()' - Is the resource a CGI script/program?
3930 */
3931
3932static int /* O - 1 = CGI, 0 = file */
3933is_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
e1d6a774 3941 /*
3942 * Get the options, if any...
3943 */
3944
3945 if ((options = strchr(con->uri, '?')) != NULL)
a0f6818e
MS
3946 {
3947 options ++;
3948 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", options);
3949 }
b86bc4cf 3950
e1d6a774 3951 /*
3952 * Check for known types...
3953 */
3954
88f9aafc 3955 if (!type || _cups_strcasecmp(type->super, "application"))
e1d6a774 3956 {
b9faaae1
MS
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");
e1d6a774 3962 return (0);
3963 }
3964
88f9aafc 3965 if (!_cups_strcasecmp(type->type, "x-httpd-cgi") &&
e1d6a774 3966 (filestats->st_mode & 0111))
3967 {
3968 /*
3969 * "application/x-httpd-cgi" is a CGI script.
3970 */
3971
3972 cupsdSetString(&con->command, filename);
3973
a0f6818e
MS
3974 if (options)
3975 cupsdSetStringf(&con->options, " %s", options);
e1d6a774 3976
3977 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1
MS
3978 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
3979 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
321d8d57 3980 type->super, type->type);
e1d6a774 3981 return (1);
3982 }
3983#ifdef HAVE_JAVA
88f9aafc 3984 else if (!_cups_strcasecmp(type->type, "x-httpd-java"))
e1d6a774 3985 {
3986 /*
3987 * "application/x-httpd-java" is a Java servlet.
3988 */
3989
3990 cupsdSetString(&con->command, CUPS_JAVA);
3991
3992 if (options)
b94498cf 3993 cupsdSetStringf(&con->options, " %s %s", filename, options);
e1d6a774 3994 else
b94498cf 3995 cupsdSetStringf(&con->options, " %s", filename);
e1d6a774 3996
3997 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1
MS
3998 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
3999 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
321d8d57 4000 type->super, type->type);
e1d6a774 4001 return (1);
4002 }
4003#endif /* HAVE_JAVA */
4004#ifdef HAVE_PERL
88f9aafc 4005 else if (!_cups_strcasecmp(type->type, "x-httpd-perl"))
e1d6a774 4006 {
4007 /*
4008 * "application/x-httpd-perl" is a Perl page.
4009 */
4010
4011 cupsdSetString(&con->command, CUPS_PERL);
4012
4013 if (options)
b94498cf 4014 cupsdSetStringf(&con->options, " %s %s", filename, options);
e1d6a774 4015 else
b94498cf 4016 cupsdSetStringf(&con->options, " %s", filename);
e1d6a774 4017
4018 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1
MS
4019 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4020 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
321d8d57 4021 type->super, type->type);
e1d6a774 4022 return (1);
4023 }
4024#endif /* HAVE_PERL */
4025#ifdef HAVE_PHP
88f9aafc 4026 else if (!_cups_strcasecmp(type->type, "x-httpd-php"))
e1d6a774 4027 {
4028 /*
4029 * "application/x-httpd-php" is a PHP page.
4030 */
4031
4032 cupsdSetString(&con->command, CUPS_PHP);
4033
4034 if (options)
b94498cf 4035 cupsdSetStringf(&con->options, " %s %s", filename, options);
e1d6a774 4036 else
b94498cf 4037 cupsdSetStringf(&con->options, " %s", filename);
e1d6a774 4038
4039 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1
MS
4040 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4041 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
321d8d57 4042 type->super, type->type);
e1d6a774 4043 return (1);
4044 }
4045#endif /* HAVE_PHP */
4046#ifdef HAVE_PYTHON
88f9aafc 4047 else if (!_cups_strcasecmp(type->type, "x-httpd-python"))
e1d6a774 4048 {
4049 /*
4050 * "application/x-httpd-python" is a Python page.
4051 */
4052
4053 cupsdSetString(&con->command, CUPS_PYTHON);
4054
4055 if (options)
b94498cf 4056 cupsdSetStringf(&con->options, " %s %s", filename, options);
e1d6a774 4057 else
b94498cf 4058 cupsdSetStringf(&con->options, " %s", filename);
e1d6a774 4059
4060 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1
MS
4061 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
4062 "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
321d8d57 4063 type->super, type->type);
e1d6a774 4064 return (1);
4065 }
4066#endif /* HAVE_PYTHON */
4067
b9faaae1
MS
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,
321d8d57 4071 type->super, type->type);
e1d6a774 4072 return (0);
4073}
4074
4075
ef416fc2 4076/*
4077 * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. "..").
4078 */
4079
4080static int /* O - 0 if relative, 1 if absolute */
4081is_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
8ca02f3c 4111#ifdef HAVE_SSL
4744bd90 4112/*
4113 * 'make_certificate()' - Make a self-signed SSL/TLS certificate.
4114 */
4115
8ca02f3c 4116static int /* O - 1 on success, 0 on failure */
b86bc4cf 4117make_certificate(cupsd_client_t *con) /* I - Client connection */
4744bd90 4118{
8ca02f3c 4119#if defined(HAVE_LIBSSL) && defined(HAVE_WAITPID)
d09495fa 4120 int pid, /* Process ID of command */
4121 status; /* Status of command */
4122 char command[1024], /* Command */
f7deaa1a 4123 *argv[12], /* Command-line arguments */
d09495fa 4124 *envp[MAX_ENV + 1], /* Environment variables */
d09495fa 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 */
8ca02f3c 4131
4132
4133 /*
d09495fa 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
8ca02f3c 4138 *
4139 * openssl req -new -x509 -keyout ServerKey \
4140 * -out ServerCertificate -days 3650 -nodes
d09495fa 4141 *
4142 * The seeding step is crucial in ensuring that the openssl command
4143 * does not block on systems without sufficient entropy...
8ca02f3c 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
d09495fa 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
d09495fa 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);
acb056cb 4193 envp[envc] = NULL;
d09495fa 4194
f11a948a 4195 if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, NULL,
38e73f87 4196 NULL, &pid))
d09495fa 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
b9faaae1 4209 cupsdFinishProcess(pid, command, sizeof(command), NULL);
d09495fa 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
8ca02f3c 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
d09495fa 4271 infofd = open(infofile, O_RDONLY);
4272
a4924f6c 4273 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
38e73f87 4274 NULL, &pid))
d09495fa 4275 {
4276 close(infofd);
4277 unlink(infofile);
8ca02f3c 4278 return (0);
d09495fa 4279 }
4280
4281 close(infofd);
4282 unlink(infofile);
8ca02f3c 4283
4284 while (waitpid(pid, &status, 0) < 0)
4285 if (errno != EINTR)
4286 {
4287 status = 1;
4288 break;
4289 }
4290
b9faaae1 4291 cupsdFinishProcess(pid, command, sizeof(command), NULL);
8ca02f3c 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)
4744bd90 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
8ca02f3c 4333 cupsdLogMessage(CUPSD_LOG_INFO, "Generating SSL server key...");
4744bd90 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 {
8ca02f3c 4347 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to export SSL server key - %s",
4744bd90 4348 gnutls_strerror(result));
4349 gnutls_x509_privkey_deinit(key);
8ca02f3c 4350 return (0);
4744bd90 4351 }
4352 else if ((fp = cupsFileOpen(ServerKey, "w")) != NULL)
4353 {
4354 cupsFileWrite(fp, (char *)buffer, bytes);
4355 cupsFileClose(fp);
4356
8ca02f3c 4357 cupsdLogMessage(CUPSD_LOG_INFO, "Created SSL server key file \"%s\"...",
4744bd90 4358 ServerKey);
4359 }
4360 else
4361 {
4362 cupsdLogMessage(CUPSD_LOG_ERROR,
8ca02f3c 4363 "Unable to create SSL server key file \"%s\" - %s",
4744bd90 4364 ServerKey, strerror(errno));
4365 gnutls_x509_privkey_deinit(key);
8ca02f3c 4366 return (0);
4744bd90 4367 }
4368
4369 /*
4370 * Create the self-signed certificate...
4371 */
4372
8ca02f3c 4373 cupsdLogMessage(CUPSD_LOG_INFO, "Generating self-signed SSL certificate...");
4744bd90 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,
8ca02f3c 4426 "Unable to export SSL server certificate - %s",
4744bd90 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,
8ca02f3c 4434 "Created SSL server certificate file \"%s\"...",
4744bd90 4435 ServerCertificate);
4436 }
4437 else
4438 cupsdLogMessage(CUPSD_LOG_ERROR,
8ca02f3c 4439 "Unable to create SSL server certificate file \"%s\" - %s",
4744bd90 4440 ServerCertificate, strerror(errno));
4441
4442 /*
4443 * Cleanup...
4444 */
4445
4446 gnutls_x509_crt_deinit(crt);
4447 gnutls_x509_privkey_deinit(key);
8ca02f3c 4448
4449 return (1);
4450
4451#elif defined(HAVE_CDSASSL) && defined(HAVE_WAITPID)
b86bc4cf 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 */
10d09e33
MS
4458 infofile[1024], /* Type-in information for cert */
4459 localname[1024], /* Local hostname */
4460 *servername; /* Name of server in cert */
b86bc4cf 4461 cups_file_t *fp; /* Seed/info file */
4462 int infofd; /* Info file descriptor */
8ca02f3c 4463
4464
10d09e33
MS
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;
22c9029b 4472
8ca02f3c 4473 /*
b86bc4cf 4474 * Run the "certtool" command to generate a self-signed certificate...
8ca02f3c 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
b86bc4cf 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
7cf5915e 4499 cupsFilePrintf(fp, "%s\nr\n\ny\nb\ns\ny\n%s\n\n\n\n\n%s\ny\n",
10d09e33 4500 servername, servername, ServerAdmin);
b86bc4cf 4501 cupsFileClose(fp);
4502
8ca02f3c 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";
b86bc4cf 4510 argv[2] = keychain;
4511 argv[3] = NULL;
8ca02f3c 4512
4513 cupsdLoadEnv(envp, MAX_ENV);
4514
b86bc4cf 4515 infofd = open(infofile, O_RDONLY);
4516
a4924f6c 4517 if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
38e73f87 4518 NULL, &pid))
b86bc4cf 4519 {
4520 close(infofd);
4521 unlink(infofile);
8ca02f3c 4522 return (0);
b86bc4cf 4523 }
4524
4525 close(infofd);
4526 unlink(infofile);
8ca02f3c 4527
4528 while (waitpid(pid, &status, 0) < 0)
4529 if (errno != EINTR)
4530 {
4531 status = 1;
4532 break;
4533 }
4534
b9faaae1 4535 cupsdFinishProcess(pid, command, sizeof(command), NULL);
8ca02f3c 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 */
4744bd90 4562}
8ca02f3c 4563#endif /* HAVE_SSL */
4744bd90 4564
4565
ef416fc2 4566/*
4567 * 'pipe_command()' - Pipe the output of a command to the remote client.
4568 */
4569
4570static int /* O - Process ID */
4571pipe_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 */
b423cd4c 4580 char *commptr, /* Command string pointer */
4581 commch; /* Command string character */
ef416fc2 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 */
b94498cf 4588 *envp[MAX_ENV + 20]; /* Environment variables */
c7017ecc 4589 char auth_type[256], /* AUTH_TYPE environment variable */
b94498cf 4590 content_length[1024], /* CONTENT_LENGTH environment variable */
ef416fc2 4591 content_type[1024], /* CONTENT_TYPE environment variable */
4592 http_cookie[32768], /* HTTP_COOKIE environment variable */
f7deaa1a 4593 http_referer[1024], /* HTTP_REFERER environment variable */
ef416fc2 4594 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
4595 lang[1024], /* LANG environment variable */
b423cd4c 4596 path_info[1024], /* PATH_INFO environment variable */
ef416fc2 4597 remote_addr[1024], /* REMOTE_ADDR environment variable */
4598 remote_host[1024], /* REMOTE_HOST environment variable */
4599 remote_user[1024], /* REMOTE_USER environment variable */
b94498cf 4600 script_filename[1024], /* SCRIPT_FILENAME environment variable */
ef416fc2 4601 script_name[1024], /* SCRIPT_NAME environment variable */
4602 server_name[1024], /* SERVER_NAME environment variable */
4603 server_port[1024]; /* SERVER_PORT environment variable */
7dfedb92 4604 ipp_attribute_t *attr; /* attributes-natural-language attribute */
e07d4801 4605 void *ccache = NULL; /* Kerberos credentials */
ef416fc2 4606
4607
4608 /*
4609 * Parse a copy of the options string, which is of the form:
4610 *
b423cd4c 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
ef416fc2 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,
b9faaae1
MS
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);
ef416fc2 4631
b86bc4cf 4632 argv[0] = command;
ef416fc2 4633
b423cd4c 4634 if (options)
c7017ecc
MS
4635 {
4636 commptr = options;
4637 if (*commptr == ' ')
4638 commptr ++;
4639 strlcpy(argbuf, commptr, sizeof(argbuf));
4640 }
b423cd4c 4641 else
4642 argbuf[0] = '\0';
4643
4644 if (argbuf[0] == '/')
ef416fc2 4645 {
4646 /*
b423cd4c 4647 * Found some trailing path information, set PATH_INFO...
ef416fc2 4648 */
4649
b423cd4c 4650 if ((commptr = strchr(argbuf, '?')) == NULL)
4651 commptr = argbuf + strlen(argbuf);
ef416fc2 4652
b423cd4c 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';
b94498cf 4662
4663 if (*commptr == ' ')
4664 commptr ++;
b423cd4c 4665 }
ef416fc2 4666
b86bc4cf 4667 if (*commptr == '?' && con->operation == HTTP_GET && !con->query_string)
b423cd4c 4668 {
4669 commptr ++;
b86bc4cf 4670 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", commptr);
b423cd4c 4671 }
ef416fc2 4672
b423cd4c 4673 argc = 1;
ef416fc2 4674
b423cd4c 4675 if (*commptr)
4676 {
4677 argv[argc ++] = commptr;
ef416fc2 4678
b423cd4c 4679 for (; *commptr && argc < 99; commptr ++)
4680 {
ef416fc2 4681 /*
b423cd4c 4682 * Break arguments whenever we see a + or space...
ef416fc2 4683 */
4684
b423cd4c 4685 if (*commptr == ' ' || *commptr == '+')
4686 {
4687 while (*commptr == ' ' || *commptr == '+')
4688 *commptr++ = '\0';
ef416fc2 4689
b423cd4c 4690 /*
4691 * If we don't have a blank string, save it as another argument...
4692 */
ef416fc2 4693
b423cd4c 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 */
ef416fc2 4708
b423cd4c 4709 if (commptr[1] >= '0' && commptr[1] <= '9')
4710 *commptr = (commptr[1] - '0') << 4;
4711 else
4712 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
ef416fc2 4713
b423cd4c 4714 if (commptr[2] >= '0' && commptr[2] <= '9')
4715 *commptr |= commptr[2] - '0';
4716 else
4717 *commptr |= tolower(commptr[2]) - 'a' + 10;
ef416fc2 4718
b423cd4c 4719 _cups_strcpy(commptr + 1, commptr + 3);
ef416fc2 4720
b423cd4c 4721 /*
4722 * Check for a %00 and break if that is the case...
4723 */
ef416fc2 4724
b423cd4c 4725 if (!*commptr)
4726 break;
4727 }
ef416fc2 4728 }
4729 }
4730
4731 argv[argc] = NULL;
4732
ef416fc2 4733 /*
4734 * Setup the environment variables as needed...
4735 */
4736
b94498cf 4737 if (con->username[0])
4738 {
c7017ecc 4739 snprintf(auth_type, sizeof(auth_type), "AUTH_TYPE=%s",
b94498cf 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
7dfedb92
MS
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);
ef416fc2 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
b94498cf 4801 snprintf(script_filename, sizeof(script_filename), "SCRIPT_FILENAME=%s%s",
4802 DocumentRoot, script_name + 12);
4803
ef416fc2 4804 sprintf(server_port, "SERVER_PORT=%d", con->serverport);
4805
0268488e
MS
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);
ef416fc2 4818
4819 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
4820
b94498cf 4821 if (auth_type[0])
4822 envp[envc ++] = auth_type;
4823
ef416fc2 4824 envp[envc ++] = lang;
4825 envp[envc ++] = "REDIRECT_STATUS=1";
b94498cf 4826 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
ef416fc2 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;
b94498cf 4832 envp[envc ++] = script_filename;
ef416fc2 4833
b423cd4c 4834 if (path_info[0])
4835 envp[envc ++] = path_info;
4836
ef416fc2 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
f7deaa1a 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
ef416fc2 4872 if (con->operation == HTTP_GET)
4873 {
ef416fc2 4874 envp[envc ++] = "REQUEST_METHOD=GET";
4875
b86bc4cf 4876 if (con->query_string)
ef416fc2 4877 {
4878 /*
4879 * Add GET form variables after ?...
4880 */
4881
b86bc4cf 4882 envp[envc ++] = con->query_string;
ef416fc2 4883 }
3dfe78b3
MS
4884 else
4885 envp[envc ++] = "QUERY_STRING=";
ef416fc2 4886 }
4887 else
4888 {
e1d6a774 4889 sprintf(content_length, "CONTENT_LENGTH=" CUPS_LLFMT,
4890 CUPS_LLCAST con->bytes);
ef416fc2 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
a74454a7 4903 if (con->http.tls)
ef416fc2 4904 envp[envc ++] = "HTTPS=ON";
4905
4906 /*
4907 * Terminate the environment array...
4908 */
4909
4910 envp[envc] = NULL;
4911
38e73f87 4912 if (LogLevel >= CUPSD_LOG_DEBUG)
ef416fc2 4913 {
4914 for (i = 0; i < argc; i ++)
38e73f87
MS
4915 cupsdLogMessage(CUPSD_LOG_DEBUG,
4916 "[CGI] argv[%d] = \"%s\"", i, argv[i]);
ef416fc2 4917 for (i = 0; i < envc; i ++)
38e73f87
MS
4918 cupsdLogMessage(CUPSD_LOG_DEBUG,
4919 "[CGI] envp[%d] = \"%s\"", i, envp[i]);
ef416fc2 4920 }
4921
4922 /*
4923 * Create a pipe for the output...
4924 */
4925
4926 if (cupsdOpenPipe(fds))
4927 {
38e73f87 4928 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to create pipe for %s - %s",
ef416fc2 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],
38e73f87 4938 -1, -1, root, DefaultProfile, NULL, &pid) < 0)
ef416fc2 4939 {
4940 /*
4941 * Error - can't fork!
4942 */
4943
38e73f87 4944 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to start %s - %s", argv[0],
ef416fc2 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])
5bd77a73 4957 cupsdAddCert(pid, con->username, ccache);
ef416fc2 4958
38e73f87 4959 cupsdLogMessage(CUPSD_LOG_DEBUG, "[CGI] Started %s (PID %d)", command, pid);
ef416fc2 4960
4961 *outfile = fds[0];
4962 close(fds[1]);
4963 }
4964
ef416fc2 4965 return (pid);
4966}
4967
4968
e07d4801
MS
4969/*
4970 * 'valid_host()' - Is the Host: field valid?
4971 */
4972
4973static int /* O - 1 if valid, 0 if not */
4974valid_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
88f9aafc
MS
4991 return (!_cups_strcasecmp(host, "localhost") ||
4992 !_cups_strncasecmp(host, "localhost:", 10) ||
4993 !_cups_strcasecmp(host, "localhost.") ||
4994 !_cups_strncasecmp(host, "localhost.:", 11) ||
e07d4801 4995#ifdef __linux
88f9aafc
MS
4996 !_cups_strcasecmp(host, "localhost.localdomain") ||
4997 !_cups_strncasecmp(host, "localhost.localdomain:", 22) ||
e07d4801
MS
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 &&
88f9aafc
MS
5011 (!_cups_strcasecmp(end, ".local") || !_cups_strncasecmp(end, ".local:", 7) ||
5012 !_cups_strcasecmp(end, ".local.") || !_cups_strncasecmp(end, ".local.:", 8)))
e07d4801
MS
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
88f9aafc 5061 if (!_cups_strncasecmp(host, a->name, a->namelen))
e07d4801
MS
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
88f9aafc 5087 if (!_cups_strncasecmp(host, a->name, a->namelen))
e07d4801
MS
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 {
88f9aafc 5110 if (!_cups_strncasecmp(host, netif->hostname, netif->hostlen))
e07d4801
MS
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
ef416fc2 5128/*
a74454a7 5129 * 'write_file()' - Send a file via HTTP.
e1d6a774 5130 */
5131
5132static int /* O - 0 on failure, 1 on success */
a74454a7 5133write_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 */
e1d6a774 5138{
5139 con->file = open(filename, O_RDONLY);
5140
b9faaae1
MS
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);
e1d6a774 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
5bd77a73 5153 if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
e1d6a774 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
07725fee 5165 if (cupsdFlushHeader(con) < 0)
5166 return (0);
d09495fa 5167
e1d6a774 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
f7deaa1a 5176 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
5177 (cupsd_selfunc_t)cupsdWriteClient, con);
e1d6a774 5178
5179 return (1);
5180}
5181
5182
5183/*
f899b121 5184 * 'write_pipe()' - Flag that data is available on the CGI pipe.
5185 */
5186
5187static void
5188write_pipe(cupsd_client_t *con) /* I - Client connection */
5189{
b9faaae1
MS
5190 cupsdLogMessage(CUPSD_LOG_DEBUG2,
5191 "write_pipe(con=%p(%d)) CGI output on fd %d",
5192 con, con->http.fd, con->file);
f899b121 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/*
b19ccc9e 5202 * End of "$Id: client.c 7950 2008-09-17 00:21:59Z mike $".
ef416fc2 5203 */