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