]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/client.c
Merge changes from CUPS 1.6svn-r10510.
[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 *
82cc1f9a 6 * Copyright 2007-2012 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 *
82cc1f9a
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 * get_file() - Get a filename and state info.
35 * install_conf_file() - Install a configuration file.
36 * is_cgi() - Is the resource a CGI script/program?
37 * is_path_absolute() - Is a path absolute and free of relative elements
38 * (i.e. "..").
39 * pipe_command() - Pipe the output of a command to the remote
40 * client.
41 * valid_host() - Is the Host: field valid?
42 * write_file() - Send a file via HTTP.
43 * write_pipe() - Flag that data is available on the CGI pipe.
ef416fc2 44 */
45
46/*
47 * Include necessary headers...
48 */
49
ef416fc2 50#include "cupsd.h"
51
749b1e90
MS
52#ifdef HAVE_TCPD_H
53# include <tcpd.h>
54#endif /* HAVE_TCPD_H */
55
ef416fc2 56
85dda01c
MS
57/*
58 * Local globals...
59 */
60
61static const char * const http_states[] =
62 { /* HTTP state strings */
63 "HTTP_WAITING",
64 "HTTP_OPTIONS",
65 "HTTP_GET",
66 "HTTP_GET_SEND",
67 "HTTP_HEAD",
68 "HTTP_POST",
69 "HTTP_POST_RECV",
70 "HTTP_POST_SEND",
71 "HTTP_PUT",
72 "HTTP_PUT_RECV",
73 "HTTP_DELETE",
74 "HTTP_TRACE",
75 "HTTP_CLOSE",
76 "HTTP_STATUS"
77 };
78static const char * const ipp_states[] =
79 { /* IPP state strings */
80 "IPP_IDLE",
81 "IPP_HEADER",
82 "IPP_ATTRIBUTE",
83 "IPP_DATA"
84 };
85
86
ef416fc2 87/*
88 * Local functions...
89 */
90
91static int check_if_modified(cupsd_client_t *con,
92 struct stat *filestats);
3dfe78b3
MS
93static int compare_clients(cupsd_client_t *a, cupsd_client_t *b,
94 void *data);
ee571f26 95static int data_ready(cupsd_client_t *con);
f7deaa1a 96static char *get_file(cupsd_client_t *con, struct stat *filestats,
ef416fc2 97 char *filename, int len);
98static http_status_t install_conf_file(cupsd_client_t *con);
e1d6a774 99static int is_cgi(cupsd_client_t *con, const char *filename,
100 struct stat *filestats, mime_type_t *type);
ef416fc2 101static int is_path_absolute(const char *path);
102static int pipe_command(cupsd_client_t *con, int infile, int *outfile,
103 char *command, char *options, int root);
e07d4801 104static int valid_host(cupsd_client_t *con);
a74454a7 105static int write_file(cupsd_client_t *con, http_status_t code,
106 char *filename, char *type,
107 struct stat *filestats);
f899b121 108static void write_pipe(cupsd_client_t *con);
ef416fc2 109
110
111/*
112 * 'cupsdAcceptClient()' - Accept a new client.
113 */
114
115void
116cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
117{
ef416fc2 118 int count; /* Count of connections on a host */
119 int val; /* Parameter value */
bd7854cb 120 cupsd_client_t *con, /* New client pointer */
121 *tempcon; /* Temporary client pointer */
ef416fc2 122 http_addrlist_t *addrlist, /* List of adddresses for host */
123 *addr; /* Current address */
124 socklen_t addrlen; /* Length of address */
125 char *hostname; /* Hostname for address */
126 http_addr_t temp; /* Temporary address variable */
127 static time_t last_dos = 0; /* Time of last DoS attack */
749b1e90
MS
128#ifdef HAVE_TCPD_H
129 struct request_info wrap_req; /* TCP wrappers request information */
130#endif /* HAVE_TCPD_H */
ef416fc2 131
132
133 cupsdLogMessage(CUPSD_LOG_DEBUG2,
b9faaae1 134 "cupsdAcceptClient(lis=%p(%d)) Clients=%d",
bd7854cb 135 lis, lis->fd, cupsArrayCount(Clients));
ef416fc2 136
137 /*
138 * Make sure we don't have a full set of clients already...
139 */
140
bd7854cb 141 if (cupsArrayCount(Clients) == MaxClients)
ef416fc2 142 return;
143
144 /*
145 * Get a pointer to the next available client...
146 */
147
bd7854cb 148 if (!Clients)
149 Clients = cupsArrayNew(NULL, NULL);
150
151 if (!Clients)
91c84a35
MS
152 {
153 cupsdLogMessage(CUPSD_LOG_ERROR,
3dfe78b3
MS
154 "Unable to allocate memory for clients array!");
155 cupsdPauseListening();
156 return;
157 }
158
159 if (!ActiveClients)
160 ActiveClients = cupsArrayNew((cups_array_func_t)compare_clients, NULL);
161
162 if (!ActiveClients)
163 {
164 cupsdLogMessage(CUPSD_LOG_ERROR,
165 "Unable to allocate memory for active clients array!");
91c84a35 166 cupsdPauseListening();
bd7854cb 167 return;
91c84a35 168 }
bd7854cb 169
91c84a35
MS
170 if ((con = calloc(1, sizeof(cupsd_client_t))) == NULL)
171 {
172 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate memory for client!");
173 cupsdPauseListening();
174 return;
175 }
ef416fc2 176
85dda01c 177 con->file = -1;
dcb445bc 178 con->http.activity = time(NULL);
85dda01c
MS
179 con->http.hostaddr = &(con->clientaddr);
180 con->http.wait_value = 10000;
ef416fc2 181
182 /*
183 * Accept the client and get the remote address...
184 */
185
186 addrlen = sizeof(http_addr_t);
187
188 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)con->http.hostaddr,
189 &addrlen)) < 0)
190 {
76cd9e37
MS
191 if (errno == ENFILE || errno == EMFILE)
192 cupsdPauseListening();
193
ef416fc2 194 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to accept client connection - %s.",
195 strerror(errno));
bd7854cb 196 free(con);
76cd9e37 197
ef416fc2 198 return;
199 }
200
22c9029b
MS
201 /*
202 * Save the connected port number...
203 */
ef416fc2 204
22c9029b 205 _httpAddrSetPort(con->http.hostaddr, _httpAddrPort(&(lis->address)));
ef416fc2 206
22c9029b
MS
207#ifdef AF_INET6
208 /*
209 * Convert IPv4 over IPv6 addresses (::ffff:n.n.n.n) to IPv4 forms we
210 * can more easily use...
211 */
ef416fc2 212
22c9029b
MS
213 if (lis->address.addr.sa_family == AF_INET6 &&
214 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[0] == 0 &&
215 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[1] == 0 &&
216 ntohl(con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2]) == 0xffff)
217 con->http.hostaddr->ipv6.sin6_addr.s6_addr32[2] = 0;
ef416fc2 218#endif /* AF_INET6 */
ef416fc2 219
220 /*
221 * Check the number of clients on the same address...
222 */
223
bd7854cb 224 for (count = 0, tempcon = (cupsd_client_t *)cupsArrayFirst(Clients);
225 tempcon;
226 tempcon = (cupsd_client_t *)cupsArrayNext(Clients))
227 if (httpAddrEqual(tempcon->http.hostaddr, con->http.hostaddr))
ef416fc2 228 {
229 count ++;
230 if (count >= MaxClientsPerHost)
231 break;
232 }
233
234 if (count >= MaxClientsPerHost)
235 {
236 if ((time(NULL) - last_dos) >= 60)
237 {
238 last_dos = time(NULL);
239 cupsdLogMessage(CUPSD_LOG_WARN,
bd7854cb 240 "Possible DoS attack - more than %d clients connecting "
241 "from %s!",
749b1e90
MS
242 MaxClientsPerHost,
243 httpAddrString(con->http.hostaddr, con->http.hostname,
244 sizeof(con->http.hostname)));
ef416fc2 245 }
246
247#ifdef WIN32
248 closesocket(con->http.fd);
249#else
250 close(con->http.fd);
251#endif /* WIN32 */
252
bd7854cb 253 free(con);
ef416fc2 254 return;
255 }
256
257 /*
258 * Get the hostname or format the IP address as needed...
259 */
260
261 if (httpAddrLocalhost(con->http.hostaddr))
262 {
263 /*
264 * Map accesses from the loopback interface to "localhost"...
265 */
266
267 strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname));
268 hostname = con->http.hostname;
269 }
270 else
271 {
272 /*
273 * Map accesses from the same host to the server name.
274 */
275
d1c13e16 276 if (HostNameLookups)
ef416fc2 277 hostname = httpAddrLookup(con->http.hostaddr, con->http.hostname,
278 sizeof(con->http.hostname));
279 else
280 {
281 hostname = NULL;
282 httpAddrString(con->http.hostaddr, con->http.hostname,
283 sizeof(con->http.hostname));
284 }
285 }
286
287 if (hostname == NULL && HostNameLookups == 2)
288 {
289 /*
290 * Can't have an unresolved IP address with double-lookups enabled...
291 */
292
ef416fc2 293#ifdef WIN32
294 closesocket(con->http.fd);
295#else
296 close(con->http.fd);
297#endif /* WIN32 */
298
299 cupsdLogMessage(CUPSD_LOG_WARN,
300 "Name lookup failed - connection from %s closed!",
301 con->http.hostname);
bd7854cb 302
303 free(con);
ef416fc2 304 return;
305 }
306
307 if (HostNameLookups == 2)
308 {
309 /*
310 * Do double lookups as needed...
311 */
312
749b1e90
MS
313 if ((addrlist = httpAddrGetList(con->http.hostname, AF_UNSPEC, NULL))
314 != NULL)
ef416fc2 315 {
316 /*
317 * See if the hostname maps to the same IP address...
318 */
319
320 for (addr = addrlist; addr; addr = addr->next)
321 if (httpAddrEqual(con->http.hostaddr, &(addr->addr)))
322 break;
323 }
324 else
325 addr = NULL;
326
327 httpAddrFreeList(addrlist);
328
329 if (!addr)
330 {
331 /*
332 * Can't have a hostname that doesn't resolve to the same IP address
333 * with double-lookups enabled...
334 */
335
ef416fc2 336#ifdef WIN32
337 closesocket(con->http.fd);
338#else
339 close(con->http.fd);
340#endif /* WIN32 */
341
342 cupsdLogMessage(CUPSD_LOG_WARN,
343 "IP lookup failed - connection from %s closed!",
344 con->http.hostname);
bd7854cb 345 free(con);
ef416fc2 346 return;
347 }
348 }
349
749b1e90
MS
350#ifdef HAVE_TCPD_H
351 /*
352 * See if the connection is denied by TCP wrappers...
353 */
354
355 request_init(&wrap_req, RQ_DAEMON, "cupsd", RQ_FILE, con->http.fd, NULL);
356 fromhost(&wrap_req);
357
358 if (!hosts_access(&wrap_req))
359 {
749b1e90
MS
360#ifdef WIN32
361 closesocket(con->http.fd);
362#else
363 close(con->http.fd);
364#endif /* WIN32 */
365
366 cupsdLogMessage(CUPSD_LOG_WARN,
367 "Connection from %s refused by /etc/hosts.allow and "
368 "/etc/hosts.deny rules.", con->http.hostname);
369 free(con);
370 return;
371 }
372#endif /* HAVE_TCPD_H */
373
ef416fc2 374#ifdef AF_LOCAL
375 if (con->http.hostaddr->addr.sa_family == AF_LOCAL)
85dda01c 376 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Accepted from %s (Domain)",
ef416fc2 377 con->http.fd, con->http.hostname);
378 else
379#endif /* AF_LOCAL */
85dda01c 380 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Accepted from %s:%d (IPv%d)",
ef416fc2 381 con->http.fd, con->http.hostname,
22c9029b
MS
382 _httpAddrPort(con->http.hostaddr),
383 _httpAddrFamily(con->http.hostaddr) == AF_INET ? 4 : 6);
ef416fc2 384
385 /*
386 * Get the local address the client connected to...
387 */
388
389 addrlen = sizeof(temp);
390 if (getsockname(con->http.fd, (struct sockaddr *)&temp, &addrlen))
391 {
392 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get local address - %s",
393 strerror(errno));
394
395 strcpy(con->servername, "localhost");
396 con->serverport = LocalPort;
397 }
22c9029b
MS
398#ifdef AF_LOCAL
399 else if (_httpAddrFamily(&temp) == AF_LOCAL)
400 {
401 strcpy(con->servername, "localhost");
402 con->serverport = LocalPort;
403 }
404#endif /* AF_LOCAL */
ef416fc2 405 else
406 {
22c9029b
MS
407 if (httpAddrLocalhost(&temp))
408 strlcpy(con->servername, "localhost", sizeof(con->servername));
409 else if (HostNameLookups || RemotePort)
410 httpAddrLookup(&temp, con->servername, sizeof(con->servername));
ef416fc2 411 else
22c9029b 412 httpAddrString(&temp, con->servername, sizeof(con->servername));
b19ccc9e 413
22c9029b 414 con->serverport = _httpAddrPort(&(lis->address));
ef416fc2 415 }
416
dcb445bc
MS
417 /*
418 * Add the connection to the array of active clients...
419 */
420
bd7854cb 421 cupsArrayAdd(Clients, con);
422
ef416fc2 423 /*
b9faaae1
MS
424 * Using TCP_NODELAY improves responsiveness, especially on systems with a slow
425 * loopback interface. Since we write large buffers when sending print files
426 * and requests there shouldn't be any performance penalty for this...
ef416fc2 427 */
428
429 val = 1;
f7deaa1a 430 setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
ef416fc2 431
432 /*
433 * Close this file on all execs...
434 */
435
436 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
437
438 /*
b9faaae1 439 * Add the socket to the server select.
ef416fc2 440 */
441
f7deaa1a 442 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
ef416fc2 443
ef416fc2 444 /*
445 * Temporarily suspend accept()'s until we lose a client...
446 */
447
bd7854cb 448 if (cupsArrayCount(Clients) == MaxClients)
ef416fc2 449 cupsdPauseListening();
450
451#ifdef HAVE_SSL
452 /*
453 * See if we are connecting on a secure port...
454 */
455
456 if (lis->encryption == HTTP_ENCRYPT_ALWAYS)
457 {
458 /*
459 * https connection; go secure...
460 */
461
462 con->http.encryption = HTTP_ENCRYPT_ALWAYS;
463
82cc1f9a 464 if (!cupsdStartTLS(con))
411affcf 465 cupsdCloseClient(con);
ef416fc2 466 }
467 else
468 con->auto_ssl = 1;
469#endif /* HAVE_SSL */
470}
471
472
473/*
474 * 'cupsdCloseAllClients()' - Close all remote clients immediately.
475 */
476
477void
478cupsdCloseAllClients(void)
479{
bd7854cb 480 cupsd_client_t *con; /* Current client */
481
482
b9faaae1
MS
483 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d",
484 cupsArrayCount(Clients));
485
bd7854cb 486 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
487 con;
488 con = (cupsd_client_t *)cupsArrayNext(Clients))
49d87452
MS
489 if (cupsdCloseClient(con))
490 cupsdCloseClient(con);
ef416fc2 491}
492
493
494/*
495 * 'cupsdCloseClient()' - Close a remote client.
496 */
497
498int /* O - 1 if partial close, 0 if fully closed */
499cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */
500{
501 int partial; /* Do partial close for SSL? */
502#ifdef HAVE_LIBSSL
ef416fc2 503#elif defined(HAVE_GNUTLS)
89d46774 504# elif defined(HAVE_CDSASSL)
ef416fc2 505#endif /* HAVE_LIBSSL */
506
507
85dda01c
MS
508 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] Closing connection.",
509 con->http.fd);
ef416fc2 510
511 /*
512 * Flush pending writes before closing...
513 */
514
515 httpFlushWrite(HTTP(con));
516
517 partial = 0;
518
519#ifdef HAVE_SSL
520 /*
521 * Shutdown encryption as needed...
522 */
523
524 if (con->http.tls)
525 {
526 partial = 1;
527
82cc1f9a 528 cupsdEndTLS(con);
ef416fc2 529 }
530#endif /* HAVE_SSL */
531
532 if (con->pipe_pid != 0)
533 {
534 /*
535 * Stop any CGI process...
536 */
537
ef416fc2 538 cupsdEndProcess(con->pipe_pid, 1);
539 con->pipe_pid = 0;
540 }
541
542 if (con->file >= 0)
543 {
f7deaa1a 544 cupsdRemoveSelect(con->file);
ef416fc2 545
ef416fc2 546 close(con->file);
547 con->file = -1;
548 }
549
550 /*
551 * Close the socket and clear the file from the input set for select()...
552 */
553
3dfe78b3 554 if (con->http.fd >= 0)
ef416fc2 555 {
3dfe78b3
MS
556 cupsArrayRemove(ActiveClients, con);
557 cupsdSetBusyState();
558
ef416fc2 559 if (partial)
560 {
561 /*
562 * Only do a partial close so that the encrypted client gets everything.
563 */
564
ef416fc2 565 shutdown(con->http.fd, 0);
f7deaa1a 566 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
ef416fc2 567 }
568 else
569 {
570 /*
571 * Shut the socket down fully...
572 */
573
f7deaa1a 574 cupsdRemoveSelect(con->http.fd);
ef416fc2 575 close(con->http.fd);
ef416fc2 576 con->http.fd = -1;
577 }
578 }
579
580 if (!partial)
581 {
582 /*
583 * Free memory...
584 */
585
586 if (con->http.input_set)
587 free(con->http.input_set);
588
589 httpClearCookie(HTTP(con));
9f5eb9be 590 httpClearFields(HTTP(con));
ef416fc2 591
592 cupsdClearString(&con->filename);
593 cupsdClearString(&con->command);
594 cupsdClearString(&con->options);
b86bc4cf 595 cupsdClearString(&con->query_string);
ef416fc2 596
597 if (con->request)
598 {
599 ippDelete(con->request);
600 con->request = NULL;
601 }
602
603 if (con->response)
604 {
605 ippDelete(con->response);
606 con->response = NULL;
607 }
608
609 if (con->language)
610 {
611 cupsLangFree(con->language);
612 con->language = NULL;
613 }
614
f7deaa1a 615#ifdef HAVE_AUTHORIZATION_H
616 if (con->authref)
617 {
618 AuthorizationFree(con->authref, kAuthorizationFlagDefaults);
619 con->authref = NULL;
620 }
621#endif /* HAVE_AUTHORIZATION_H */
622
ef416fc2 623 /*
624 * Re-enable new client connections if we are going back under the
625 * limit...
626 */
627
bd7854cb 628 if (cupsArrayCount(Clients) == MaxClients)
ef416fc2 629 cupsdResumeListening();
630
631 /*
632 * Compact the list of clients as necessary...
633 */
634
bd7854cb 635 cupsArrayRemove(Clients, con);
ef416fc2 636
bd7854cb 637 free(con);
ef416fc2 638 }
639
640 return (partial);
641}
642
643
d09495fa 644/*
645 * 'cupsdFlushHeader()' - Flush the header fields to the client.
646 */
647
07725fee 648int /* I - Bytes written or -1 on error */
d09495fa 649cupsdFlushHeader(cupsd_client_t *con) /* I - Client to flush to */
650{
07725fee 651 int bytes = httpFlushWrite(HTTP(con));
d09495fa 652
653 con->http.data_encoding = HTTP_ENCODE_LENGTH;
07725fee 654
655 return (bytes);
d09495fa 656}
657
658
ef416fc2 659/*
660 * 'cupsdReadClient()' - Read data from a client.
661 */
662
f7deaa1a 663void
ef416fc2 664cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */
665{
666 char line[32768], /* Line from client... */
667 operation[64], /* Operation code from socket */
668 version[64], /* HTTP version number string */
669 locale[64], /* Locale */
670 *ptr; /* Pointer into strings */
671 int major, minor; /* HTTP version numbers */
672 http_status_t status; /* Transfer status */
673 ipp_state_t ipp_state; /* State of IPP transfer */
674 int bytes; /* Number of bytes to POST */
675 char *filename; /* Name of file for GET/HEAD */
676 char buf[1024]; /* Buffer for real filename */
677 struct stat filestats; /* File information */
678 mime_type_t *type; /* MIME type of file */
679 cupsd_printer_t *p; /* Printer */
680 static unsigned request_id = 0; /* Request ID for temp files */
681
682
683 status = HTTP_CONTINUE;
684
685 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
686 "[Client %d] cupsdReadClient "
687 "error=%d, "
688 "used=%d, "
689 "state=%s, "
690 "data_encoding=HTTP_ENCODE_%s, "
691 "data_remaining=" CUPS_LLFMT ", "
692 "request=%p(%s), "
693 "file=%d",
694 con->http.fd, con->http.error, con->http.used,
695 http_states[con->http.state],
b9faaae1
MS
696 con->http.data_encoding == HTTP_ENCODE_CHUNKED ?
697 "CHUNKED" : "LENGTH",
85dda01c
MS
698 CUPS_LLCAST con->http.data_remaining,
699 con->request,
700 con->request ? ipp_states[con->request->state] : "",
701 con->file);
ef416fc2 702
ef416fc2 703#ifdef HAVE_SSL
704 if (con->auto_ssl)
705 {
706 /*
707 * Automatically check for a SSL/TLS handshake...
708 */
709
710 con->auto_ssl = 0;
711
712 if (recv(con->http.fd, buf, 1, MSG_PEEK) == 1 &&
713 (!buf[0] || !strchr("DGHOPT", buf[0])))
714 {
715 /*
716 * Encrypt this connection...
717 */
718
719 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
720 "[Client %d] Saw first byte %02X, auto-negotiating "
721 "SSL/TLS session.", con->http.fd, buf[0] & 255);
ef416fc2 722
82cc1f9a 723 if (!cupsdStartTLS(con))
f7deaa1a 724 cupsdCloseClient(con);
411affcf 725
f7deaa1a 726 return;
ef416fc2 727 }
728 }
729#endif /* HAVE_SSL */
730
731 switch (con->http.state)
732 {
733 case HTTP_WAITING :
734 /*
735 * See if we've received a request line...
736 */
737
738 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
739 {
f11a948a
MS
740 if (con->http.error && con->http.error != EPIPE)
741 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c 742 "[Client %d] HTTP_WAITING Closing for error %d "
f11a948a
MS
743 "(%s)", con->http.fd, con->http.error,
744 strerror(con->http.error));
745 else
746 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c 747 "[Client %d] HTTP_WAITING Closing on EOF",
f11a948a
MS
748 con->http.fd);
749
f7deaa1a 750 cupsdCloseClient(con);
751 return;
ef416fc2 752 }
753
754 /*
755 * Ignore blank request lines...
756 */
757
758 if (line[0] == '\0')
759 break;
760
761 /*
762 * Clear other state variables...
763 */
764
765 httpClearFields(HTTP(con));
766
767 con->http.activity = time(NULL);
768 con->http.version = HTTP_1_0;
769 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
770 con->http.data_encoding = HTTP_ENCODE_LENGTH;
771 con->http.data_remaining = 0;
772 con->http._data_remaining = 0;
773 con->operation = HTTP_WAITING;
774 con->bytes = 0;
775 con->file = -1;
776 con->file_ready = 0;
777 con->pipe_pid = 0;
778 con->username[0] = '\0';
779 con->password[0] = '\0';
780 con->uri[0] = '\0';
781
782 cupsdClearString(&con->command);
783 cupsdClearString(&con->options);
b86bc4cf 784 cupsdClearString(&con->query_string);
ef416fc2 785
2abf387c 786 if (con->request)
787 {
788 ippDelete(con->request);
789 con->request = NULL;
790 }
791
792 if (con->response)
793 {
794 ippDelete(con->response);
795 con->response = NULL;
796 }
797
798 if (con->language)
ef416fc2 799 {
800 cupsLangFree(con->language);
801 con->language = NULL;
802 }
803
09a101d6 804#ifdef HAVE_GSSAPI
97c9a8d7 805 con->have_gss = 0;
07ed0e9a 806 con->gss_uid = 0;
09a101d6 807#endif /* HAVE_GSSAPI */
808
ef416fc2 809 /*
810 * Grab the request line...
811 */
812
813 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
814 {
815 case 1 :
c934a06c
MS
816 if (line[0])
817 {
818 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c
MS
819 "[Client %d] Bad request line \"%s\" from %s.",
820 con->http.fd,
c8fef167 821 _httpEncodeURI(buf, line, sizeof(buf)),
c934a06c
MS
822 con->http.hostname);
823 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
824 cupsdCloseClient(con);
825 }
f7deaa1a 826 return;
ef416fc2 827 case 2 :
828 con->http.version = HTTP_0_9;
829 break;
830 case 3 :
831 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
832 {
833 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c
MS
834 "[Client %d] Bad request line \"%s\" from %s.",
835 con->http.fd,
c8fef167 836 _httpEncodeURI(buf, line, sizeof(buf)),
ef416fc2 837 con->http.hostname);
5bd77a73 838 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 839 cupsdCloseClient(con);
840 return;
ef416fc2 841 }
842
843 if (major < 2)
844 {
845 con->http.version = (http_version_t)(major * 100 + minor);
846 if (con->http.version == HTTP_1_1 && KeepAlive)
847 con->http.keep_alive = HTTP_KEEPALIVE_ON;
848 else
849 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
850 }
851 else
852 {
f11a948a 853 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c
MS
854 "[Client %d] Unsupported request line \"%s\" "
855 "from %s.", con->http.fd,
c8fef167
MS
856 _httpEncodeURI(buf, line, sizeof(buf)),
857 con->http.hostname);
5bd77a73 858 cupsdSendError(con, HTTP_NOT_SUPPORTED, CUPSD_AUTH_NONE);
f7deaa1a 859 cupsdCloseClient(con);
860 return;
ef416fc2 861 }
862 break;
863 }
864
865 /*
866 * Handle full URLs in the request line...
867 */
868
869 if (strcmp(con->uri, "*"))
870 {
e6013cfa 871 char scheme[HTTP_MAX_URI], /* Method/scheme */
ef416fc2 872 userpass[HTTP_MAX_URI], /* Username:password */
873 hostname[HTTP_MAX_URI], /* Hostname */
874 resource[HTTP_MAX_URI]; /* Resource path */
875 int port; /* Port number */
876
877
878 /*
879 * Separate the URI into its components...
880 */
881
a4d04587 882 httpSeparateURI(HTTP_URI_CODING_MOST, con->uri,
e6013cfa 883 scheme, sizeof(scheme),
ef416fc2 884 userpass, sizeof(userpass),
885 hostname, sizeof(hostname), &port,
886 resource, sizeof(resource));
887
888 /*
889 * Only allow URIs with the servername, localhost, or an IP
890 * address...
891 */
892
e6013cfa 893 if (strcmp(scheme, "file") &&
88f9aafc
MS
894 _cups_strcasecmp(hostname, ServerName) &&
895 _cups_strcasecmp(hostname, "localhost") &&
e6013cfa 896 !isdigit(hostname[0]) && hostname[0] != '[')
ef416fc2 897 {
898 /*
899 * Nope, we don't do proxies...
900 */
901
85dda01c
MS
902 cupsdLogMessage(CUPSD_LOG_ERROR,
903 "[Client %d] Bad URI \"%s\" in request.",
904 con->http.fd, con->uri);
5bd77a73 905 cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE);
f7deaa1a 906 cupsdCloseClient(con);
907 return;
ef416fc2 908 }
909
910 /*
911 * Copy the resource portion back into the URI; both resource and
912 * con->uri are HTTP_MAX_URI bytes in size...
913 */
914
915 strcpy(con->uri, resource);
916 }
917
918 /*
919 * Process the request...
920 */
921
922 if (!strcmp(operation, "GET"))
923 con->http.state = HTTP_GET;
924 else if (!strcmp(operation, "PUT"))
925 con->http.state = HTTP_PUT;
926 else if (!strcmp(operation, "POST"))
927 con->http.state = HTTP_POST;
928 else if (!strcmp(operation, "DELETE"))
929 con->http.state = HTTP_DELETE;
930 else if (!strcmp(operation, "TRACE"))
931 con->http.state = HTTP_TRACE;
932 else if (!strcmp(operation, "OPTIONS"))
933 con->http.state = HTTP_OPTIONS;
934 else if (!strcmp(operation, "HEAD"))
935 con->http.state = HTTP_HEAD;
936 else
937 {
85dda01c
MS
938 cupsdLogMessage(CUPSD_LOG_ERROR,
939 "[Client %d] Bad operation \"%s\".", con->http.fd,
940 operation);
5bd77a73 941 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 942 cupsdCloseClient(con);
943 return;
ef416fc2 944 }
945
dfd5680b 946 gettimeofday(&(con->start), NULL);
ef416fc2 947 con->operation = con->http.state;
948
85dda01c 949 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] %s %s HTTP/%d.%d",
89d46774 950 con->http.fd, operation, con->uri,
ef416fc2 951 con->http.version / 100, con->http.version % 100);
952
953 con->http.status = HTTP_OK;
954
49d87452
MS
955 if (!cupsArrayFind(ActiveClients, con))
956 {
957 cupsArrayAdd(ActiveClients, con);
958 cupsdSetBusyState();
959 }
3dfe78b3 960
ef416fc2 961 case HTTP_OPTIONS :
962 case HTTP_DELETE :
963 case HTTP_GET :
964 case HTTP_HEAD :
965 case HTTP_POST :
966 case HTTP_PUT :
967 case HTTP_TRACE :
968 /*
969 * Parse incoming parameters until the status changes...
970 */
971
f7deaa1a 972 while ((status = httpUpdate(HTTP(con))) == HTTP_CONTINUE)
ee571f26 973 if (!data_ready(con))
f7deaa1a 974 break;
ef416fc2 975
976 if (status != HTTP_OK && status != HTTP_CONTINUE)
977 {
f11a948a
MS
978 if (con->http.error && con->http.error != EPIPE)
979 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c
MS
980 "[Client %d] Closing for error %d (%s) while "
981 "reading headers.",
982 con->http.fd, con->http.error,
f11a948a
MS
983 strerror(con->http.error));
984 else
985 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c
MS
986 "[Client %d] Closing on EOF while reading headers.",
987 con->http.fd);
f11a948a 988
5bd77a73 989 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 990 cupsdCloseClient(con);
991 return;
ef416fc2 992 }
993 break;
994
995 default :
e6013cfa
MS
996 if (!data_ready(con) && recv(con->http.fd, buf, 1, MSG_PEEK) < 1)
997 {
998 /*
999 * Connection closed...
1000 */
1001
f11a948a 1002 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c 1003 "[Client %d] Closing on EOF", con->http.fd);
e6013cfa
MS
1004 cupsdCloseClient(con);
1005 return;
1006 }
ef416fc2 1007 break; /* Anti-compiler-warning-code */
1008 }
1009
1010 /*
1011 * Handle new transfers...
1012 */
1013
1014 if (status == HTTP_OK)
1015 {
1016 if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
1017 {
1018 /*
1019 * Figure out the locale from the Accept-Language and Content-Type
1020 * fields...
1021 */
1022
f11a948a
MS
1023 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
1024 ',')) != NULL)
ef416fc2 1025 *ptr = '\0';
1026
f11a948a
MS
1027 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
1028 ';')) != NULL)
ef416fc2 1029 *ptr = '\0';
1030
f11a948a
MS
1031 if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
1032 "charset=")) != NULL)
ef416fc2 1033 {
1034 /*
1035 * Combine language and charset, and trim any extra params in the
1036 * content-type.
1037 */
1038
1039 snprintf(locale, sizeof(locale), "%s.%s",
1040 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8);
1041
1042 if ((ptr = strchr(locale, ',')) != NULL)
1043 *ptr = '\0';
1044 }
1045 else
f8b3a85b
MS
1046 snprintf(locale, sizeof(locale), "%s.UTF-8",
1047 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
d6ae789d 1048
ef416fc2 1049 con->language = cupsLangGet(locale);
1050 }
1051 else
1052 con->language = cupsLangGet(DefaultLocale);
1053
1054 cupsdAuthorize(con);
1055
85dda01c
MS
1056 if (!_cups_strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION],
1057 "Keep-Alive", 10) && KeepAlive)
ef416fc2 1058 con->http.keep_alive = HTTP_KEEPALIVE_ON;
85dda01c
MS
1059 else if (!_cups_strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION],
1060 "close", 5))
52f6f666 1061 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
ef416fc2 1062
1063 if (!con->http.fields[HTTP_FIELD_HOST][0] &&
1064 con->http.version >= HTTP_1_1)
1065 {
1066 /*
1067 * HTTP/1.1 and higher require the "Host:" field...
1068 */
1069
5bd77a73 1070 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
f7deaa1a 1071 {
85dda01c
MS
1072 cupsdLogMessage(CUPSD_LOG_ERROR,
1073 "[Client %d] Missing Host: field in request.",
1074 con->http.fd);
f7deaa1a 1075 cupsdCloseClient(con);
1076 return;
1077 }
ef416fc2 1078 }
e07d4801 1079 else if (!valid_host(con))
e6013cfa
MS
1080 {
1081 /*
1082 * Access to localhost must use "localhost" or the corresponding IPv4
1083 * or IPv6 values in the Host: field.
1084 */
1085
f11a948a 1086 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c
MS
1087 "[Client %d] Request from \"%s\" using invalid Host: "
1088 "field \"%s\"", con->http.fd, con->http.hostname,
1089 con->http.fields[HTTP_FIELD_HOST]);
e6013cfa
MS
1090
1091 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
1092 {
1093 cupsdCloseClient(con);
1094 return;
1095 }
1096 }
ef416fc2 1097 else if (con->operation == HTTP_OPTIONS)
1098 {
1099 /*
1100 * Do OPTIONS command...
1101 */
1102
5bd77a73 1103 if (con->best && con->best->type != CUPSD_AUTH_NONE)
ef416fc2 1104 {
5bd77a73 1105 if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1106 {
1107 cupsdCloseClient(con);
1108 return;
1109 }
ef416fc2 1110 }
1111
88f9aafc 1112 if (!_cups_strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
ef416fc2 1113 con->http.tls == NULL)
1114 {
1115#ifdef HAVE_SSL
1116 /*
1117 * Do encryption stuff...
1118 */
1119
5bd77a73 1120 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1121 {
1122 cupsdCloseClient(con);
1123 return;
1124 }
ef416fc2 1125
1126 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1127 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1128 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1129 httpPrintf(HTTP(con), "\r\n");
07725fee 1130
1131 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1132 {
1133 cupsdCloseClient(con);
1134 return;
1135 }
ef416fc2 1136
82cc1f9a 1137 if (!cupsdStartTLS(con))
f7deaa1a 1138 {
1139 cupsdCloseClient(con);
1140 return;
1141 }
ef416fc2 1142#else
5bd77a73 1143 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
f7deaa1a 1144 {
1145 cupsdCloseClient(con);
1146 return;
1147 }
ef416fc2 1148#endif /* HAVE_SSL */
1149 }
1150
5bd77a73 1151 if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1152 {
1153 cupsdCloseClient(con);
1154 return;
1155 }
ef416fc2 1156
1157 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
1158 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1159 httpPrintf(HTTP(con), "\r\n");
07725fee 1160
1161 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1162 {
1163 cupsdCloseClient(con);
1164 return;
1165 }
ef416fc2 1166 }
1167 else if (!is_path_absolute(con->uri))
1168 {
1169 /*
1170 * Protect against malicious users!
1171 */
1172
bf3816c7 1173 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c
MS
1174 "[Client %d] Request for non-absolute resource \"%s\".",
1175 con->http.fd, con->uri);
bf3816c7 1176
5bd77a73 1177 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
f7deaa1a 1178 {
1179 cupsdCloseClient(con);
1180 return;
1181 }
ef416fc2 1182 }
1183 else
1184 {
85dda01c
MS
1185 if (!_cups_strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION],
1186 "Upgrade") && con->http.tls == NULL)
ef416fc2 1187 {
1188#ifdef HAVE_SSL
1189 /*
1190 * Do encryption stuff...
1191 */
1192
85dda01c
MS
1193 if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL,
1194 CUPSD_AUTH_NONE))
f7deaa1a 1195 {
1196 cupsdCloseClient(con);
1197 return;
1198 }
ef416fc2 1199
1200 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
1201 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1202 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1203 httpPrintf(HTTP(con), "\r\n");
07725fee 1204
1205 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1206 {
1207 cupsdCloseClient(con);
1208 return;
1209 }
ef416fc2 1210
82cc1f9a 1211 if (!cupsdStartTLS(con))
f7deaa1a 1212 {
1213 cupsdCloseClient(con);
1214 return;
1215 }
ef416fc2 1216#else
5bd77a73 1217 if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
f7deaa1a 1218 {
1219 cupsdCloseClient(con);
1220 return;
1221 }
ef416fc2 1222#endif /* HAVE_SSL */
1223 }
1224
1225 if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_OK)
1226 {
5bd77a73 1227 cupsdSendError(con, status, CUPSD_AUTH_NONE);
f7deaa1a 1228 cupsdCloseClient(con);
1229 return;
ef416fc2 1230 }
1231
b423cd4c 1232 if (con->http.expect &&
1233 (con->operation == HTTP_POST || con->operation == HTTP_PUT))
ef416fc2 1234 {
b423cd4c 1235 if (con->http.expect == HTTP_CONTINUE)
1236 {
1237 /*
1238 * Send 100-continue header...
1239 */
1240
5bd77a73 1241 if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL, CUPSD_AUTH_NONE))
f7deaa1a 1242 {
1243 cupsdCloseClient(con);
1244 return;
1245 }
b423cd4c 1246 }
1247 else
1248 {
1249 /*
1250 * Send 417-expectation-failed header...
1251 */
1252
f11a948a
MS
1253 if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL,
1254 CUPSD_AUTH_NONE))
f7deaa1a 1255 {
1256 cupsdCloseClient(con);
1257 return;
1258 }
b423cd4c 1259
1260 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
1261 httpPrintf(HTTP(con), "\r\n");
07725fee 1262
1263 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1264 {
1265 cupsdCloseClient(con);
1266 return;
1267 }
b423cd4c 1268 }
ef416fc2 1269 }
1270
1271 switch (con->http.state)
1272 {
1273 case HTTP_GET_SEND :
1274 if (!strncmp(con->uri, "/printers/", 10) &&
1275 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd"))
1276 {
1277 /*
1278 * Send PPD file - get the real printer name since printer
1279 * names are not case sensitive but filenames can be...
1280 */
1281
1282 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1283
1284 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1285 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1286 else
1287 {
5bd77a73 1288 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1289 {
1290 cupsdCloseClient(con);
1291 return;
1292 }
ef416fc2 1293
1294 break;
1295 }
1296 }
7cf5915e
MS
1297 else if ((!strncmp(con->uri, "/printers/", 10) ||
1298 !strncmp(con->uri, "/classes/", 9)) &&
1299 !strcmp(con->uri + strlen(con->uri) - 4, ".png"))
1300 {
1301 /*
1302 * Send icon file - get the real queue name since queue names are
1303 * not case sensitive but filenames can be...
1304 */
1305
1306 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".png" */
1307
1308 if (!strncmp(con->uri, "/printers/", 10))
1309 p = cupsdFindPrinter(con->uri + 10);
1310 else
1311 p = cupsdFindClass(con->uri + 9);
1312
1313 if (p)
1314 snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name);
1315 else
1316 {
1317 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1318 {
1319 cupsdCloseClient(con);
1320 return;
1321 }
1322
1323 break;
1324 }
1325 }
0268488e 1326 else if (!WebInterface)
229681c1
MS
1327 {
1328 /*
1329 * Web interface is disabled. Show an appropriate message...
1330 */
1331
1332 if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE))
1333 {
1334 cupsdCloseClient(con);
1335 return;
1336 }
1337
1338 break;
1339 }
0268488e
MS
1340
1341 if ((!strncmp(con->uri, "/admin", 6) &&
1342 strncmp(con->uri, "/admin/conf/", 12) &&
1343 strncmp(con->uri, "/admin/log/", 11)) ||
1344 !strncmp(con->uri, "/printers", 9) ||
1345 !strncmp(con->uri, "/classes", 8) ||
1346 !strncmp(con->uri, "/help", 5) ||
1347 !strncmp(con->uri, "/jobs", 5))
ef416fc2 1348 {
1349 /*
1350 * Send CGI output...
1351 */
1352
1353 if (!strncmp(con->uri, "/admin", 6))
1354 {
1355 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi",
1356 ServerBin);
1357
b423cd4c 1358 cupsdSetString(&con->options, strchr(con->uri + 6, '?'));
ef416fc2 1359 }
1360 else if (!strncmp(con->uri, "/printers", 9))
1361 {
1362 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1363 ServerBin);
b423cd4c 1364
1365 if (con->uri[9] && con->uri[10])
1366 cupsdSetString(&con->options, con->uri + 9);
1367 else
1368 cupsdSetString(&con->options, NULL);
ef416fc2 1369 }
1370 else if (!strncmp(con->uri, "/classes", 8))
1371 {
1372 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1373 ServerBin);
b423cd4c 1374
1375 if (con->uri[8] && con->uri[9])
1376 cupsdSetString(&con->options, con->uri + 8);
1377 else
1378 cupsdSetString(&con->options, NULL);
ef416fc2 1379 }
1380 else if (!strncmp(con->uri, "/jobs", 5))
1381 {
1382 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1383 ServerBin);
b423cd4c 1384
1385 if (con->uri[5] && con->uri[6])
1386 cupsdSetString(&con->options, con->uri + 5);
1387 else
1388 cupsdSetString(&con->options, NULL);
ef416fc2 1389 }
1390 else
1391 {
1392 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1393 ServerBin);
ef416fc2 1394
b423cd4c 1395 if (con->uri[5] && con->uri[6])
1396 cupsdSetString(&con->options, con->uri + 5);
1397 else
1398 cupsdSetString(&con->options, NULL);
1399 }
ef416fc2 1400
1401 if (!cupsdSendCommand(con, con->command, con->options, 0))
1402 {
5bd77a73 1403 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1404 {
1405 cupsdCloseClient(con);
1406 return;
1407 }
ef416fc2 1408 }
1409 else
1410 cupsdLogRequest(con, HTTP_OK);
1411
1412 if (con->http.version <= HTTP_1_0)
1413 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1414 }
1415 else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
1416 (strchr(con->uri + 12, '/') ||
1417 strlen(con->uri) == 12)) ||
1418 (!strncmp(con->uri, "/admin/log/", 11) &&
1419 (strchr(con->uri + 11, '/') ||
1420 strlen(con->uri) == 11)))
1421 {
1422 /*
bf3816c7 1423 * GET can only be done to configuration files directly under
ef416fc2 1424 * /admin/conf...
1425 */
1426
bf3816c7
MS
1427 cupsdLogMessage(CUPSD_LOG_ERROR,
1428 "Request for subdirectory \"%s\"!", con->uri);
1429
5bd77a73 1430 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
f7deaa1a 1431 {
1432 cupsdCloseClient(con);
1433 return;
1434 }
ef416fc2 1435
1436 break;
1437 }
1438 else
1439 {
1440 /*
1441 * Serve a file...
1442 */
1443
1444 if ((filename = get_file(con, &filestats, buf,
1445 sizeof(buf))) == NULL)
1446 {
5bd77a73 1447 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1448 {
1449 cupsdCloseClient(con);
1450 return;
1451 }
ef416fc2 1452
1453 break;
1454 }
1455
4400e98d 1456 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
ef416fc2 1457
e1d6a774 1458 if (is_cgi(con, filename, &filestats, type))
ef416fc2 1459 {
1460 /*
1461 * Note: con->command and con->options were set by
e1d6a774 1462 * is_cgi()...
ef416fc2 1463 */
1464
1465 if (!cupsdSendCommand(con, con->command, con->options, 0))
1466 {
5bd77a73 1467 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1468 {
1469 cupsdCloseClient(con);
1470 return;
1471 }
ef416fc2 1472 }
1473 else
1474 cupsdLogRequest(con, HTTP_OK);
1475
1476 if (con->http.version <= HTTP_1_0)
1477 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1478 break;
1479 }
1480
1481 if (!check_if_modified(con, &filestats))
1482 {
5bd77a73 1483 if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
f7deaa1a 1484 {
1485 cupsdCloseClient(con);
1486 return;
1487 }
ef416fc2 1488 }
1489 else
1490 {
1491 if (type == NULL)
1492 strcpy(line, "text/plain");
1493 else
1494 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1495
a74454a7 1496 if (!write_file(con, HTTP_OK, filename, line, &filestats))
f7deaa1a 1497 {
1498 cupsdCloseClient(con);
1499 return;
1500 }
ef416fc2 1501 }
1502 }
1503 break;
1504
1505 case HTTP_POST_RECV :
1506 /*
1507 * See if the POST request includes a Content-Length field, and if
1508 * so check the length against any limits that are set...
1509 */
1510
ef416fc2 1511 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1512 MaxRequestSize > 0 &&
1513 con->http.data_remaining > MaxRequestSize)
1514 {
1515 /*
1516 * Request too large...
1517 */
1518
5bd77a73 1519 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 1520 {
1521 cupsdCloseClient(con);
1522 return;
1523 }
ef416fc2 1524
1525 break;
1526 }
839a51c8
MS
1527 else if (con->http.data_remaining < 0 ||
1528 (!con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1529 con->http.data_encoding == HTTP_ENCODE_LENGTH))
ef416fc2 1530 {
1531 /*
1532 * Negative content lengths are invalid!
1533 */
1534
5bd77a73 1535 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
f7deaa1a 1536 {
1537 cupsdCloseClient(con);
1538 return;
1539 }
ef416fc2 1540
1541 break;
1542 }
1543
1544 /*
1545 * See what kind of POST request this is; for IPP requests the
1546 * content-type field will be "application/ipp"...
1547 */
1548
1549 if (!strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
1550 "application/ipp"))
1551 con->request = ippNew();
229681c1
MS
1552 else if (!WebInterface)
1553 {
1554 /*
1555 * Web interface is disabled. Show an appropriate message...
1556 */
1557
1558 if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE))
1559 {
1560 cupsdCloseClient(con);
1561 return;
1562 }
1563
1564 break;
1565 }
ef416fc2 1566 else if ((!strncmp(con->uri, "/admin", 6) &&
1567 strncmp(con->uri, "/admin/conf/", 12) &&
1568 strncmp(con->uri, "/admin/log/", 11)) ||
1569 !strncmp(con->uri, "/printers", 9) ||
1570 !strncmp(con->uri, "/classes", 8) ||
1571 !strncmp(con->uri, "/help", 5) ||
1572 !strncmp(con->uri, "/jobs", 5))
1573 {
1574 /*
1575 * CGI request...
1576 */
1577
1578 if (!strncmp(con->uri, "/admin", 6))
1579 {
1580 cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi",
1581 ServerBin);
1582
b423cd4c 1583 cupsdSetString(&con->options, strchr(con->uri + 6, '?'));
ef416fc2 1584 }
1585 else if (!strncmp(con->uri, "/printers", 9))
1586 {
1587 cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi",
1588 ServerBin);
b423cd4c 1589
1590 if (con->uri[9] && con->uri[10])
1591 cupsdSetString(&con->options, con->uri + 9);
1592 else
1593 cupsdSetString(&con->options, NULL);
ef416fc2 1594 }
1595 else if (!strncmp(con->uri, "/classes", 8))
1596 {
1597 cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi",
1598 ServerBin);
b423cd4c 1599
1600 if (con->uri[8] && con->uri[9])
1601 cupsdSetString(&con->options, con->uri + 8);
1602 else
1603 cupsdSetString(&con->options, NULL);
ef416fc2 1604 }
1605 else if (!strncmp(con->uri, "/jobs", 5))
1606 {
1607 cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi",
1608 ServerBin);
b423cd4c 1609
1610 if (con->uri[5] && con->uri[6])
1611 cupsdSetString(&con->options, con->uri + 5);
1612 else
1613 cupsdSetString(&con->options, NULL);
ef416fc2 1614 }
1615 else
1616 {
1617 cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi",
1618 ServerBin);
ef416fc2 1619
b423cd4c 1620 if (con->uri[5] && con->uri[6])
1621 cupsdSetString(&con->options, con->uri + 5);
1622 else
1623 cupsdSetString(&con->options, NULL);
1624 }
ef416fc2 1625
ef416fc2 1626 if (con->http.version <= HTTP_1_0)
1627 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1628 }
1629 else
1630 {
1631 /*
1632 * POST to a file...
1633 */
1634
1635 if ((filename = get_file(con, &filestats, buf,
1636 sizeof(buf))) == NULL)
1637 {
5bd77a73 1638 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1639 {
1640 cupsdCloseClient(con);
1641 return;
1642 }
ef416fc2 1643
1644 break;
1645 }
1646
4400e98d 1647 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
ef416fc2 1648
e1d6a774 1649 if (!is_cgi(con, filename, &filestats, type))
ef416fc2 1650 {
1651 /*
1652 * Only POST to CGI's...
1653 */
1654
5bd77a73 1655 if (!cupsdSendError(con, HTTP_UNAUTHORIZED, CUPSD_AUTH_NONE))
f7deaa1a 1656 {
1657 cupsdCloseClient(con);
1658 return;
1659 }
ef416fc2 1660 }
1661 }
1662 break;
1663
1664 case HTTP_PUT_RECV :
1665 /*
1666 * Validate the resource name...
1667 */
1668
1669 if (strncmp(con->uri, "/admin/conf/", 12) ||
1670 strchr(con->uri + 12, '/') ||
1671 strlen(con->uri) == 12)
1672 {
1673 /*
1674 * PUT can only be done to configuration files under
1675 * /admin/conf...
1676 */
1677
bf3816c7 1678 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c
MS
1679 "[Client %d] Request for subdirectory \"%s\".",
1680 con->http.fd, con->uri);
bf3816c7 1681
5bd77a73 1682 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
f7deaa1a 1683 {
1684 cupsdCloseClient(con);
1685 return;
1686 }
ef416fc2 1687
1688 break;
1689 }
1690
1691 /*
1692 * See if the PUT request includes a Content-Length field, and if
1693 * so check the length against any limits that are set...
1694 */
1695
ef416fc2 1696 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1697 MaxRequestSize > 0 &&
1698 con->http.data_remaining > MaxRequestSize)
1699 {
1700 /*
1701 * Request too large...
1702 */
1703
5bd77a73 1704 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 1705 {
1706 cupsdCloseClient(con);
1707 return;
1708 }
ef416fc2 1709
1710 break;
1711 }
1712 else if (con->http.data_remaining < 0)
1713 {
1714 /*
1715 * Negative content lengths are invalid!
1716 */
1717
5bd77a73 1718 if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
f7deaa1a 1719 {
1720 cupsdCloseClient(con);
1721 return;
1722 }
ef416fc2 1723
1724 break;
1725 }
1726
1727 /*
1728 * Open a temporary file to hold the request...
1729 */
1730
1731 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
1732 request_id ++);
1733 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1734
ef416fc2 1735 if (con->file < 0)
1736 {
a4924f6c 1737 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c
MS
1738 "[Client %d] Unable to create request file "
1739 "\"%s\": %s", con->http.fd, con->filename,
1740 strerror(errno));
a4924f6c 1741
5bd77a73 1742 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 1743 {
1744 cupsdCloseClient(con);
1745 return;
1746 }
ef416fc2 1747 }
1748
1749 fchmod(con->file, 0640);
1750 fchown(con->file, RunUser, Group);
1751 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1752 break;
1753
1754 case HTTP_DELETE :
1755 case HTTP_TRACE :
5bd77a73 1756 cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE);
f7deaa1a 1757 cupsdCloseClient(con);
1758 return;
ef416fc2 1759
1760 case HTTP_HEAD :
1761 if (!strncmp(con->uri, "/printers/", 10) &&
1762 !strcmp(con->uri + strlen(con->uri) - 4, ".ppd"))
1763 {
1764 /*
1765 * Send PPD file - get the real printer name since printer
1766 * names are not case sensitive but filenames can be...
1767 */
1768
1769 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1770
1771 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1772 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1773 else
1774 {
5bd77a73 1775 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 1776 {
1777 cupsdCloseClient(con);
1778 return;
1779 }
ef416fc2 1780
1781 break;
1782 }
1783 }
229681c1
MS
1784 else if (!strncmp(con->uri, "/printers/", 10) &&
1785 !strcmp(con->uri + strlen(con->uri) - 4, ".png"))
1786 {
1787 /*
1788 * Send PNG file - get the real printer name since printer
1789 * names are not case sensitive but filenames can be...
1790 */
1791
1792 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1793
1794 if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
1795 snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name);
1796 else
1797 {
1798 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
1799 {
1800 cupsdCloseClient(con);
1801 return;
1802 }
1803
1804 break;
1805 }
1806 }
1807 else if (!WebInterface)
1808 {
1809 if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
1810 {
1811 cupsdCloseClient(con);
1812 return;
1813 }
1814
1815 if (httpPrintf(HTTP(con), "\r\n") < 0)
1816 {
1817 cupsdCloseClient(con);
1818 return;
1819 }
1820
1821 if (cupsdFlushHeader(con) < 0)
1822 {
1823 cupsdCloseClient(con);
1824 return;
1825 }
1826
1827 con->http.state = HTTP_WAITING;
1828 break;
1829 }
ef416fc2 1830
1831 if ((!strncmp(con->uri, "/admin", 6) &&
1832 strncmp(con->uri, "/admin/conf/", 12) &&
1833 strncmp(con->uri, "/admin/log/", 11)) ||
1834 !strncmp(con->uri, "/printers", 9) ||
1835 !strncmp(con->uri, "/classes", 8) ||
1836 !strncmp(con->uri, "/help", 5) ||
1837 !strncmp(con->uri, "/jobs", 5))
1838 {
1839 /*
1840 * CGI output...
1841 */
1842
5bd77a73 1843 if (!cupsdSendHeader(con, HTTP_OK, "text/html", CUPSD_AUTH_NONE))
f7deaa1a 1844 {
1845 cupsdCloseClient(con);
1846 return;
1847 }
ef416fc2 1848
1849 if (httpPrintf(HTTP(con), "\r\n") < 0)
f7deaa1a 1850 {
1851 cupsdCloseClient(con);
1852 return;
1853 }
ef416fc2 1854
07725fee 1855 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1856 {
1857 cupsdCloseClient(con);
1858 return;
1859 }
d09495fa 1860
ef416fc2 1861 cupsdLogRequest(con, HTTP_OK);
1862 }
1863 else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
1864 (strchr(con->uri + 12, '/') ||
1865 strlen(con->uri) == 12)) ||
1866 (!strncmp(con->uri, "/admin/log/", 11) &&
1867 (strchr(con->uri + 11, '/') ||
1868 strlen(con->uri) == 11)))
1869 {
1870 /*
1871 * HEAD can only be done to configuration files under
1872 * /admin/conf...
1873 */
1874
bf3816c7 1875 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c
MS
1876 "[Client %d] Request for subdirectory \"%s\".",
1877 con->http.fd, con->uri);
bf3816c7 1878
5bd77a73 1879 if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
f7deaa1a 1880 {
1881 cupsdCloseClient(con);
1882 return;
1883 }
ef416fc2 1884
1885 break;
1886 }
1887 else if ((filename = get_file(con, &filestats, buf,
1888 sizeof(buf))) == NULL)
1889 {
f11a948a
MS
1890 if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html",
1891 CUPSD_AUTH_NONE))
f7deaa1a 1892 {
1893 cupsdCloseClient(con);
1894 return;
1895 }
ef416fc2 1896
1897 cupsdLogRequest(con, HTTP_NOT_FOUND);
1898 }
1899 else if (!check_if_modified(con, &filestats))
1900 {
5bd77a73 1901 if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
f7deaa1a 1902 {
1903 cupsdCloseClient(con);
1904 return;
1905 }
ef416fc2 1906
1907 cupsdLogRequest(con, HTTP_NOT_MODIFIED);
1908 }
1909 else
1910 {
1911 /*
1912 * Serve a file...
1913 */
1914
4400e98d 1915 type = mimeFileType(MimeDatabase, filename, NULL, NULL);
ef416fc2 1916 if (type == NULL)
1917 strcpy(line, "text/plain");
1918 else
1919 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1920
5bd77a73 1921 if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
f7deaa1a 1922 {
1923 cupsdCloseClient(con);
1924 return;
1925 }
ef416fc2 1926
1927 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1928 httpGetDateString(filestats.st_mtime)) < 0)
f7deaa1a 1929 {
1930 cupsdCloseClient(con);
1931 return;
1932 }
ef416fc2 1933
1934 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1935 (unsigned long)filestats.st_size) < 0)
f7deaa1a 1936 {
1937 cupsdCloseClient(con);
1938 return;
1939 }
ef416fc2 1940
1941 cupsdLogRequest(con, HTTP_OK);
1942 }
1943
1944 if (httpPrintf(HTTP(con), "\r\n") < 0)
f7deaa1a 1945 {
1946 cupsdCloseClient(con);
1947 return;
1948 }
ef416fc2 1949
07725fee 1950 if (cupsdFlushHeader(con) < 0)
f7deaa1a 1951 {
1952 cupsdCloseClient(con);
1953 return;
1954 }
d09495fa 1955
ef416fc2 1956 con->http.state = HTTP_WAITING;
1957 break;
1958
1959 default :
1960 break; /* Anti-compiler-warning-code */
1961 }
1962 }
1963 }
1964
1965 /*
1966 * Handle any incoming data...
1967 */
1968
1969 switch (con->http.state)
1970 {
1971 case HTTP_PUT_RECV :
f7deaa1a 1972 do
ef416fc2 1973 {
f7deaa1a 1974 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
1975 {
f11a948a
MS
1976 if (con->http.error && con->http.error != EPIPE)
1977 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c 1978 "[Client %d] HTTP_PUT_RECV Closing for error "
f11a948a
MS
1979 "%d (%s)", con->http.fd, con->http.error,
1980 strerror(con->http.error));
1981 else
1982 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c 1983 "[Client %d] HTTP_PUT_RECV Closing on EOF",
f11a948a
MS
1984 con->http.fd);
1985
f7deaa1a 1986 cupsdCloseClient(con);
1987 return;
1988 }
1989 else if (bytes > 0)
1990 {
1991 con->bytes += bytes;
ef416fc2 1992
f7deaa1a 1993 if (write(con->file, line, bytes) < bytes)
1994 {
1995 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c
MS
1996 "[Client %d] Unable to write %d bytes to "
1997 "\"%s\": %s", con->http.fd, bytes, con->filename,
1998 strerror(errno));
ef416fc2 1999
f7deaa1a 2000 close(con->file);
2001 con->file = -1;
2002 unlink(con->filename);
2003 cupsdClearString(&con->filename);
ef416fc2 2004
5bd77a73 2005 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 2006 {
2007 cupsdCloseClient(con);
2008 return;
2009 }
2010 }
ef416fc2 2011 }
f7deaa1a 2012 }
ee571f26 2013 while (con->http.state == HTTP_PUT_RECV && data_ready(con));
ef416fc2 2014
2015 if (con->http.state == HTTP_WAITING)
2016 {
2017 /*
2018 * End of file, see how big it is...
2019 */
2020
2021 fstat(con->file, &filestats);
2022
ef416fc2 2023 close(con->file);
2024 con->file = -1;
2025
2026 if (filestats.st_size > MaxRequestSize &&
2027 MaxRequestSize > 0)
2028 {
2029 /*
2030 * Request is too big; remove it and send an error...
2031 */
2032
ef416fc2 2033 unlink(con->filename);
2034 cupsdClearString(&con->filename);
2035
5bd77a73 2036 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 2037 {
2038 cupsdCloseClient(con);
2039 return;
2040 }
ef416fc2 2041 }
2042
2043 /*
2044 * Install the configuration file...
2045 */
2046
2047 status = install_conf_file(con);
2048
2049 /*
2050 * Return the status to the client...
2051 */
2052
5bd77a73 2053 if (!cupsdSendError(con, status, CUPSD_AUTH_NONE))
f7deaa1a 2054 {
2055 cupsdCloseClient(con);
2056 return;
2057 }
ef416fc2 2058 }
2059 break;
2060
2061 case HTTP_POST_RECV :
f7deaa1a 2062 do
ef416fc2 2063 {
f11a948a 2064 if (con->request && con->file < 0)
ef416fc2 2065 {
f7deaa1a 2066 /*
2067 * Grab any request data from the connection...
2068 */
ef416fc2 2069
f7deaa1a 2070 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
ef416fc2 2071 {
f7deaa1a 2072 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c
MS
2073 "[Client %d] IPP read error: %s", con->http.fd,
2074 cupsLastErrorString());
f7deaa1a 2075
5bd77a73 2076 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 2077 cupsdCloseClient(con);
2078 return;
ef416fc2 2079 }
f7deaa1a 2080 else if (ipp_state != IPP_DATA)
2081 {
2082 if (con->http.state == HTTP_POST_SEND)
2083 {
5bd77a73 2084 cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
f7deaa1a 2085 cupsdCloseClient(con);
2086 return;
2087 }
ef416fc2 2088
f7deaa1a 2089 break;
2090 }
2091 else
f11a948a 2092 {
85dda01c 2093 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] %d.%d %s %d",
f11a948a
MS
2094 con->http.fd, con->request->request.op.version[0],
2095 con->request->request.op.version[1],
2096 ippOpString(con->request->request.op.operation_id),
2097 con->request->request.op.request_id);
f7deaa1a 2098 con->bytes += ippLength(con->request);
f11a948a 2099 }
f7deaa1a 2100 }
ef416fc2 2101
f7deaa1a 2102 if (con->file < 0 && con->http.state != HTTP_POST_SEND)
2103 {
2104 /*
2105 * Create a file as needed for the request data...
2106 */
ef416fc2 2107
f11a948a
MS
2108 cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
2109 request_id ++);
f7deaa1a 2110 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
ef416fc2 2111
f7deaa1a 2112 if (con->file < 0)
2113 {
a4924f6c 2114 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c
MS
2115 "[Client %d] Unable to create request file "
2116 "\"%s\": %s", con->http.fd, con->filename,
2117 strerror(errno));
a4924f6c 2118
5bd77a73 2119 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 2120 {
2121 cupsdCloseClient(con);
2122 return;
2123 }
2124 }
ef416fc2 2125
f7deaa1a 2126 fchmod(con->file, 0640);
2127 fchown(con->file, RunUser, Group);
2128 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2129 }
ef416fc2 2130
f7deaa1a 2131 if (con->http.state != HTTP_POST_SEND)
ef416fc2 2132 {
f7deaa1a 2133 if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
2134 {
f11a948a
MS
2135 if (con->http.error && con->http.error != EPIPE)
2136 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c 2137 "[Client %d] HTTP_POST_SEND Closing for "
f11a948a
MS
2138 "error %d (%s)", con->http.fd, con->http.error,
2139 strerror(con->http.error));
2140 else
2141 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c 2142 "[Client %d] HTTP_POST_SEND Closing on EOF",
f11a948a
MS
2143 con->http.fd);
2144
f7deaa1a 2145 cupsdCloseClient(con);
2146 return;
2147 }
2148 else if (bytes > 0)
2149 {
2150 con->bytes += bytes;
ef416fc2 2151
f7deaa1a 2152 if (write(con->file, line, bytes) < bytes)
2153 {
2154 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c
MS
2155 "[Client %d] Unable to write %d bytes to "
2156 "\"%s\": %s", con->http.fd, bytes,
2157 con->filename, strerror(errno));
ef416fc2 2158
f7deaa1a 2159 close(con->file);
2160 con->file = -1;
2161 unlink(con->filename);
2162 cupsdClearString(&con->filename);
ef416fc2 2163
f11a948a
MS
2164 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE,
2165 CUPSD_AUTH_NONE))
f7deaa1a 2166 {
2167 cupsdCloseClient(con);
2168 return;
2169 }
2170 }
2171 }
2172 else if (con->http.state == HTTP_POST_RECV)
2173 return;
2174 else if (con->http.state != HTTP_POST_SEND)
2175 {
f11a948a 2176 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c
MS
2177 "[Client %d] Closing on unexpected state %s.",
2178 con->http.fd, http_states[con->http.state]);
f7deaa1a 2179 cupsdCloseClient(con);
2180 return;
ef416fc2 2181 }
2182 }
f7deaa1a 2183 }
ee571f26 2184 while (con->http.state == HTTP_POST_RECV && data_ready(con));
ef416fc2 2185
2186 if (con->http.state == HTTP_POST_SEND)
2187 {
2188 if (con->file >= 0)
2189 {
2190 fstat(con->file, &filestats);
2191
ef416fc2 2192 close(con->file);
2193 con->file = -1;
2194
2195 if (filestats.st_size > MaxRequestSize &&
2196 MaxRequestSize > 0)
2197 {
2198 /*
2199 * Request is too big; remove it and send an error...
2200 */
2201
ef416fc2 2202 unlink(con->filename);
2203 cupsdClearString(&con->filename);
2204
2205 if (con->request)
2206 {
2207 /*
2208 * Delete any IPP request data...
2209 */
2210
2211 ippDelete(con->request);
2212 con->request = NULL;
2213 }
2214
5bd77a73 2215 if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
f7deaa1a 2216 {
2217 cupsdCloseClient(con);
2218 return;
2219 }
ef416fc2 2220 }
f8b3a85b
MS
2221 else if (filestats.st_size == 0)
2222 {
2223 /*
2224 * Don't allow empty file...
2225 */
2226
2227 unlink(con->filename);
2228 cupsdClearString(&con->filename);
2229 }
ef416fc2 2230
2231 if (con->command)
2232 {
2233 if (!cupsdSendCommand(con, con->command, con->options, 0))
2234 {
5bd77a73 2235 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
f7deaa1a 2236 {
2237 cupsdCloseClient(con);
2238 return;
2239 }
ef416fc2 2240 }
2241 else
2242 cupsdLogRequest(con, HTTP_OK);
2243 }
2244 }
2245
2246 if (con->request)
f7deaa1a 2247 {
2248 cupsdProcessIPPRequest(con);
bc44d920 2249
2250 if (con->filename)
2251 {
bc44d920 2252 unlink(con->filename);
2253 cupsdClearString(&con->filename);
2254 }
2255
f7deaa1a 2256 return;
2257 }
ef416fc2 2258 }
2259 break;
2260
2261 default :
2262 break; /* Anti-compiler-warning-code */
2263 }
2264
3dfe78b3
MS
2265 if (con->http.state == HTTP_WAITING)
2266 {
2267 if (!con->http.keep_alive)
f11a948a
MS
2268 {
2269 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c 2270 "[Client %d] Closing because Keep-Alive disabled",
f11a948a 2271 con->http.fd);
3dfe78b3 2272 cupsdCloseClient(con);
f11a948a 2273 }
3dfe78b3
MS
2274 else
2275 {
2276 cupsArrayRemove(ActiveClients, con);
2277 cupsdSetBusyState();
2278 }
2279 }
ef416fc2 2280}
2281
2282
2283/*
2284 * 'cupsdSendCommand()' - Send output from a command via HTTP.
2285 */
2286
2287int /* O - 1 on success, 0 on failure */
2288cupsdSendCommand(
2289 cupsd_client_t *con, /* I - Client connection */
2290 char *command, /* I - Command to run */
2291 char *options, /* I - Command-line options */
2292 int root) /* I - Run as root? */
2293{
2294 int fd; /* Standard input file descriptor */
2295
2296
2297 if (con->filename)
ed486911 2298 {
ef416fc2 2299 fd = open(con->filename, O_RDONLY);
ef416fc2 2300
ed486911 2301 if (fd < 0)
2302 {
2303 cupsdLogMessage(CUPSD_LOG_ERROR,
85dda01c 2304 "[Client %d] Unable to open \"%s\" for reading: %s",
ed486911 2305 con->http.fd, con->filename ? con->filename : "/dev/null",
2306 strerror(errno));
2307 return (0);
2308 }
ef416fc2 2309
ed486911 2310 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
2311 }
2312 else
2313 fd = -1;
ef416fc2 2314
2315 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root);
2316
ed486911 2317 if (fd >= 0)
2318 close(fd);
ef416fc2 2319
85dda01c
MS
2320 cupsdLogMessage(CUPSD_LOG_INFO, "[Client %d] Started \"%s\" (pid=%d)",
2321 con->http.fd, command, con->pipe_pid);
ef416fc2 2322
85dda01c
MS
2323 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Client %d] file=%d", con->http.fd,
2324 con->file);
ef416fc2 2325
2326 if (con->pipe_pid == 0)
2327 return (0);
2328
2329 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2330
f899b121 2331 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
ef416fc2 2332
2333 con->sent_header = 0;
2334 con->file_ready = 0;
2335 con->got_fields = 0;
68b10830 2336 con->header_used = 0;
ef416fc2 2337
2338 return (1);
2339}
2340
2341
2342/*
2343 * 'cupsdSendError()' - Send an error message via HTTP.
2344 */
2345
2346int /* O - 1 if successful, 0 otherwise */
2347cupsdSendError(cupsd_client_t *con, /* I - Connection */
f899b121 2348 http_status_t code, /* I - Error code */
2349 int auth_type)/* I - Authentication type */
ef416fc2 2350{
b9faaae1 2351 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c 2352 "[Client %d] cupsdSendError code=%d, auth_type=%d",
b9faaae1
MS
2353 con->http.fd, code, auth_type);
2354
4744bd90 2355#ifdef HAVE_SSL
2356 /*
2357 * Force client to upgrade for authentication if that is how the
2358 * server is configured...
2359 */
2360
2361 if (code == HTTP_UNAUTHORIZED &&
2362 DefaultEncryption == HTTP_ENCRYPT_REQUIRED &&
88f9aafc 2363 _cups_strcasecmp(con->http.hostname, "localhost") &&
4744bd90 2364 !con->http.tls)
2365 {
4744bd90 2366 code = HTTP_UPGRADE_REQUIRED;
2367 }
2368#endif /* HAVE_SSL */
2369
ef416fc2 2370 /*
2371 * Put the request in the access_log file...
2372 */
2373
2374 cupsdLogRequest(con, code);
2375
ef416fc2 2376 /*
2377 * To work around bugs in some proxies, don't use Keep-Alive for some
2378 * error messages...
f7deaa1a 2379 *
2380 * Kerberos authentication doesn't work without Keep-Alive, so
2381 * never disable it in that case.
ef416fc2 2382 */
2383
5bd77a73 2384 if (code >= HTTP_BAD_REQUEST && con->http.auth_type != CUPSD_AUTH_NEGOTIATE)
ef416fc2 2385 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
2386
2387 /*
2388 * Send an error message back to the client. If the error code is a
2389 * 400 or 500 series, make sure the message contains some text, too!
2390 */
2391
f899b121 2392 if (!cupsdSendHeader(con, code, NULL, auth_type))
ef416fc2 2393 return (0);
2394
2395#ifdef HAVE_SSL
2396 if (code == HTTP_UPGRADE_REQUIRED)
2397 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
2398 return (0);
2399
2400 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0)
2401 return (0);
2402#endif /* HAVE_SSL */
2403
f7deaa1a 2404 if (con->http.version >= HTTP_1_1 &&
2405 con->http.keep_alive == HTTP_KEEPALIVE_OFF)
ef416fc2 2406 {
2407 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
2408 return (0);
2409 }
2410
2411 if (code >= HTTP_BAD_REQUEST)
2412 {
2413 /*
2414 * Send a human-readable error message.
2415 */
2416
80ca4592 2417 char message[4096], /* Message for user */
2418 urltext[1024], /* URL redirection text */
2419 redirect[1024]; /* Redirection link */
ef416fc2 2420 const char *text; /* Status-specific text */
2421
80ca4592 2422
2423 redirect[0] = '\0';
2424
ef416fc2 2425 if (code == HTTP_UNAUTHORIZED)
2426 text = _cupsLangString(con->language,
2427 _("Enter your username and password or the "
2428 "root username and password to access this "
f7deaa1a 2429 "page. If you are using Kerberos authentication, "
2430 "make sure you have a valid Kerberos ticket."));
ef416fc2 2431 else if (code == HTTP_UPGRADE_REQUIRED)
80ca4592 2432 {
2433 text = urltext;
2434
2435 snprintf(urltext, sizeof(urltext),
2436 _cupsLangString(con->language,
2437 _("You must access this page using the URL "
2438 "<A HREF=\"https://%s:%d%s\">"
2439 "https://%s:%d%s</A>.")),
2440 con->servername, con->serverport, con->uri,
2441 con->servername, con->serverport, con->uri);
2442
2443 snprintf(redirect, sizeof(redirect),
b86bc4cf 2444 "<META HTTP-EQUIV=\"Refresh\" "
2445 "CONTENT=\"3;URL=https://%s:%d%s\">\n",
80ca4592 2446 con->servername, con->serverport, con->uri);
2447 }
229681c1
MS
2448 else if (code == HTTP_WEBIF_DISABLED)
2449 text = _cupsLangString(con->language,
2450 _("The web interface is currently disabled. Run "
2451 "\"cupsctl WebInterface=yes\" to enable it."));
ef416fc2 2452 else
2453 text = "";
2454
2455 snprintf(message, sizeof(message),
745129be
MS
2456 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" "
2457 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
ef416fc2 2458 "<HTML>\n"
2459 "<HEAD>\n"
2460 "\t<META HTTP-EQUIV=\"Content-Type\" "
2461 "CONTENT=\"text/html; charset=utf-8\">\n"
229681c1 2462 "\t<TITLE>%s - " CUPS_SVERSION "</TITLE>\n"
ef416fc2 2463 "\t<LINK REL=\"STYLESHEET\" TYPE=\"text/css\" "
2464 "HREF=\"/cups.css\">\n"
80ca4592 2465 "%s"
ef416fc2 2466 "</HEAD>\n"
2467 "<BODY>\n"
229681c1 2468 "<H1>%s</H1>\n"
ef416fc2 2469 "<P>%s</P>\n"
2470 "</BODY>\n"
2471 "</HTML>\n",
229681c1 2472 httpStatus(code), redirect, httpStatus(code), text);
ef416fc2 2473
2474 if (httpPrintf(HTTP(con), "Content-Type: text/html; charset=utf-8\r\n") < 0)
2475 return (0);
2476 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
2477 (int)strlen(message)) < 0)
2478 return (0);
2479 if (httpPrintf(HTTP(con), "\r\n") < 0)
2480 return (0);
2481 if (httpPrintf(HTTP(con), "%s", message) < 0)
2482 return (0);
2483 }
2484 else if (httpPrintf(HTTP(con), "\r\n") < 0)
2485 return (0);
2486
07725fee 2487 if (cupsdFlushHeader(con) < 0)
2488 return (0);
d09495fa 2489
ef416fc2 2490 con->http.state = HTTP_WAITING;
2491
2492 return (1);
2493}
2494
2495
ef416fc2 2496/*
2497 * 'cupsdSendHeader()' - Send an HTTP request.
2498 */
2499
2500int /* O - 1 on success, 0 on failure */
f899b121 2501cupsdSendHeader(
2502 cupsd_client_t *con, /* I - Client to send to */
2503 http_status_t code, /* I - HTTP status code */
2504 char *type, /* I - MIME type of document */
2505 int auth_type) /* I - Type of authentication */
ef416fc2 2506{
5a738aea 2507 char auth_str[1024]; /* Authorization string */
f7deaa1a 2508
2509
4744bd90 2510 /*
2511 * Send the HTTP status header...
2512 */
2513
b423cd4c 2514 if (code == HTTP_CONTINUE)
2515 {
4744bd90 2516 /*
2517 * 100-continue doesn't send any headers...
2518 */
2519
07725fee 2520 return (httpPrintf(HTTP(con), "HTTP/%d.%d 100 Continue\r\n\r\n",
2521 con->http.version / 100, con->http.version % 100) > 0);
b423cd4c 2522 }
229681c1
MS
2523 else if (code == HTTP_WEBIF_DISABLED)
2524 {
2525 /*
2526 * Treat our special "web interface is disabled" status as "200 OK" for web
2527 * browsers.
2528 */
2529
2530 code = HTTP_OK;
2531 }
b423cd4c 2532
07725fee 2533 httpFlushWrite(HTTP(con));
2534
2535 con->http.data_encoding = HTTP_ENCODE_FIELDS;
2536
2537 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
2538 con->http.version % 100, code, httpStatus(code)) < 0)
2539 return (0);
ef416fc2 2540 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
2541 return (0);
2542 if (ServerHeader)
2543 if (httpPrintf(HTTP(con), "Server: %s\r\n", ServerHeader) < 0)
2544 return (0);
2545 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
2546 {
2547 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
2548 return (0);
2549 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n",
2550 KeepAliveTimeout) < 0)
2551 return (0);
2552 }
2553 if (code == HTTP_METHOD_NOT_ALLOWED)
e6013cfa 2554 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n") < 0)
ef416fc2 2555 return (0);
2556
2557 if (code == HTTP_UNAUTHORIZED)
2558 {
5bd77a73 2559 if (auth_type == CUPSD_AUTH_NONE)
f899b121 2560 {
5bd77a73 2561 if (!con->best || con->best->type <= CUPSD_AUTH_NONE)
dcb445bc 2562 auth_type = cupsdDefaultAuthType();
f899b121 2563 else
2564 auth_type = con->best->type;
2565 }
f7deaa1a 2566
2567 auth_str[0] = '\0';
2568
5bd77a73 2569 if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
f7deaa1a 2570 strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
5bd77a73 2571 else if (auth_type == CUPSD_AUTH_DIGEST)
f7deaa1a 2572 snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"",
2573 con->http.hostname);
2574#ifdef HAVE_GSSAPI
eac3a0a0
MS
2575 else if (auth_type == CUPSD_AUTH_NEGOTIATE)
2576 {
2577# ifdef AF_LOCAL
2578 if (_httpAddrFamily(con->http.hostaddr) == AF_LOCAL)
2579 strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
2580 else
2581# endif /* AF_LOCAL */
f7deaa1a 2582 strlcpy(auth_str, "Negotiate", sizeof(auth_str));
eac3a0a0 2583 }
f7deaa1a 2584#endif /* HAVE_GSSAPI */
2585
e07d4801 2586 if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE &&
88f9aafc 2587 !_cups_strcasecmp(con->http.hostname, "localhost"))
ef416fc2 2588 {
e07d4801
MS
2589 /*
2590 * Add a "trc" (try root certification) parameter for local non-Kerberos
2591 * requests when the request requires system group membership - then the
2592 * client knows the root certificate can/should be used.
2593 *
f3c17241 2594 * Also, for OS X we also look for @AUTHKEY and add an "authkey"
e07d4801
MS
2595 * parameter as needed...
2596 */
2597
10d09e33
MS
2598 char *name, /* Current user name */
2599 *auth_key; /* Auth key buffer */
f7deaa1a 2600 size_t auth_size; /* Size of remaining buffer */
2601
f7deaa1a 2602 auth_key = auth_str + strlen(auth_str);
2603 auth_size = sizeof(auth_str) - (auth_key - auth_str);
2604
10d09e33
MS
2605 for (name = (char *)cupsArrayFirst(con->best->names);
2606 name;
2607 name = (char *)cupsArrayNext(con->best->names))
f7deaa1a 2608 {
e07d4801 2609#ifdef HAVE_AUTHORIZATION_H
88f9aafc 2610 if (!_cups_strncasecmp(name, "@AUTHKEY(", 9))
f7deaa1a 2611 {
10d09e33 2612 snprintf(auth_key, auth_size, ", authkey=\"%s\"", name + 9);
f7deaa1a 2613 /* end parenthesis is stripped in conf.c */
2614 break;
2615 }
e07d4801
MS
2616 else
2617#endif /* HAVE_AUTHORIZATION_H */
88f9aafc 2618 if (!_cups_strcasecmp(name, "@SYSTEM"))
f7deaa1a 2619 {
e07d4801
MS
2620#ifdef HAVE_AUTHORIZATION_H
2621 if (SystemGroupAuthKey)
2622 snprintf(auth_key, auth_size,
07ed0e9a 2623 ", authkey=\"%s\"",
e07d4801
MS
2624 SystemGroupAuthKey);
2625 else
2626#else
f11a948a 2627 strlcpy(auth_key, ", trc=\"y\"", auth_size);
e07d4801 2628#endif /* HAVE_AUTHORIZATION_H */
f7deaa1a 2629 break;
2630 }
2631 }
ef416fc2 2632 }
f7deaa1a 2633
bc44d920 2634 if (auth_str[0])
2635 {
68b10830 2636 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c 2637 "[Client %d] WWW-Authenticate: %s", con->http.fd,
bc44d920 2638 auth_str);
2639
2640 if (httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0)
2641 return (0);
2642 }
ef416fc2 2643 }
2644
e1d6a774 2645 if (con->language && strcmp(con->language->language, "C"))
ef416fc2 2646 {
2647 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
2648 con->language->language) < 0)
2649 return (0);
2650 }
2651
e1d6a774 2652 if (type)
ef416fc2 2653 {
2654 if (!strcmp(type, "text/html"))
2655 {
2656 if (httpPrintf(HTTP(con),
2657 "Content-Type: text/html; charset=utf-8\r\n") < 0)
2658 return (0);
2659 }
2660 else if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
2661 return (0);
2662 }
2663
2664 return (1);
2665}
2666
2667
2668/*
2669 * 'cupsdUpdateCGI()' - Read status messages from CGI scripts and programs.
2670 */
2671
2672void
2673cupsdUpdateCGI(void)
2674{
2675 char *ptr, /* Pointer to end of line in buffer */
2676 message[1024]; /* Pointer to message text */
2677 int loglevel; /* Log level for message */
2678
2679
2680 while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel,
2681 message, sizeof(message))) != NULL)
f0ab5bff
MS
2682 {
2683 if (loglevel == CUPSD_LOG_INFO)
2684 cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
2685
ef416fc2 2686 if (!strchr(CGIStatusBuffer->buffer, '\n'))
2687 break;
f0ab5bff 2688 }
ef416fc2 2689
d09495fa 2690 if (ptr == NULL && !CGIStatusBuffer->bufused)
ef416fc2 2691 {
2692 /*
2693 * Fatal error on pipe - should never happen!
2694 */
2695
2696 cupsdLogMessage(CUPSD_LOG_CRIT,
2697 "cupsdUpdateCGI: error reading from CGI error pipe - %s",
2698 strerror(errno));
2699 }
2700}
2701
2702
2703/*
2704 * 'cupsdWriteClient()' - Write data to a client as needed.
2705 */
2706
f7deaa1a 2707void
ef416fc2 2708cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */
2709{
68b10830
MS
2710 int bytes, /* Number of bytes written */
2711 field_col; /* Current column */
2712 char *bufptr, /* Pointer into buffer */
2713 *bufend; /* Pointer to end of buffer */
ef416fc2 2714 ipp_state_t ipp_state; /* IPP state value */
2715
2716
ef416fc2 2717 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
2718 "[Client %d] cupsdWriteClient "
2719 "error=%d, "
2720 "used=%d, "
2721 "state=%s, "
2722 "data_encoding=HTTP_ENCODE_%s, "
2723 "data_remaining=" CUPS_LLFMT ", "
2724 "response=%p(%s), "
2725 "pipe_pid=%d, "
2726 "file=%d",
2727 con->http.fd, con->http.error, con->http.used,
2728 http_states[con->http.state],
2729 con->http.data_encoding == HTTP_ENCODE_CHUNKED ?
2730 "CHUNKED" : "LENGTH",
2731 CUPS_LLCAST con->http.data_remaining,
2732 con->response,
2733 con->response ? ipp_states[con->response->state] : "",
2734 con->pipe_pid, con->file);
ef416fc2 2735
2736 if (con->http.state != HTTP_GET_SEND &&
2737 con->http.state != HTTP_POST_SEND)
8b116e60
MS
2738 {
2739 /*
2740 * If we get called in the wrong state, then something went wrong with the
2741 * connection and we need to shut it down...
2742 */
2743
f11a948a 2744 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c
MS
2745 "[Client %d] Closing on unexpected HTTP state %s.",
2746 con->http.fd, http_states[con->http.state]);
8b116e60 2747 cupsdCloseClient(con);
f7deaa1a 2748 return;
8b116e60 2749 }
f7deaa1a 2750
2751 if (con->pipe_pid)
2752 {
2753 /*
2754 * Make sure we select on the CGI output...
2755 */
2756
f899b121 2757 cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
f7deaa1a 2758
2759 if (!con->file_ready)
2760 {
2761 /*
2762 * Try again later when there is CGI output available...
2763 */
2764
2765 cupsdRemoveSelect(con->http.fd);
2766 return;
2767 }
2768
2769 con->file_ready = 0;
2770 }
ef416fc2 2771
b94498cf 2772 if (con->response && con->response->state != IPP_DATA)
ef416fc2 2773 {
07725fee 2774 ipp_state = ippWrite(HTTP(con), con->response);
b94498cf 2775 bytes = ipp_state != IPP_ERROR &&
2776 (con->file >= 0 || ipp_state != IPP_DATA);
ef416fc2 2777 }
a2326b5b 2778 else if ((bytes = read(con->file, con->header + con->header_used,
68b10830 2779 sizeof(con->header) - con->header_used)) > 0)
ef416fc2 2780 {
68b10830
MS
2781 con->header_used += bytes;
2782
ef416fc2 2783 if (con->pipe_pid && !con->got_fields)
2784 {
2785 /*
2786 * Inspect the data for Content-Type and other fields.
2787 */
2788
68b10830
MS
2789 for (bufptr = con->header, bufend = con->header + con->header_used,
2790 field_col = 0;
2791 !con->got_fields && bufptr < bufend;
2792 bufptr ++)
d1c13e16 2793 {
ef416fc2 2794 if (*bufptr == '\n')
2795 {
2796 /*
2797 * Send line to client...
2798 */
2799
68b10830 2800 if (bufptr > con->header && bufptr[-1] == '\r')
ef416fc2 2801 bufptr[-1] = '\0';
2802 *bufptr++ = '\0';
2803
68b10830 2804 cupsdLogMessage(CUPSD_LOG_DEBUG, "Script header: %s", con->header);
ef416fc2 2805
2806 if (!con->sent_header)
2807 {
2808 /*
2809 * Handle redirection and CGI status codes...
2810 */
2811
88f9aafc 2812 if (!_cups_strncasecmp(con->header, "Location:", 9))
d6ae789d 2813 {
321d8d57
MS
2814 if (!cupsdSendHeader(con, HTTP_SEE_OTHER, NULL, CUPSD_AUTH_NONE))
2815 {
2816 cupsdCloseClient(con);
2817 return;
2818 }
2819
07725fee 2820 con->sent_header = 2;
2821
d6ae789d 2822 if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0)
f7deaa1a 2823 return;
d6ae789d 2824 }
88f9aafc 2825 else if (!_cups_strncasecmp(con->header, "Status:", 7))
07725fee 2826 {
68b10830
MS
2827 cupsdSendError(con, (http_status_t)atoi(con->header + 7),
2828 CUPSD_AUTH_NONE);
07725fee 2829 con->sent_header = 2;
2830 }
ef416fc2 2831 else
4744bd90 2832 {
321d8d57
MS
2833 if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE))
2834 {
2835 cupsdCloseClient(con);
2836 return;
2837 }
2838
07725fee 2839 con->sent_header = 1;
ef416fc2 2840
4744bd90 2841 if (con->http.version == HTTP_1_1)
2842 {
4744bd90 2843 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
f7deaa1a 2844 return;
4744bd90 2845 }
2846 }
ef416fc2 2847 }
2848
88f9aafc 2849 if (_cups_strncasecmp(con->header, "Status:", 7))
68b10830 2850 httpPrintf(HTTP(con), "%s\r\n", con->header);
ef416fc2 2851
ef416fc2 2852 /*
2853 * Update buffer...
2854 */
2855
68b10830 2856 con->header_used -= bufptr - con->header;
d1c13e16 2857
68b10830
MS
2858 if (con->header_used > 0)
2859 memmove(con->header, bufptr, con->header_used);
d1c13e16 2860
68b10830 2861 bufptr = con->header - 1;
ef416fc2 2862
2863 /*
2864 * See if the line was empty...
2865 */
2866
68b10830 2867 if (field_col == 0)
d09495fa 2868 {
ef416fc2 2869 con->got_fields = 1;
d09495fa 2870
07725fee 2871 if (cupsdFlushHeader(con) < 0)
2872 {
2873 cupsdCloseClient(con);
f7deaa1a 2874 return;
07725fee 2875 }
d09495fa 2876
2877 if (con->http.version == HTTP_1_1)
2878 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
2879 }
ef416fc2 2880 else
68b10830 2881 field_col = 0;
ef416fc2 2882 }
2883 else if (*bufptr != '\r')
68b10830 2884 field_col ++;
d1c13e16 2885 }
ef416fc2 2886
68b10830 2887 if (!con->got_fields)
47879b8b 2888 {
ef416fc2 2889 con->http.activity = time(NULL);
47879b8b
MS
2890 return;
2891 }
ef416fc2 2892 }
2893
68b10830 2894 if (con->header_used > 0)
ef416fc2 2895 {
68b10830 2896 if (httpWrite2(HTTP(con), con->header, con->header_used) < 0)
4744bd90 2897 {
f11a948a 2898 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c 2899 "[Client %d] Closing for error %d (%s)",
f11a948a
MS
2900 con->http.fd, con->http.error,
2901 strerror(con->http.error));
4744bd90 2902 cupsdCloseClient(con);
f7deaa1a 2903 return;
4744bd90 2904 }
ef416fc2 2905
01ce6322
MS
2906 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
2907 httpFlushWrite(HTTP(con));
ae71f5de 2908
68b10830 2909 con->bytes += con->header_used;
ef416fc2 2910
4744bd90 2911 if (con->http.state == HTTP_WAITING)
2912 bytes = 0;
68b10830
MS
2913 else
2914 bytes = con->header_used;
2915
2916 con->header_used = 0;
4744bd90 2917 }
ef416fc2 2918 }
2919
8b116e60
MS
2920 if (bytes <= 0 ||
2921 (con->http.state != HTTP_GET_SEND && con->http.state != HTTP_POST_SEND))
ef416fc2 2922 {
38e73f87 2923 if (!con->sent_header && con->pipe_pid)
94da7e34
MS
2924 cupsdSendError(con, HTTP_SERVER_ERROR, CUPSD_AUTH_NONE);
2925 else
2926 {
2927 cupsdLogRequest(con, HTTP_OK);
ef416fc2 2928
94da7e34 2929 httpFlushWrite(HTTP(con));
ef416fc2 2930
94da7e34 2931 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED && con->sent_header == 1)
ef416fc2 2932 {
94da7e34
MS
2933 if (httpWrite2(HTTP(con), "", 0) < 0)
2934 {
f11a948a 2935 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c 2936 "[Client %d] Closing for error %d (%s)",
f11a948a
MS
2937 con->http.fd, con->http.error,
2938 strerror(con->http.error));
94da7e34
MS
2939 cupsdCloseClient(con);
2940 return;
2941 }
ef416fc2 2942 }
2943 }
2944
2945 con->http.state = HTTP_WAITING;
2946
f7deaa1a 2947 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
ef416fc2 2948
2949 if (con->file >= 0)
2950 {
f7deaa1a 2951 cupsdRemoveSelect(con->file);
ef416fc2 2952
2953 if (con->pipe_pid)
2954 cupsdEndProcess(con->pipe_pid, 0);
2955
ef416fc2 2956 close(con->file);
2957 con->file = -1;
2958 con->pipe_pid = 0;
2959 }
2960
2961 if (con->filename)
2962 {
ef416fc2 2963 unlink(con->filename);
2964 cupsdClearString(&con->filename);
2965 }
2966
2abf387c 2967 if (con->request)
ef416fc2 2968 {
2969 ippDelete(con->request);
2970 con->request = NULL;
2971 }
2972
2abf387c 2973 if (con->response)
ef416fc2 2974 {
2975 ippDelete(con->response);
2976 con->response = NULL;
2977 }
2978
2979 cupsdClearString(&con->command);
2980 cupsdClearString(&con->options);
b86bc4cf 2981 cupsdClearString(&con->query_string);
ef416fc2 2982
2983 if (!con->http.keep_alive)
2984 {
f11a948a 2985 cupsdLogMessage(CUPSD_LOG_DEBUG,
85dda01c 2986 "[Client %d] Closing because Keep-Alive disabled.",
f11a948a 2987 con->http.fd);
ef416fc2 2988 cupsdCloseClient(con);
f7deaa1a 2989 return;
ef416fc2 2990 }
f11a948a
MS
2991 else
2992 {
2993 cupsArrayRemove(ActiveClients, con);
2994 cupsdSetBusyState();
2995 }
ef416fc2 2996 }
2997
2998 con->http.activity = time(NULL);
f7deaa1a 2999}
ef416fc2 3000
f7deaa1a 3001
e1d6a774 3002/*
3003 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
3004 */
3005
3006static int /* O - 1 if modified since */
3007check_if_modified(
3008 cupsd_client_t *con, /* I - Client connection */
3009 struct stat *filestats) /* I - File information */
3010{
3011 char *ptr; /* Pointer into field */
3012 time_t date; /* Time/date value */
3013 off_t size; /* Size/length value */
3014
3015
3016 size = 0;
3017 date = 0;
3018 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
3019
3020 if (*ptr == '\0')
3021 return (1);
3022
3023 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c 3024 "[Client %d] check_if_modified "
b9faaae1 3025 "filestats=%p(" CUPS_LLFMT ", %d)) If-Modified-Since=\"%s\"",
85dda01c 3026 con->http.fd, filestats, CUPS_LLCAST filestats->st_size,
b9faaae1 3027 (int)filestats->st_mtime, ptr);
e1d6a774 3028
3029 while (*ptr != '\0')
3030 {
3031 while (isspace(*ptr) || *ptr == ';')
3032 ptr ++;
3033
88f9aafc 3034 if (_cups_strncasecmp(ptr, "length=", 7) == 0)
e1d6a774 3035 {
3036 ptr += 7;
3037 size = strtoll(ptr, NULL, 10);
3038
3039 while (isdigit(*ptr))
3040 ptr ++;
3041 }
3042 else if (isalpha(*ptr))
3043 {
3044 date = httpGetDateTime(ptr);
3045 while (*ptr != '\0' && *ptr != ';')
3046 ptr ++;
3047 }
e53920b9 3048 else
3049 ptr ++;
e1d6a774 3050 }
3051
e1d6a774 3052 return ((size != filestats->st_size && size != 0) ||
3053 (date < filestats->st_mtime && date != 0) ||
3054 (size == 0 && date == 0));
3055}
3056
3057
3dfe78b3
MS
3058/*
3059 * 'compare_clients()' - Compare two client connections.
3060 */
3061
3062static int /* O - Result of comparison */
3063compare_clients(cupsd_client_t *a, /* I - First client */
3064 cupsd_client_t *b, /* I - Second client */
3065 void *data) /* I - User data (not used) */
3066{
3067 (void)data;
3068
3069 if (a == b)
3070 return (0);
3071 else if (a < b)
3072 return (-1);
3073 else
3074 return (1);
3075}
3076
3077
85dda01c
MS
3078/*
3079 * 'data_ready()' - Check whether data is available from a client.
3080 */
ef416fc2 3081
85dda01c
MS
3082static int /* O - 1 if data is ready, 0 otherwise */
3083data_ready(cupsd_client_t *con) /* I - Client */
3084{
3085 if (con->http.used > 0)
3086 return (1);
3087#ifdef HAVE_SSL
3088 else if (con->http.tls)
e1d6a774 3089 {
85dda01c
MS
3090# ifdef HAVE_LIBSSL
3091 if (SSL_pending((SSL *)(con->http.tls)))
3092 return (1);
3093# elif defined(HAVE_GNUTLS)
3094 if (gnutls_record_check_pending(con->http.tls))
3095 return (1);
3096# elif defined(HAVE_CDSASSL)
3097 size_t bytes; /* Bytes that are available */
ef416fc2 3098
85dda01c
MS
3099 if (!SSLGetBufferedReadSize(con->http.tls, &bytes) && bytes > 0)
3100 return (1);
3101# endif /* HAVE_LIBSSL */
e1d6a774 3102 }
85dda01c 3103#endif /* HAVE_SSL */
ef416fc2 3104
85dda01c
MS
3105 return (0);
3106}
ef416fc2 3107
ef416fc2 3108
ef416fc2 3109/*
3110 * 'get_file()' - Get a filename and state info.
3111 */
3112
3113static char * /* O - Real filename */
3114get_file(cupsd_client_t *con, /* I - Client connection */
3115 struct stat *filestats, /* O - File information */
3116 char *filename, /* IO - Filename buffer */
3117 int len) /* I - Buffer length */
3118{
3119 int status; /* Status of filesystem calls */
3120 char *ptr; /* Pointer info filename */
3121 int plen; /* Remaining length after pointer */
db1f069b 3122 char language[7]; /* Language subdirectory, if any */
ef416fc2 3123
3124
3125 /*
f7deaa1a 3126 * Figure out the real filename...
ef416fc2 3127 */
3128
db1f069b
MS
3129 language[0] = '\0';
3130
7cf5915e 3131 if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/'))
ef416fc2 3132 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
7cf5915e
MS
3133 else if (!strncmp(con->uri, "/icons/", 7) && !strchr(con->uri + 7, '/'))
3134 {
3135 snprintf(filename, len, "%s/%s", CacheDir, con->uri + 7);
3136 if (access(filename, F_OK) < 0)
3137 snprintf(filename, len, "%s/images/generic.png", DocumentRoot);
3138 }
f7deaa1a 3139 else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/'))
3140 snprintf(filename, len, "%s/rss/%s", CacheDir, con->uri + 5);
ef416fc2 3141 else if (!strncmp(con->uri, "/admin/conf/", 12))
3142 snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
3143 else if (!strncmp(con->uri, "/admin/log/", 11))
3144 {
2e4ff8af 3145 if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/')
ef416fc2 3146 strlcpy(filename, AccessLog, len);
2e4ff8af 3147 else if (!strncmp(con->uri + 11, "error_log", 9) && ErrorLog[0] == '/')
ef416fc2 3148 strlcpy(filename, ErrorLog, len);
2e4ff8af 3149 else if (!strncmp(con->uri + 11, "page_log", 8) && PageLog[0] == '/')
ef416fc2 3150 strlcpy(filename, PageLog, len);
3151 else
3152 return (NULL);
3153 }
d6ae789d 3154 else if (con->language)
db1f069b
MS
3155 {
3156 snprintf(language, sizeof(language), "/%s", con->language->language);
3157 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3158 }
ef416fc2 3159 else
3160 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
3161
3162 if ((ptr = strchr(filename, '?')) != NULL)
3163 *ptr = '\0';
3164
3165 /*
3166 * Grab the status for this language; if there isn't a language-specific file
3167 * then fallback to the default one...
3168 */
3169
db1f069b 3170 if ((status = stat(filename, filestats)) != 0 && language[0] &&
7cf5915e 3171 strncmp(con->uri, "/icons/", 7) &&
d6ae789d 3172 strncmp(con->uri, "/ppd/", 5) &&
7cf5915e 3173 strncmp(con->uri, "/rss/", 5) &&
d6ae789d 3174 strncmp(con->uri, "/admin/conf/", 12) &&
3175 strncmp(con->uri, "/admin/log/", 11))
ef416fc2 3176 {
3177 /*
d6ae789d 3178 * Drop the country code...
ef416fc2 3179 */
3180
db1f069b
MS
3181 language[3] = '\0';
3182 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
d6ae789d 3183
3184 if ((ptr = strchr(filename, '?')) != NULL)
3185 *ptr = '\0';
3186
3187 if ((status = stat(filename, filestats)) != 0)
ef416fc2 3188 {
d6ae789d 3189 /*
3190 * Drop the language prefix and try the root directory...
3191 */
3192
db1f069b 3193 language[0] = '\0';
ef416fc2 3194 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
3195
3196 if ((ptr = strchr(filename, '?')) != NULL)
3197 *ptr = '\0';
3198
3199 status = stat(filename, filestats);
3200 }
3201 }
3202
3203 /*
3204 * If we're found a directory, get the index.html file instead...
3205 */
3206
3207 if (!status && S_ISDIR(filestats->st_mode))
3208 {
db1f069b
MS
3209 /*
3210 * Make sure the URI ends with a slash...
3211 */
ef416fc2 3212
db1f069b
MS
3213 if (con->uri[strlen(con->uri) - 1] != '/')
3214 strlcat(con->uri, "/", sizeof(con->uri));
ef416fc2 3215
db1f069b
MS
3216 /*
3217 * Find the directory index file, trying every language...
3218 */
ef416fc2 3219
db1f069b 3220 do
ef416fc2 3221 {
db1f069b
MS
3222 if (status && language[0])
3223 {
3224 /*
3225 * Try a different language subset...
3226 */
3227
3228 if (language[3])
3229 language[0] = '\0'; /* Strip country code */
3230 else
3231 language[0] = '\0'; /* Strip language */
3232 }
3233
3234 /*
3235 * Look for the index file...
3236 */
3237
3238 snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
3239
3240 if ((ptr = strchr(filename, '?')) != NULL)
3241 *ptr = '\0';
3242
3243 ptr = filename + strlen(filename);
3244 plen = len - (ptr - filename);
3245
3246 strlcpy(ptr, "index.html", plen);
ef416fc2 3247 status = stat(filename, filestats);
db1f069b
MS
3248
3249#ifdef HAVE_JAVA
3250 if (status)
3251 {
3252 strlcpy(ptr, "index.class", plen);
3253 status = stat(filename, filestats);
3254 }
ef416fc2 3255#endif /* HAVE_JAVA */
3256
3257#ifdef HAVE_PERL
db1f069b
MS
3258 if (status)
3259 {
3260 strlcpy(ptr, "index.pl", plen);
3261 status = stat(filename, filestats);
3262 }
ef416fc2 3263#endif /* HAVE_PERL */
3264
3265#ifdef HAVE_PHP
db1f069b
MS
3266 if (status)
3267 {
3268 strlcpy(ptr, "index.php", plen);
3269 status = stat(filename, filestats);
3270 }
ef416fc2 3271#endif /* HAVE_PHP */
3272
3273#ifdef HAVE_PYTHON
db1f069b
MS
3274 if (status)
3275 {
3276 strlcpy(ptr, "index.pyc", plen);
3277 status = stat(filename, filestats);
3278 }
ef416fc2 3279
db1f069b
MS
3280 if (status)
3281 {
3282 strlcpy(ptr, "index.py", plen);
3283 status = stat(filename, filestats);
3284 }
ef416fc2 3285#endif /* HAVE_PYTHON */
db1f069b
MS
3286
3287 }
3288 while (status && language[0]);
ef416fc2 3289 }
3290
b9faaae1 3291 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
3292 "[Client %d] get_file filestats=%p, filename=%p, len=%d, "
3293 "returning \"%s\".", con->http.fd, filestats, filename, len,
b9faaae1 3294 status ? "(null)" : filename);
ef416fc2 3295
ef416fc2 3296 if (status)
3297 return (NULL);
3298 else
3299 return (filename);
3300}
3301
3302
3303/*
3304 * 'install_conf_file()' - Install a configuration file.
3305 */
3306
3307static http_status_t /* O - Status */
3308install_conf_file(cupsd_client_t *con) /* I - Connection */
3309{
321d8d57
MS
3310 char filename[1024]; /* Configuration filename */
3311 mode_t mode; /* Permissions */
ef416fc2 3312 cups_file_t *in, /* Input file */
3313 *out; /* Output file */
321d8d57
MS
3314 char buffer[16384]; /* Copy buffer */
3315 ssize_t bytes; /* Number of bytes */
ef416fc2 3316
3317
3318 /*
321d8d57 3319 * Open the request file...
ef416fc2 3320 */
3321
321d8d57 3322 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
ef416fc2 3323 {
321d8d57
MS
3324 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open request file \"%s\": %s",
3325 con->filename, strerror(errno));
3326 return (HTTP_SERVER_ERROR);
ef416fc2 3327 }
3328
3329 /*
321d8d57 3330 * Open the new config file...
ef416fc2 3331 */
3332
321d8d57
MS
3333 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri + 11);
3334 if (!strcmp(con->uri, "/admin/conf/printers.conf"))
3335 mode = ConfigFilePerm & 0600;
3336 else
3337 mode = ConfigFilePerm;
ef416fc2 3338
321d8d57 3339 if ((out = cupsdCreateConfFile(filename, mode)) == NULL)
ef416fc2 3340 {
3341 cupsFileClose(in);
ef416fc2 3342 return (HTTP_SERVER_ERROR);
3343 }
3344
321d8d57 3345 cupsdLogMessage(CUPSD_LOG_INFO, "Installing config file \"%s\"...", filename);
ef416fc2 3346
3347 /*
3348 * Copy from the request to the new config file...
3349 */
3350
3351 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
3352 if (cupsFileWrite(out, buffer, bytes) < bytes)
3353 {
3354 cupsdLogMessage(CUPSD_LOG_ERROR,
321d8d57
MS
3355 "Unable to copy to config file \"%s\": %s",
3356 filename, strerror(errno));
ef416fc2 3357
3358 cupsFileClose(in);
3359 cupsFileClose(out);
321d8d57
MS
3360
3361 snprintf(filename, sizeof(filename), "%s%s.N", ServerRoot, con->uri + 11);
3362 cupsdRemoveFile(filename);
ef416fc2 3363
3364 return (HTTP_SERVER_ERROR);
3365 }
3366
3367 /*
3368 * Close the files...
3369 */
3370
3371 cupsFileClose(in);
ef416fc2 3372
321d8d57 3373 if (cupsdCloseCreatedConfFile(out, filename))
ef416fc2 3374 return (HTTP_SERVER_ERROR);
ef416fc2 3375
3376 /*
3377 * Remove the request file...
3378 */
3379
321d8d57 3380 cupsdRemoveFile(con->filename);
ef416fc2 3381 cupsdClearString(&con->filename);
3382
ef416fc2 3383 /*
3384 * If the cupsd.conf file was updated, set the NeedReload flag...
3385 */
3386
3387 if (!strcmp(con->uri, "/admin/conf/cupsd.conf"))
3388 NeedReload = RELOAD_CUPSD;
3389 else
3390 NeedReload = RELOAD_ALL;
3391
3392 ReloadTime = time(NULL);
3393
3394 /*
3395 * Return that the file was created successfully...
3396 */
3397
3398 return (HTTP_CREATED);
3399}
3400
3401
e1d6a774 3402/*
3403 * 'is_cgi()' - Is the resource a CGI script/program?
3404 */
3405
3406static int /* O - 1 = CGI, 0 = file */
3407is_cgi(cupsd_client_t *con, /* I - Client connection */
3408 const char *filename, /* I - Real filename */
3409 struct stat *filestats, /* I - File information */
3410 mime_type_t *type) /* I - MIME type */
3411{
3412 const char *options; /* Options on URL */
3413
3414
e1d6a774 3415 /*
3416 * Get the options, if any...
3417 */
3418
3419 if ((options = strchr(con->uri, '?')) != NULL)
a0f6818e
MS
3420 {
3421 options ++;
3422 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", options);
3423 }
b86bc4cf 3424
e1d6a774 3425 /*
3426 * Check for known types...
3427 */
3428
88f9aafc 3429 if (!type || _cups_strcasecmp(type->super, "application"))
e1d6a774 3430 {
b9faaae1 3431 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
3432 "[Client %d] is_cgi filename=\"%s\", filestats=%p, "
3433 "type=%s/%s, returning 0", con->http.fd, filename,
3434 filestats, type ? type->super : "unknown",
b9faaae1 3435 type ? type->type : "unknown");
e1d6a774 3436 return (0);
3437 }
3438
88f9aafc 3439 if (!_cups_strcasecmp(type->type, "x-httpd-cgi") &&
e1d6a774 3440 (filestats->st_mode & 0111))
3441 {
3442 /*
3443 * "application/x-httpd-cgi" is a CGI script.
3444 */
3445
3446 cupsdSetString(&con->command, filename);
3447
a0f6818e
MS
3448 if (options)
3449 cupsdSetStringf(&con->options, " %s", options);
e1d6a774 3450
3451 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
3452 "[Client %d] is_cgi filename=\"%s\", filestats=%p, "
3453 "type=%s/%s, returning 1", con->http.fd, filename,
3454 filestats, type->super, type->type);
e1d6a774 3455 return (1);
3456 }
3457#ifdef HAVE_JAVA
88f9aafc 3458 else if (!_cups_strcasecmp(type->type, "x-httpd-java"))
e1d6a774 3459 {
3460 /*
3461 * "application/x-httpd-java" is a Java servlet.
3462 */
3463
3464 cupsdSetString(&con->command, CUPS_JAVA);
3465
3466 if (options)
b94498cf 3467 cupsdSetStringf(&con->options, " %s %s", filename, options);
e1d6a774 3468 else
b94498cf 3469 cupsdSetStringf(&con->options, " %s", filename);
e1d6a774 3470
3471 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
3472 "[Client %d] is_cgi filename=\"%s\", filestats=%p, "
3473 "type=%s/%s, returning 1", con->http.fd, filename,
3474 filestats, type->super, type->type);
e1d6a774 3475 return (1);
3476 }
3477#endif /* HAVE_JAVA */
3478#ifdef HAVE_PERL
88f9aafc 3479 else if (!_cups_strcasecmp(type->type, "x-httpd-perl"))
e1d6a774 3480 {
3481 /*
3482 * "application/x-httpd-perl" is a Perl page.
3483 */
3484
3485 cupsdSetString(&con->command, CUPS_PERL);
3486
3487 if (options)
b94498cf 3488 cupsdSetStringf(&con->options, " %s %s", filename, options);
e1d6a774 3489 else
b94498cf 3490 cupsdSetStringf(&con->options, " %s", filename);
e1d6a774 3491
3492 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
3493 "[Client %d] is_cgi filename=\"%s\", filestats=%p, "
3494 "type=%s/%s, returning 1", con->http.fd, filename,
3495 filestats, type->super, type->type);
e1d6a774 3496 return (1);
3497 }
3498#endif /* HAVE_PERL */
3499#ifdef HAVE_PHP
88f9aafc 3500 else if (!_cups_strcasecmp(type->type, "x-httpd-php"))
e1d6a774 3501 {
3502 /*
3503 * "application/x-httpd-php" is a PHP page.
3504 */
3505
3506 cupsdSetString(&con->command, CUPS_PHP);
3507
3508 if (options)
b94498cf 3509 cupsdSetStringf(&con->options, " %s %s", filename, options);
e1d6a774 3510 else
b94498cf 3511 cupsdSetStringf(&con->options, " %s", filename);
e1d6a774 3512
3513 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
3514 "[Client %d] is_cgi filename=\"%s\", filestats=%p, "
3515 "type=%s/%s, returning 1", con->http.fd, filename,
3516 filestats, type->super, type->type);
e1d6a774 3517 return (1);
3518 }
3519#endif /* HAVE_PHP */
3520#ifdef HAVE_PYTHON
88f9aafc 3521 else if (!_cups_strcasecmp(type->type, "x-httpd-python"))
e1d6a774 3522 {
3523 /*
3524 * "application/x-httpd-python" is a Python page.
3525 */
3526
3527 cupsdSetString(&con->command, CUPS_PYTHON);
3528
3529 if (options)
b94498cf 3530 cupsdSetStringf(&con->options, " %s %s", filename, options);
e1d6a774 3531 else
b94498cf 3532 cupsdSetStringf(&con->options, " %s", filename);
e1d6a774 3533
3534 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
3535 "[Client %d] is_cgi filename=\"%s\", filestats=%p, "
3536 "type=%s/%s, returning 1", con->http.fd, filename,
3537 filestats, type->super, type->type);
e1d6a774 3538 return (1);
3539 }
3540#endif /* HAVE_PYTHON */
3541
b9faaae1 3542 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
3543 "[Client %d] is_cgi filename=\"%s\", filestats=%p, "
3544 "type=%s/%s, returning 0", con->http.fd, filename,
3545 filestats, type->super, type->type);
e1d6a774 3546 return (0);
3547}
3548
3549
ef416fc2 3550/*
3551 * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. "..").
3552 */
3553
3554static int /* O - 0 if relative, 1 if absolute */
3555is_path_absolute(const char *path) /* I - Input path */
3556{
3557 /*
3558 * Check for a leading slash...
3559 */
3560
3561 if (path[0] != '/')
3562 return (0);
3563
3564 /*
3565 * Check for "/.." in the path...
3566 */
3567
3568 while ((path = strstr(path, "/..")) != NULL)
3569 {
3570 if (!path[3] || path[3] == '/')
3571 return (0);
3572
3573 path ++;
3574 }
3575
3576 /*
3577 * If we haven't found any relative paths, return 1 indicating an
3578 * absolute path...
3579 */
3580
3581 return (1);
3582}
3583
3584
3585/*
3586 * 'pipe_command()' - Pipe the output of a command to the remote client.
3587 */
3588
3589static int /* O - Process ID */
3590pipe_command(cupsd_client_t *con, /* I - Client connection */
3591 int infile, /* I - Standard input for command */
3592 int *outfile, /* O - Standard output for command */
3593 char *command, /* I - Command to run */
3594 char *options, /* I - Options for command */
3595 int root) /* I - Run as root? */
3596{
3597 int i; /* Looping var */
3598 int pid; /* Process ID */
b423cd4c 3599 char *commptr, /* Command string pointer */
3600 commch; /* Command string character */
ef416fc2 3601 char *uriptr; /* URI string pointer */
3602 int fds[2]; /* Pipe FDs */
3603 int argc; /* Number of arguments */
3604 int envc; /* Number of environment variables */
3605 char argbuf[10240], /* Argument buffer */
3606 *argv[100], /* Argument strings */
b94498cf 3607 *envp[MAX_ENV + 20]; /* Environment variables */
c7017ecc 3608 char auth_type[256], /* AUTH_TYPE environment variable */
b94498cf 3609 content_length[1024], /* CONTENT_LENGTH environment variable */
ef416fc2 3610 content_type[1024], /* CONTENT_TYPE environment variable */
3611 http_cookie[32768], /* HTTP_COOKIE environment variable */
f7deaa1a 3612 http_referer[1024], /* HTTP_REFERER environment variable */
ef416fc2 3613 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
3614 lang[1024], /* LANG environment variable */
b423cd4c 3615 path_info[1024], /* PATH_INFO environment variable */
ef416fc2 3616 remote_addr[1024], /* REMOTE_ADDR environment variable */
3617 remote_host[1024], /* REMOTE_HOST environment variable */
3618 remote_user[1024], /* REMOTE_USER environment variable */
b94498cf 3619 script_filename[1024], /* SCRIPT_FILENAME environment variable */
ef416fc2 3620 script_name[1024], /* SCRIPT_NAME environment variable */
3621 server_name[1024], /* SERVER_NAME environment variable */
3622 server_port[1024]; /* SERVER_PORT environment variable */
7dfedb92 3623 ipp_attribute_t *attr; /* attributes-natural-language attribute */
e07d4801 3624 void *ccache = NULL; /* Kerberos credentials */
ef416fc2 3625
3626
3627 /*
3628 * Parse a copy of the options string, which is of the form:
3629 *
b423cd4c 3630 * argument+argument+argument
3631 * ?argument+argument+argument
3632 * param=value&param=value
3633 * ?param=value&param=value
3634 * /name?argument+argument+argument
3635 * /name?param=value&param=value
ef416fc2 3636 *
3637 * If the string contains an "=" character after the initial name,
3638 * then we treat it as a HTTP GET form request and make a copy of
3639 * the remaining string for the environment variable.
3640 *
3641 * The string is always parsed out as command-line arguments, to
3642 * be consistent with Apache...
3643 */
3644
3645 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
3646 "[Client %d] pipe_command infile=%d, outfile=%p, "
3647 "command=\"%s\", options=\"%s\", root=%d",
3648 con->http.fd, infile, outfile, command,
b9faaae1 3649 options ? options : "(null)", root);
ef416fc2 3650
b86bc4cf 3651 argv[0] = command;
ef416fc2 3652
b423cd4c 3653 if (options)
c7017ecc
MS
3654 {
3655 commptr = options;
3656 if (*commptr == ' ')
3657 commptr ++;
3658 strlcpy(argbuf, commptr, sizeof(argbuf));
3659 }
b423cd4c 3660 else
3661 argbuf[0] = '\0';
3662
3663 if (argbuf[0] == '/')
ef416fc2 3664 {
3665 /*
b423cd4c 3666 * Found some trailing path information, set PATH_INFO...
ef416fc2 3667 */
3668
b423cd4c 3669 if ((commptr = strchr(argbuf, '?')) == NULL)
3670 commptr = argbuf + strlen(argbuf);
ef416fc2 3671
b423cd4c 3672 commch = *commptr;
3673 *commptr = '\0';
3674 snprintf(path_info, sizeof(path_info), "PATH_INFO=%s", argbuf);
3675 *commptr = commch;
3676 }
3677 else
3678 {
3679 commptr = argbuf;
3680 path_info[0] = '\0';
b94498cf 3681
3682 if (*commptr == ' ')
3683 commptr ++;
b423cd4c 3684 }
ef416fc2 3685
b86bc4cf 3686 if (*commptr == '?' && con->operation == HTTP_GET && !con->query_string)
b423cd4c 3687 {
3688 commptr ++;
b86bc4cf 3689 cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", commptr);
b423cd4c 3690 }
ef416fc2 3691
b423cd4c 3692 argc = 1;
ef416fc2 3693
b423cd4c 3694 if (*commptr)
3695 {
3696 argv[argc ++] = commptr;
ef416fc2 3697
b423cd4c 3698 for (; *commptr && argc < 99; commptr ++)
3699 {
ef416fc2 3700 /*
b423cd4c 3701 * Break arguments whenever we see a + or space...
ef416fc2 3702 */
3703
b423cd4c 3704 if (*commptr == ' ' || *commptr == '+')
3705 {
3706 while (*commptr == ' ' || *commptr == '+')
3707 *commptr++ = '\0';
ef416fc2 3708
b423cd4c 3709 /*
3710 * If we don't have a blank string, save it as another argument...
3711 */
ef416fc2 3712
b423cd4c 3713 if (*commptr)
3714 {
3715 argv[argc] = commptr;
3716 argc ++;
3717 }
3718 else
3719 break;
3720 }
3721 else if (*commptr == '%' && isxdigit(commptr[1] & 255) &&
3722 isxdigit(commptr[2] & 255))
3723 {
3724 /*
3725 * Convert the %xx notation to the individual character.
3726 */
ef416fc2 3727
b423cd4c 3728 if (commptr[1] >= '0' && commptr[1] <= '9')
3729 *commptr = (commptr[1] - '0') << 4;
3730 else
3731 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
ef416fc2 3732
b423cd4c 3733 if (commptr[2] >= '0' && commptr[2] <= '9')
3734 *commptr |= commptr[2] - '0';
3735 else
3736 *commptr |= tolower(commptr[2]) - 'a' + 10;
ef416fc2 3737
b423cd4c 3738 _cups_strcpy(commptr + 1, commptr + 3);
ef416fc2 3739
b423cd4c 3740 /*
3741 * Check for a %00 and break if that is the case...
3742 */
ef416fc2 3743
b423cd4c 3744 if (!*commptr)
3745 break;
3746 }
ef416fc2 3747 }
3748 }
3749
3750 argv[argc] = NULL;
3751
ef416fc2 3752 /*
3753 * Setup the environment variables as needed...
3754 */
3755
b94498cf 3756 if (con->username[0])
3757 {
c7017ecc 3758 snprintf(auth_type, sizeof(auth_type), "AUTH_TYPE=%s",
b94498cf 3759 httpGetField(HTTP(con), HTTP_FIELD_AUTHORIZATION));
3760
3761 if ((uriptr = strchr(auth_type + 10, ' ')) != NULL)
3762 *uriptr = '\0';
3763 }
3764 else
3765 auth_type[0] = '\0';
3766
7dfedb92
MS
3767 if (con->request &&
3768 (attr = ippFindAttribute(con->request, "attributes-natural-language",
3769 IPP_TAG_LANGUAGE)) != NULL)
3770 {
3771 switch (strlen(attr->values[0].string.text))
3772 {
3773 default :
3774 /*
3775 * This is an unknown or badly formatted language code; use
3776 * the POSIX locale...
3777 */
3778
3779 strcpy(lang, "LANG=C");
3780 break;
3781
3782 case 2 :
3783 /*
3784 * Just the language code (ll)...
3785 */
3786
3787 snprintf(lang, sizeof(lang), "LANG=%s.UTF8",
3788 attr->values[0].string.text);
3789 break;
3790
3791 case 5 :
3792 /*
3793 * Language and country code (ll-cc)...
3794 */
3795
3796 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF8",
3797 attr->values[0].string.text[0],
3798 attr->values[0].string.text[1],
3799 toupper(attr->values[0].string.text[3] & 255),
3800 toupper(attr->values[0].string.text[4] & 255));
3801 break;
3802 }
3803 }
3804 else if (con->language)
3805 snprintf(lang, sizeof(lang), "LANG=%s.UTF8", con->language->language);
ef416fc2 3806 else
3807 strcpy(lang, "LANG=C");
3808
3809 strcpy(remote_addr, "REMOTE_ADDR=");
3810 httpAddrString(con->http.hostaddr, remote_addr + 12,
3811 sizeof(remote_addr) - 12);
3812
3813 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s",
3814 con->http.hostname);
3815
3816 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
3817 if ((uriptr = strchr(script_name, '?')) != NULL)
3818 *uriptr = '\0';
3819
b94498cf 3820 snprintf(script_filename, sizeof(script_filename), "SCRIPT_FILENAME=%s%s",
3821 DocumentRoot, script_name + 12);
3822
ef416fc2 3823 sprintf(server_port, "SERVER_PORT=%d", con->serverport);
3824
0268488e
MS
3825 if (con->http.fields[HTTP_FIELD_HOST][0])
3826 {
3827 char *nameptr; /* Pointer to ":port" */
3828
3829 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
3830 con->http.fields[HTTP_FIELD_HOST]);
3831 if ((nameptr = strrchr(server_name, ':')) != NULL && !strchr(nameptr, ']'))
3832 *nameptr = '\0'; /* Strip trailing ":port" */
3833 }
3834 else
3835 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
3836 con->servername);
ef416fc2 3837
3838 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
3839
b94498cf 3840 if (auth_type[0])
3841 envp[envc ++] = auth_type;
3842
ef416fc2 3843 envp[envc ++] = lang;
3844 envp[envc ++] = "REDIRECT_STATUS=1";
b94498cf 3845 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
ef416fc2 3846 envp[envc ++] = server_name;
3847 envp[envc ++] = server_port;
3848 envp[envc ++] = remote_addr;
3849 envp[envc ++] = remote_host;
3850 envp[envc ++] = script_name;
b94498cf 3851 envp[envc ++] = script_filename;
ef416fc2 3852
b423cd4c 3853 if (path_info[0])
3854 envp[envc ++] = path_info;
3855
ef416fc2 3856 if (con->username[0])
3857 {
3858 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
3859
3860 envp[envc ++] = remote_user;
3861 }
3862
3863 if (con->http.version == HTTP_1_1)
3864 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
3865 else if (con->http.version == HTTP_1_0)
3866 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
3867 else
3868 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
3869
3870 if (con->http.cookie)
3871 {
3872 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
3873 con->http.cookie);
3874 envp[envc ++] = http_cookie;
3875 }
3876
3877 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
3878 {
3879 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
3880 con->http.fields[HTTP_FIELD_USER_AGENT]);
3881 envp[envc ++] = http_user_agent;
3882 }
3883
f7deaa1a 3884 if (con->http.fields[HTTP_FIELD_REFERER][0])
3885 {
3886 snprintf(http_referer, sizeof(http_referer), "HTTP_REFERER=%s",
3887 con->http.fields[HTTP_FIELD_REFERER]);
3888 envp[envc ++] = http_referer;
3889 }
3890
ef416fc2 3891 if (con->operation == HTTP_GET)
3892 {
ef416fc2 3893 envp[envc ++] = "REQUEST_METHOD=GET";
3894
b86bc4cf 3895 if (con->query_string)
ef416fc2 3896 {
3897 /*
3898 * Add GET form variables after ?...
3899 */
3900
b86bc4cf 3901 envp[envc ++] = con->query_string;
ef416fc2 3902 }
3dfe78b3
MS
3903 else
3904 envp[envc ++] = "QUERY_STRING=";
ef416fc2 3905 }
3906 else
3907 {
e1d6a774 3908 sprintf(content_length, "CONTENT_LENGTH=" CUPS_LLFMT,
3909 CUPS_LLCAST con->bytes);
ef416fc2 3910 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
3911 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
3912
3913 envp[envc ++] = "REQUEST_METHOD=POST";
3914 envp[envc ++] = content_length;
3915 envp[envc ++] = content_type;
3916 }
3917
3918 /*
3919 * Tell the CGI if we are using encryption...
3920 */
3921
a74454a7 3922 if (con->http.tls)
ef416fc2 3923 envp[envc ++] = "HTTPS=ON";
3924
3925 /*
3926 * Terminate the environment array...
3927 */
3928
3929 envp[envc] = NULL;
3930
38e73f87 3931 if (LogLevel >= CUPSD_LOG_DEBUG)
ef416fc2 3932 {
3933 for (i = 0; i < argc; i ++)
38e73f87
MS
3934 cupsdLogMessage(CUPSD_LOG_DEBUG,
3935 "[CGI] argv[%d] = \"%s\"", i, argv[i]);
ef416fc2 3936 for (i = 0; i < envc; i ++)
38e73f87
MS
3937 cupsdLogMessage(CUPSD_LOG_DEBUG,
3938 "[CGI] envp[%d] = \"%s\"", i, envp[i]);
ef416fc2 3939 }
3940
3941 /*
3942 * Create a pipe for the output...
3943 */
3944
3945 if (cupsdOpenPipe(fds))
3946 {
38e73f87 3947 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to create pipe for %s - %s",
ef416fc2 3948 argv[0], strerror(errno));
3949 return (0);
3950 }
3951
3952 /*
3953 * Then execute the command...
3954 */
3955
3956 if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1],
38e73f87 3957 -1, -1, root, DefaultProfile, NULL, &pid) < 0)
ef416fc2 3958 {
3959 /*
3960 * Error - can't fork!
3961 */
3962
38e73f87 3963 cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to start %s - %s", argv[0],
ef416fc2 3964 strerror(errno));
3965
3966 cupsdClosePipe(fds);
3967 pid = 0;
3968 }
3969 else
3970 {
3971 /*
3972 * Fork successful - return the PID...
3973 */
3974
3975 if (con->username[0])
5bd77a73 3976 cupsdAddCert(pid, con->username, ccache);
ef416fc2 3977
38e73f87 3978 cupsdLogMessage(CUPSD_LOG_DEBUG, "[CGI] Started %s (PID %d)", command, pid);
ef416fc2 3979
3980 *outfile = fds[0];
3981 close(fds[1]);
3982 }
3983
ef416fc2 3984 return (pid);
3985}
3986
3987
e07d4801
MS
3988/*
3989 * 'valid_host()' - Is the Host: field valid?
3990 */
3991
3992static int /* O - 1 if valid, 0 if not */
3993valid_host(cupsd_client_t *con) /* I - Client connection */
3994{
3995 cupsd_alias_t *a; /* Current alias */
3996 cupsd_netif_t *netif; /* Current network interface */
3997 const char *host, /* Host field */
3998 *end; /* End character */
3999
4000
4001 host = con->http.fields[HTTP_FIELD_HOST];
4002
4003 if (httpAddrLocalhost(con->http.hostaddr))
4004 {
4005 /*
4006 * Only allow "localhost" or the equivalent IPv4 or IPv6 numerical
4007 * addresses when accessing CUPS via the loopback interface...
4008 */
4009
88f9aafc
MS
4010 return (!_cups_strcasecmp(host, "localhost") ||
4011 !_cups_strncasecmp(host, "localhost:", 10) ||
4012 !_cups_strcasecmp(host, "localhost.") ||
4013 !_cups_strncasecmp(host, "localhost.:", 11) ||
e07d4801 4014#ifdef __linux
88f9aafc
MS
4015 !_cups_strcasecmp(host, "localhost.localdomain") ||
4016 !_cups_strncasecmp(host, "localhost.localdomain:", 22) ||
e07d4801
MS
4017#endif /* __linux */
4018 !strcmp(host, "127.0.0.1") ||
4019 !strncmp(host, "127.0.0.1:", 10) ||
4020 !strcmp(host, "[::1]") ||
4021 !strncmp(host, "[::1]:", 6));
4022 }
4023
4024#ifdef HAVE_DNSSD
4025 /*
4026 * Check if the hostname is something.local (Bonjour); if so, allow it.
4027 */
4028
a29fd7dd
MS
4029 if ((end = strrchr(host, '.')) != NULL && end > host &&
4030 (!end[1] || end[1] == ':'))
4031 {
4032 /*
4033 * "." on end, work back to second-to-last "."...
4034 */
4035 for (end --; end > host && *end != '.'; end --);
4036 }
4037
4038 if (end && (!_cups_strcasecmp(end, ".local") ||
4039 !_cups_strncasecmp(end, ".local:", 7) ||
4040 !_cups_strcasecmp(end, ".local.") ||
4041 !_cups_strncasecmp(end, ".local.:", 8)))
e07d4801
MS
4042 return (1);
4043#endif /* HAVE_DNSSD */
4044
4045 /*
4046 * Check if the hostname is an IP address...
4047 */
4048
4049 if (isdigit(*host & 255) || *host == '[')
4050 {
4051 /*
4052 * Possible IPv4/IPv6 address...
4053 */
4054
4055 char temp[1024], /* Temporary string */
4056 *ptr; /* Pointer into temporary string */
4057 http_addrlist_t *addrlist; /* List of addresses */
4058
4059
4060 strlcpy(temp, host, sizeof(temp));
4061 if ((ptr = strrchr(temp, ':')) != NULL && !strchr(ptr, ']'))
4062 *ptr = '\0'; /* Strip :port from host value */
4063
4064 if ((addrlist = httpAddrGetList(temp, AF_UNSPEC, NULL)) != NULL)
4065 {
4066 /*
4067 * Good IPv4/IPv6 address...
4068 */
4069
4070 httpAddrFreeList(addrlist);
4071 return (1);
4072 }
4073 }
4074
4075 /*
4076 * Check for (alias) name matches...
4077 */
4078
4079 for (a = (cupsd_alias_t *)cupsArrayFirst(ServerAlias);
4080 a;
4081 a = (cupsd_alias_t *)cupsArrayNext(ServerAlias))
4082 {
4083 /*
4084 * "ServerAlias *" allows all host values through...
4085 */
4086
4087 if (!strcmp(a->name, "*"))
4088 return (1);
4089
88f9aafc 4090 if (!_cups_strncasecmp(host, a->name, a->namelen))
e07d4801
MS
4091 {
4092 /*
4093 * Prefix matches; check the character at the end - it must be ":", ".",
4094 * ".:", or nul...
4095 */
4096
4097 end = host + a->namelen;
4098
4099 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
4100 return (1);
4101 }
4102 }
4103
4104#ifdef HAVE_DNSSD
4105 for (a = (cupsd_alias_t *)cupsArrayFirst(DNSSDAlias);
4106 a;
4107 a = (cupsd_alias_t *)cupsArrayNext(DNSSDAlias))
4108 {
4109 /*
4110 * "ServerAlias *" allows all host values through...
4111 */
4112
4113 if (!strcmp(a->name, "*"))
4114 return (1);
4115
88f9aafc 4116 if (!_cups_strncasecmp(host, a->name, a->namelen))
e07d4801
MS
4117 {
4118 /*
4119 * Prefix matches; check the character at the end - it must be ":", ".",
4120 * ".:", or nul...
4121 */
4122
4123 end = host + a->namelen;
4124
4125 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
4126 return (1);
4127 }
4128 }
4129#endif /* HAVE_DNSSD */
4130
4131 /*
4132 * Check for interface hostname matches...
4133 */
4134
4135 for (netif = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
4136 netif;
4137 netif = (cupsd_netif_t *)cupsArrayNext(NetIFList))
4138 {
88f9aafc 4139 if (!_cups_strncasecmp(host, netif->hostname, netif->hostlen))
e07d4801
MS
4140 {
4141 /*
4142 * Prefix matches; check the character at the end - it must be ":", ".",
4143 * ".:", or nul...
4144 */
4145
4146 end = host + netif->hostlen;
4147
4148 if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
4149 return (1);
4150 }
4151 }
4152
4153 return (0);
4154}
4155
4156
ef416fc2 4157/*
a74454a7 4158 * 'write_file()' - Send a file via HTTP.
e1d6a774 4159 */
4160
4161static int /* O - 0 on failure, 1 on success */
a74454a7 4162write_file(cupsd_client_t *con, /* I - Client connection */
4163 http_status_t code, /* I - HTTP status */
4164 char *filename, /* I - Filename */
4165 char *type, /* I - File type */
4166 struct stat *filestats) /* O - File information */
e1d6a774 4167{
4168 con->file = open(filename, O_RDONLY);
4169
b9faaae1 4170 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
4171 "[Client %d] write_file code=%d, filename=\"%s\" (%d), "
4172 "type=\"%s\", filestats=%p", con->http.fd,
b9faaae1 4173 code, filename, con->file, type ? type : "(null)", filestats);
e1d6a774 4174
4175 if (con->file < 0)
4176 return (0);
4177
4178 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
4179
4180 con->pipe_pid = 0;
4181
5bd77a73 4182 if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
e1d6a774 4183 return (0);
4184
4185 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
4186 httpGetDateString(filestats->st_mtime)) < 0)
4187 return (0);
4188 if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n",
4189 CUPS_LLCAST filestats->st_size) < 0)
4190 return (0);
4191 if (httpPrintf(HTTP(con), "\r\n") < 0)
4192 return (0);
4193
07725fee 4194 if (cupsdFlushHeader(con) < 0)
4195 return (0);
d09495fa 4196
e1d6a774 4197 con->http.data_encoding = HTTP_ENCODE_LENGTH;
4198 con->http.data_remaining = filestats->st_size;
4199
4200 if (con->http.data_remaining <= INT_MAX)
4201 con->http._data_remaining = con->http.data_remaining;
4202 else
4203 con->http._data_remaining = INT_MAX;
4204
f7deaa1a 4205 cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
4206 (cupsd_selfunc_t)cupsdWriteClient, con);
e1d6a774 4207
4208 return (1);
4209}
4210
4211
4212/*
f899b121 4213 * 'write_pipe()' - Flag that data is available on the CGI pipe.
4214 */
4215
4216static void
4217write_pipe(cupsd_client_t *con) /* I - Client connection */
4218{
b9faaae1 4219 cupsdLogMessage(CUPSD_LOG_DEBUG2,
85dda01c
MS
4220 "[Client %d] write_pipe CGI output on fd %d",
4221 con->http.fd, con->file);
f899b121 4222
4223 con->file_ready = 1;
4224
4225 cupsdRemoveSelect(con->file);
4226 cupsdAddSelect(con->http.fd, NULL, (cupsd_selfunc_t)cupsdWriteClient, con);
4227}
4228
4229
4230/*
b19ccc9e 4231 * End of "$Id: client.c 7950 2008-09-17 00:21:59Z mike $".
ef416fc2 4232 */