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