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