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