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