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