]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/client.c
Mirror 1.1.x changes.
[thirdparty/cups.git] / scheduler / client.c
CommitLineData
a74b005d 1/*
aa7b8dba 2 * "$Id: client.c,v 1.91.2.71 2003/09/15 20:11:08 mike Exp $"
a74b005d 3 *
4 * Client routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
1d9595ab 6 * Copyright 1997-2003 by Easy Software Products, all rights reserved.
a74b005d 7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
8784b6a6 17 * 44141 Airport View Drive, Suite 204
a74b005d 18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
93894a43 26 * AcceptClient() - Accept a new client.
27 * CloseAllClients() - Close all remote clients immediately.
28 * CloseClient() - Close a remote client.
a75c006a 29 * EncryptClient() - Enable encryption for the client...
3721af07 30 * IsCGI() - Is the resource a CGI script/program?
93894a43 31 * ReadClient() - Read data from a client.
32 * SendCommand() - Send output from a command via HTTP.
33 * SendError() - Send an error message via HTTP.
34 * SendFile() - Send a file via HTTP.
35 * SendHeader() - Send an HTTP request.
c3c5af5e 36 * ShutdownClient() - Shutdown the receiving end of a connection.
0a968cfb 37 * UpdateCGI() - Read status messages from CGI scripts and programs.
93894a43 38 * WriteClient() - Write data to a client as needed.
39 * check_if_modified() - Decode an "If-Modified-Since" line.
d2122fde 40 * decode_auth() - Decode an authorization string.
93894a43 41 * get_file() - Get a filename and state info.
753453e4 42 * install_conf_file() - Install a configuration file.
93894a43 43 * pipe_command() - Pipe the output of a command to the remote client.
dcfcaeac 44 * CDSAReadFunc() - Read function for CDSA decryption code.
45 * CDSAWriteFunc() - Write function for CDSA encryption code.
a74b005d 46 */
47
48/*
49 * Include necessary headers...
50 */
51
3b602493 52#include <cups/http-private.h>
a74b005d 53#include "cupsd.h"
bd917997 54#include <grp.h>
55
a74b005d 56
57/*
58 * Local functions...
59 */
60
753453e4 61static int check_if_modified(client_t *con,
62 struct stat *filestats);
63static void decode_auth(client_t *con);
6db7190f 64static char *get_file(client_t *con, struct stat *filestats,
65 char *filename, int len);
753453e4 66static http_status_t install_conf_file(client_t *con);
67static int pipe_command(client_t *con, int infile, int *outfile,
68 char *command, char *options);
a74b005d 69
dcfcaeac 70#ifdef HAVE_CDSASSL
df28ec49 71static OSStatus CDSAReadFunc(SSLConnectionRef connection, void *data,
dcfcaeac 72 size_t *dataLength);
df28ec49 73static OSStatus CDSAWriteFunc(SSLConnectionRef connection,
dcfcaeac 74 const void *data, size_t *dataLength);
75#endif /* HAVE_CDSASSL */
76
a74b005d 77
78/*
79 * 'AcceptClient()' - Accept a new client.
80 */
81
82void
83AcceptClient(listener_t *lis) /* I - Listener socket */
84{
27eba2dd 85 int i; /* Looping var */
1060c9b6 86 int count; /* Count of connections on a host */
a74b005d 87 int val; /* Parameter value */
88 client_t *con; /* New client pointer */
99de6da0 89 const struct hostent *host; /* Host entry for address */
18fe941f 90 char *hostname;/* Hostname for address */
99de6da0 91 http_addr_t temp; /* Temporary address variable */
f0ad8cdc 92 static time_t last_dos = 0;
93 /* Time of last DoS attack */
a74b005d 94
95
aa7e125a 96 LogMessage(L_DEBUG2, "AcceptClient(%p) %d NumClients = %d",
27eba2dd 97 lis, lis->fd, NumClients);
bfa1abf0 98
12aa0c1f 99 /*
100 * Make sure we don't have a full set of clients already...
101 */
102
103 if (NumClients == MaxClients)
104 return;
105
a74b005d 106 /*
107 * Get a pointer to the next available client...
108 */
109
110 con = Clients + NumClients;
111
112 memset(con, 0, sizeof(client_t));
113 con->http.activity = time(NULL);
114
115 /*
116 * Accept the client and get the remote address...
117 */
118
119 val = sizeof(struct sockaddr_in);
120
2f6d083c 121 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr),
122 &val)) < 0)
a74b005d 123 {
27eba2dd 124 LogMessage(L_ERROR, "Unable to accept client connection - %s.",
125 strerror(errno));
a74b005d 126 return;
127 }
128
99de6da0 129#ifdef AF_INET6
130 if (lis->address.addr.sa_family == AF_INET6)
131 con->http.hostaddr.ipv6.sin6_port = lis->address.ipv6.sin6_port;
132 else
133#endif /* AF_INET6 */
134 con->http.hostaddr.ipv4.sin_port = lis->address.ipv4.sin_port;
a74b005d 135
1060c9b6 136 /*
137 * Check the number of clients on the same address...
138 */
139
140 for (i = 0, count = 0; i < NumClients; i ++)
141 if (memcmp(&(Clients[i].http.hostaddr), &(con->http.hostaddr),
142 sizeof(con->http.hostaddr)) == 0)
143 {
144 count ++;
145 if (count >= MaxClientsPerHost)
146 break;
147 }
148
149 if (count >= MaxClientsPerHost)
150 {
f0ad8cdc 151 if ((time(NULL) - last_dos) >= 60)
152 {
153 last_dos = time(NULL);
154 LogMessage(L_WARN, "Possible DoS attack - more than %d clients connecting from %s!",
155 MaxClientsPerHost, Clients[i].http.hostname);
156 }
1060c9b6 157
158#ifdef WIN32
159 closesocket(con->http.fd);
160#else
161 close(con->http.fd);
162#endif /* WIN32 */
163
164 return;
165 }
166
167 /*
168 * Get the hostname or format the IP address as needed...
169 */
170
a74b005d 171 if (HostNameLookups)
18fe941f 172 hostname = httpAddrLookup(&(con->http.hostaddr), con->http.hostname,
173 sizeof(con->http.hostname));
a74b005d 174 else
99de6da0 175 {
18fe941f 176 hostname = NULL;
99de6da0 177 httpAddrString(&(con->http.hostaddr), con->http.hostname,
178 sizeof(con->http.hostname));
179 }
a74b005d 180
06415a96 181 if (httpAddrLocalhost(&(con->http.hostaddr)))
182 {
183 /*
184 * Map accesses from the loopback interface to "localhost"...
185 */
186
187 strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname));
188 }
189 else if (httpAddrEqual(&(con->http.hostaddr), &ServerAddr))
2bdd1992 190 {
191 /*
192 * Map accesses from the same host to the server name.
193 */
194
def978d5 195 strlcpy(con->http.hostname, ServerName, sizeof(con->http.hostname));
2bdd1992 196 }
27eba2dd 197
18fe941f 198 if (hostname == NULL && HostNameLookups == 2)
99de6da0 199 {
200 /*
201 * Can't have an unresolved IP address with double-lookups enabled...
202 */
27eba2dd 203
6cd03542 204 LogMessage(L_DEBUG2, "AcceptClient: Closing connection %d...",
205 con->http.fd);
206
27eba2dd 207#ifdef WIN32
99de6da0 208 closesocket(con->http.fd);
27eba2dd 209#else
99de6da0 210 close(con->http.fd);
27eba2dd 211#endif /* WIN32 */
212
99de6da0 213 LogMessage(L_WARN, "Name lookup failed - connection from %s closed!",
214 con->http.hostname);
215 return;
27eba2dd 216 }
a74b005d 217
27eba2dd 218 if (HostNameLookups == 2)
219 {
220 /*
221 * Do double lookups as needed...
222 */
223
753453e4 224 if ((host = httpGetHostByName(con->http.hostname)) != NULL)
27eba2dd 225 {
226 /*
99de6da0 227 * See if the hostname maps to the same IP address...
27eba2dd 228 */
229
99de6da0 230 if (host->h_addrtype != con->http.hostaddr.addr.sa_family)
27eba2dd 231 {
232 /*
99de6da0 233 * Not the right type of address...
27eba2dd 234 */
235
46490d9d 236 host = NULL;
27eba2dd 237 }
238 else
239 {
240 /*
241 * Compare all of the addresses against this one...
242 */
243
46490d9d 244 for (i = 0; host->h_addr_list[i]; i ++)
99de6da0 245 {
246 httpAddrLoad(host, 0, i, &temp);
247
248 if (httpAddrEqual(&(con->http.hostaddr), &temp))
27eba2dd 249 break;
99de6da0 250 }
27eba2dd 251
46490d9d 252 if (!host->h_addr_list[i])
253 host = NULL;
27eba2dd 254 }
255 }
256
46490d9d 257 if (host == NULL)
27eba2dd 258 {
259 /*
260 * Can't have a hostname that doesn't resolve to the same IP address
261 * with double-lookups enabled...
262 */
263
6cd03542 264 LogMessage(L_DEBUG2, "AcceptClient: Closing connection %d...",
265 con->http.fd);
266
27eba2dd 267#ifdef WIN32
268 closesocket(con->http.fd);
269#else
270 close(con->http.fd);
271#endif /* WIN32 */
272
273 LogMessage(L_WARN, "IP lookup failed - connection from %s closed!",
274 con->http.hostname);
275 return;
276 }
277 }
278
99de6da0 279#ifdef AF_INET6
280 if (con->http.hostaddr.addr.sa_family == AF_INET6)
18fe941f 281 LogMessage(L_DEBUG, "AcceptClient: %d from %s:%d.", con->http.fd,
99de6da0 282 con->http.hostname, ntohs(con->http.hostaddr.ipv6.sin6_port));
283 else
284#endif /* AF_INET6 */
18fe941f 285 LogMessage(L_DEBUG, "AcceptClient: %d from %s:%d.", con->http.fd,
99de6da0 286 con->http.hostname, ntohs(con->http.hostaddr.ipv4.sin_port));
a74b005d 287
9ae34eb7 288 /*
289 * Using TCP_NODELAY improves responsiveness, especially on systems
290 * with a slow loopback interface... Since we write large buffers
291 * when sending print files and requests, there shouldn't be any
292 * performance penalty for this...
293 */
294
295 val = 1;
296 setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
297
a74b005d 298 /*
2e32fcdb 299 * Close this file on all execs...
a74b005d 300 */
301
302 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
303
2e32fcdb 304 /*
305 * Add the socket to the select() input mask.
306 */
307
18fe941f 308 LogMessage(L_DEBUG2, "AcceptClient: Adding fd %d to InputSet...",
27eba2dd 309 con->http.fd);
f3bc1068 310 FD_SET(con->http.fd, InputSet);
a74b005d 311
312 NumClients ++;
313
314 /*
315 * Temporarily suspend accept()'s until we lose a client...
316 */
317
997edb40 318 if (NumClients == MaxClients)
d236cb49 319 PauseListening();
2a0ef17a 320
bcf61448 321#ifdef HAVE_SSL
2a0ef17a 322 /*
0b50ddbc 323 * See if we are connecting on a secure port...
2a0ef17a 324 */
325
0b50ddbc 326 if (lis->encryption == HTTP_ENCRYPT_ALWAYS)
2a0ef17a 327 {
328 /*
329 * https connection; go secure...
330 */
331
b92c5a4f 332 con->http.encryption = HTTP_ENCRYPT_ALWAYS;
333
2a0ef17a 334 EncryptClient(con);
335 }
bcf61448 336#endif /* HAVE_SSL */
a74b005d 337}
338
339
340/*
341 * 'CloseAllClients()' - Close all remote clients immediately.
342 */
343
344void
345CloseAllClients(void)
346{
347 while (NumClients > 0)
348 CloseClient(Clients);
349}
350
351
352/*
353 * 'CloseClient()' - Close a remote client.
354 */
355
356void
357CloseClient(client_t *con) /* I - Client to close */
358{
dcfcaeac 359#if defined(HAVE_LIBSSL)
2a0ef17a 360 SSL_CTX *context; /* Context for encryption */
361 SSL *conn; /* Connection for encryption */
c3c5af5e 362 unsigned long error; /* Error code */
dcfcaeac 363#elif defined(HAVE_GNUTLS)
364 http_tls_t *conn; /* TLS connection information */
bcf61448 365 int error; /* Error code */
dcfcaeac 366 gnutls_certificate_server_credentials *credentials;
367 /* TLS credentials */
bcf61448 368#endif /* HAVE_GNUTLS */
a74b005d 369
370
5ea8888e 371 LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd);
a74b005d 372
2e32fcdb 373 if (con->http.input_set)
374 free(con->http.input_set);
375
2ae14c39 376 httpClearCookie(HTTP(con));
377
fc757c63 378 ClearString(&con->filename);
379 ClearString(&con->command);
380 ClearString(&con->options);
381
bcf61448 382#ifdef HAVE_SSL
2a0ef17a 383 /*
384 * Shutdown encryption as needed...
385 */
386
387 if (con->http.tls)
388 {
dcfcaeac 389# ifdef HAVE_LIBSSL
2a0ef17a 390 conn = (SSL *)(con->http.tls);
391 context = SSL_get_SSL_CTX(conn);
392
c3c5af5e 393 switch (SSL_shutdown(conn))
394 {
395 case 1 :
396 LogMessage(L_INFO, "CloseClient: SSL shutdown successful!");
397 break;
398 case -1 :
399 LogMessage(L_ERROR, "CloseClient: Fatal error during SSL shutdown!");
400 default :
401 while ((error = ERR_get_error()) != 0)
402 LogMessage(L_ERROR, "CloseClient: %s", ERR_error_string(error, NULL));
403 break;
404 }
405
2a0ef17a 406 SSL_CTX_free(context);
407 SSL_free(conn);
dcfcaeac 408
409# elif defined(HAVE_GNUTLS)
bcf61448 410 conn = (http_tls_t *)(con->http.tls);
411 credentials = (gnutls_certificate_server_credentials *)(conn->credentials);
412
413 error = gnutls_bye(conn->session, GNUTLS_SHUT_WR);
414 switch (error)
415 {
416 case GNUTLS_E_SUCCESS:
417 LogMessage(L_INFO, "CloseClient: SSL shutdown successful!");
418 break;
419 default:
420 LogMessage(L_ERROR, "CloseClient: %s", gnutls_strerror(error));
421 break;
422 }
423
424 gnutls_deinit(conn->session);
425 gnutls_certificate_free_credentials(*credentials);
426 free(credentials);
427 free(conn);
dcfcaeac 428
429# elif defined(HAVE_CDSASSL)
430 status = SSLClose((SSLContextRef)con->http.tls);
431 SSLDisposeContext((SSLContextRef)con->http.tls);
432# endif /* HAVE_LIBSSL */
2a0ef17a 433
434 con->http.tls = NULL;
435 }
dcfcaeac 436#endif /* HAVE_SSL */
2a0ef17a 437
a74b005d 438 /*
439 * Close the socket and clear the file from the input set for select()...
440 */
441
6cd03542 442 if (con->http.fd >= 0)
882031b3 443 {
e668e627 444 LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...",
27eba2dd 445 con->http.fd);
882031b3 446 close(con->http.fd);
f3bc1068 447 FD_CLR(con->http.fd, InputSet);
448 FD_CLR(con->http.fd, OutputSet);
882031b3 449 con->http.fd = 0;
450 }
a74b005d 451
a74b005d 452 if (con->pipe_pid != 0)
bfa1abf0 453 {
27eba2dd 454 LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet...",
455 con->file);
f3bc1068 456 FD_CLR(con->file, InputSet);
bfa1abf0 457 }
458
bfa1abf0 459 if (con->file)
a74b005d 460 {
26bb6ac6 461 /*
462 * Close the open data file...
463 */
464
a74b005d 465 if (con->pipe_pid)
a74b005d 466 kill(con->pipe_pid, SIGKILL);
a74b005d 467
bd917997 468 LogMessage(L_DEBUG2, "CloseClient() %d Closing data file %d.",
469 con->http.fd, con->file);
e668e627 470 LogMessage(L_DEBUG2, "CloseClient() %d Removing fd %d from InputSet.",
471 con->http.fd, con->file);
bd917997 472
f3bc1068 473 FD_CLR(con->file, InputSet);
a74b005d 474 close(con->file);
bfa1abf0 475 con->file = 0;
a74b005d 476 }
477
25135af7 478 if (con->request)
479 {
480 ippDelete(con->request);
481 con->request = NULL;
482 }
483
484 if (con->response)
485 {
486 ippDelete(con->response);
487 con->response = NULL;
488 }
489
490 if (con->language)
491 {
492 cupsLangFree(con->language);
493 con->language = NULL;
494 }
495
26bb6ac6 496 /*
497 * Re-enable new client connections if we are going back under the
498 * limit...
499 */
500
501 if (NumClients == MaxClients)
d236cb49 502 ResumeListening();
26bb6ac6 503
a74b005d 504 /*
505 * Compact the list of clients as necessary...
506 */
507
508 NumClients --;
509
510 if (con < (Clients + NumClients))
ff40b65e 511 memmove(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
a74b005d 512}
513
514
a75c006a 515/*
516 * 'EncryptClient()' - Enable encryption for the client...
517 */
518
519int /* O - 1 on success, 0 on error */
520EncryptClient(client_t *con) /* I - Client to encrypt */
521{
bcf61448 522#if defined HAVE_LIBSSL
a75c006a 523 SSL_CTX *context; /* Context for encryption */
524 SSL *conn; /* Connection for encryption */
753453e4 525 unsigned long error; /* Error code */
1c6682dd 526
527
528 /*
529 * Create the SSL context and accept the connection...
530 */
531
bcf61448 532 context = SSL_CTX_new(SSLv23_server_method());
533
534 SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
535 SSL_CTX_use_certificate_file(context, ServerCertificate, SSL_FILETYPE_PEM);
a75c006a 536
bcf61448 537 conn = SSL_new(context);
a75c006a 538
539 SSL_set_fd(conn, con->http.fd);
540 if (SSL_accept(conn) != 1)
541 {
1c6682dd 542 while ((error = ERR_get_error()) != 0)
543 LogMessage(L_ERROR, "EncryptClient: %s", ERR_error_string(error, NULL));
544
a75c006a 545 SSL_CTX_free(context);
546 SSL_free(conn);
547 return (0);
548 }
549
bcf61448 550 con->http.tls = conn;
551 return (1);
dcfcaeac 552
553#elif defined(HAVE_GNUTLS)
bcf61448 554 http_tls_t *conn; /* TLS session object */
555 int error; /* Error code */
dcfcaeac 556 gnutls_certificate_server_credentials *credentials;
557 /* TLS credentials */
bcf61448 558
559 /*
560 * Create the SSL object and perform the SSL handshake...
561 */
562
563 conn = (http_tls_t *)malloc(sizeof(gnutls_session));
dcfcaeac 564
bcf61448 565 if (conn == NULL)
566 return (0);
dcfcaeac 567
bcf61448 568 credentials = (gnutls_certificate_server_credentials *)
dcfcaeac 569 malloc(sizeof(gnutls_certificate_server_credentials));
bcf61448 570 if (credentials == NULL)
571 {
572 free(conn);
573 return (0);
574 }
575
576 gnutls_certificate_allocate_credentials(credentials);
577 gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
578 ServerKey, GNUTLS_X509_FMT_PEM);
579
580 gnutls_init(&(conn->session), GNUTLS_SERVER);
581 gnutls_set_default_priority(conn->session);
582 gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials);
583 gnutls_transport_set_ptr(conn->session, con->http.fd);
584
585 error = gnutls_handshake(conn->session);
586
587 if (error != GNUTLS_E_SUCCESS)
588 {
589 LogMessage(L_ERROR, "EncryptClient: %s", gnutls_strerror(error));
590 gnutls_deinit(conn->session);
591 gnutls_certificate_free_credentials(*credentials);
592 free(conn);
593 free(credentials);
594 return (0);
595 }
596
597 LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.",
598 con->http.fd);
599
600 conn->credentials = credentials;
a75c006a 601 con->http.tls = conn;
602 return (1);
dcfcaeac 603
604#elif defined(HAVE_CDSASSL)
605 OSStatus error; /* Error info */
606 SSLContextRef conn; /* New connection */
607 SSLProtocol tryVersion; /* Protocol version */
608 const char *hostName; /* Local hostname */
609 int allowExpired; /* Allow expired certificates? */
610 int allowAnyRoot; /* Allow any root certificate? */
611 SSLProtocol *negVersion; /* Negotiated protocol version */
612 SSLCipherSuite *negCipher; /* Negotiated cypher */
613 CFArrayRef *peerCerts; /* Certificates */
614
615
616 conn = NULL;
617 error = SSLNewContext(true, &conn);
618 allowExpired = 1;
619 allowAnyRoot = 1;
620
621 if (!error)
622 error = SSLSetIOFuncs(conn, CDSAReadFunc, CDSAWriteFunc);
623
624 if (!error)
625 error = SSLSetProtocolVersion(conn, kSSLProtocol3);
626
627 if (!error)
628 error = SSLSetConnection(conn, (SSLConnectionRef)con->http.fd);
629
630 if (!error)
631 {
632 hostName = ServerName; /* MRS: ??? */
633 error = SSLSetPeerDomainName(conn, hostName, strlen(hostName) + 1);
634 }
635
636 /* have to do these options befor setting server certs */
637 if (!error && allowExpired)
638 error = SSLSetAllowsExpiredCerts(conn, true);
639
640 if (!error && allowAnyRoot)
641 error = SSLSetAllowsAnyRoot(conn, true);
642
643 if (!error && ServerCertificatesArray != NULL)
644 error = SSLSetCertificate(conn, ServerCertificatesArray);
645
646 /*
647 * Perform SSL/TLS handshake
648 */
649
650 do
651 {
652 error = SSLHandshake(conn);
653 }
654 while (error == errSSLWouldBlock);
655
656 if (error)
657 {
658 LogMessage(L_ERROR, "EncryptClient: %d", error);
659
660 con->http.error = error;
661 con->http.status = HTTP_ERROR;
662
663 if (conn != NULL)
664 SSLDisposeContext(conn);
665
666 return (0);
667 }
668
669 LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.",
670 con->http.fd);
671 con->http.tls = conn;
672 return (1);
673
a75c006a 674#else
675 return (0);
bcf61448 676#endif /* HAVE_GNUTLS */
a75c006a 677}
678
679
3721af07 680/*
681 * 'IsCGI()' - Is the resource a CGI script/program?
682 */
683
684int /* O - 1 = CGI, 0 = file */
685IsCGI(client_t *con, /* I - Client connection */
686 const char *filename, /* I - Real filename */
687 struct stat *filestats, /* I - File information */
688 mime_type_t *type) /* I - MIME type */
689{
690 const char *options; /* Options on URL */
691
692
693 LogMessage(L_DEBUG2, "IsCGI(con=%p, filename=\"%s\", filestats=%p, type=%s/%s)\n",
694 con, filename, filestats, type ? type->super : "unknown",
695 type ? type->type : "unknown");
696
697 /*
698 * Get the options, if any...
699 */
700
701 if ((options = strchr(con->uri, '?')) != NULL)
702 options ++;
703
704 /*
705 * Check for known types...
706 */
707
708 if (strcasecmp(type->super, "application"))
709 {
710 LogMessage(L_DEBUG2, "IsCGI: Returning 0...");
711 return (0);
712 }
713
714 if (!strcasecmp(type->type, "x-httpd-cgi") &&
715 (filestats->st_mode & 0111))
716 {
717 /*
718 * "application/x-httpd-cgi" is a CGI script.
719 */
720
721 SetString(&con->command, filename);
722
723 filename = strrchr(filename, '/') + 1; /* Filename always absolute */
724
725 if (options)
726 SetStringf(&con->options, "%s %s", filename, options);
727 else
728 SetStringf(&con->options, "%s", filename);
729
730 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
731 con->command, con->options);
732
733 return (1);
734 }
735#ifdef HAVE_JAVA
736 else if (!strcasecmp(type->type, "x-httpd-java"))
737 {
738 /*
739 * "application/x-httpd-java" is a Java servlet.
740 */
741
742 SetString(&con->command, CUPS_JAVA);
743
744 if (options)
745 SetStringf(&con->options, "java %s %s", filename, options);
746 else
747 SetStringf(&con->options, "java %s", filename);
748
749 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
750 con->command, con->options);
751
752 return (1);
753 }
754#endif /* HAVE_JAVA */
755#ifdef HAVE_PERL
756 else if (!strcasecmp(type->type, "x-httpd-perl"))
757 {
758 /*
759 * "application/x-httpd-perl" is a Perl page.
760 */
761
762 SetString(&con->command, CUPS_PERL);
763
764 if (options)
765 SetStringf(&con->options, "perl %s %s", filename, options);
766 else
767 SetStringf(&con->options, "perl %s", filename);
768
769 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
770 con->command, con->options);
771
772 return (1);
773 }
774#endif /* HAVE_PERL */
775#ifdef HAVE_PHP
776 else if (!strcasecmp(type->type, "x-httpd-php"))
777 {
778 /*
779 * "application/x-httpd-php" is a PHP page.
780 */
781
782 SetString(&con->command, CUPS_PHP);
783
784 if (options)
785 SetStringf(&con->options, "php %s %s", filename, options);
786 else
787 SetStringf(&con->options, "php %s", filename);
788
789 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
790 con->command, con->options);
791
792 return (1);
793 }
794#endif /* HAVE_PHP */
795#ifdef HAVE_PYTHON
796 else if (!strcasecmp(type->type, "x-httpd-python"))
797 {
798 /*
799 * "application/x-httpd-python" is a Python page.
800 */
801
802 SetString(&con->command, CUPS_PYTHON);
803
804 if (options)
805 SetStringf(&con->options, "python %s %s", filename, options);
806 else
807 SetStringf(&con->options, "python %s", filename);
808
809 LogMessage(L_DEBUG2, "IsCGI: Returning 1 with command=\"%s\" and options=\"%s\"",
810 con->command, con->options);
811
812 return (1);
813 }
814#endif /* HAVE_PYTHON */
815
816 LogMessage(L_DEBUG2, "IsCGI: Returning 0...");
817
818 return (0);
819}
820
821
a74b005d 822/*
823 * 'ReadClient()' - Read data from a client.
824 */
825
08c0ff15 826int /* O - 1 on success, 0 on error */
827ReadClient(client_t *con) /* I - Client to read from */
a74b005d 828{
08c0ff15 829 char line[32768], /* Line from client... */
830 operation[64], /* Operation code from socket */
831 version[64], /* HTTP version number string */
832 locale[64], /* Locale */
833 *ptr; /* Pointer into strings */
834 int major, minor; /* HTTP version numbers */
835 http_status_t status; /* Transfer status */
836 ipp_state_t ipp_state; /* State of IPP transfer */
837 int bytes; /* Number of bytes to POST */
838 char *filename; /* Name of file for GET/HEAD */
839 char buf[1024]; /* Buffer for real filename */
840 struct stat filestats; /* File information */
841 mime_type_t *type; /* MIME type of file */
842 printer_t *p; /* Printer */
843 location_t *best; /* Best match for authentication */
844 static unsigned request_id = 0; /* Request ID for temp files */
a74b005d 845
bfa1abf0 846
a74b005d 847 status = HTTP_CONTINUE;
848
2d5e06dd 849 LogMessage(L_DEBUG2, "ReadClient() %d, used=%d", con->http.fd,
850 con->http.used);
55809c6f 851
901b295d 852 if (con->http.error)
853 {
854 CloseClient(con);
855 return (0);
856 }
857
a74b005d 858 switch (con->http.state)
859 {
860 case HTTP_WAITING :
861 /*
862 * See if we've received a request line...
863 */
864
865 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
93894a43 866 {
867 CloseClient(con);
868 return (0);
869 }
a74b005d 870
871 /*
872 * Ignore blank request lines...
873 */
874
875 if (line[0] == '\0')
876 break;
877
878 /*
879 * Clear other state variables...
880 */
881
882 httpClearFields(HTTP(con));
883
884 con->http.activity = time(NULL);
885 con->http.version = HTTP_1_0;
886 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
887 con->http.data_encoding = HTTP_ENCODE_LENGTH;
888 con->http.data_remaining = 0;
6a0c519d 889 con->operation = HTTP_WAITING;
890 con->bytes = 0;
a74b005d 891 con->file = 0;
892 con->pipe_pid = 0;
893 con->username[0] = '\0';
894 con->password[0] = '\0';
895 con->uri[0] = '\0';
896
fc757c63 897 ClearString(&con->command);
898 ClearString(&con->options);
899
a74b005d 900 if (con->language != NULL)
901 {
902 cupsLangFree(con->language);
903 con->language = NULL;
904 }
905
906 /*
907 * Grab the request line...
908 */
909
970017a4 910 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
a74b005d 911 {
912 case 1 :
434ddc80 913 LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
a74b005d 914 SendError(con, HTTP_BAD_REQUEST);
c3c5af5e 915 ShutdownClient(con);
2d5e06dd 916 return (1);
a74b005d 917 case 2 :
918 con->http.version = HTTP_0_9;
919 break;
920 case 3 :
921 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
922 {
434ddc80 923 LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
a74b005d 924 SendError(con, HTTP_BAD_REQUEST);
c3c5af5e 925 ShutdownClient(con);
2d5e06dd 926 return (1);
a74b005d 927 }
928
929 if (major < 2)
930 {
931 con->http.version = (http_version_t)(major * 100 + minor);
753453e4 932 if (con->http.version == HTTP_1_1 && KeepAlive)
a74b005d 933 con->http.keep_alive = HTTP_KEEPALIVE_ON;
934 else
935 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
936 }
937 else
938 {
939 SendError(con, HTTP_NOT_SUPPORTED);
c3c5af5e 940 ShutdownClient(con);
2d5e06dd 941 return (1);
a74b005d 942 }
943 break;
944 }
945
f726e5b9 946 /*
947 * Handle full URLs in the request line...
948 */
949
38e6ed71 950 if (con->uri[0] != '/' && strcmp(con->uri, "*"))
f726e5b9 951 {
952 char method[HTTP_MAX_URI], /* Method/scheme */
953 userpass[HTTP_MAX_URI], /* Username:password */
954 hostname[HTTP_MAX_URI], /* Hostname */
955 resource[HTTP_MAX_URI]; /* Resource path */
956 int port; /* Port number */
957
958
959 /*
960 * Separate the URI into its components...
961 */
962
963 httpSeparate(con->uri, method, userpass, hostname, &port, resource);
964
965 /*
966 * Only allow URIs with the servername, localhost, or an IP
967 * address...
968 */
969
970 if (strcasecmp(hostname, ServerName) &&
971 strcasecmp(hostname, "localhost") &&
972 !isdigit(hostname[0]))
973 {
974 /*
975 * Nope, we don't do proxies...
976 */
977
978 LogMessage(L_ERROR, "Bad URI \"%s\" in request!", con->uri);
979 SendError(con, HTTP_METHOD_NOT_ALLOWED);
980 ShutdownClient(con);
981 return (1);
982 }
983
984 /*
985 * Copy the resource portion back into the URI; both resource and
986 * con->uri are HTTP_MAX_URI bytes in size...
987 */
988
989 strcpy(con->uri, resource);
990 }
991
a74b005d 992 /*
993 * Process the request...
994 */
995
996 if (strcmp(operation, "GET") == 0)
997 con->http.state = HTTP_GET;
998 else if (strcmp(operation, "PUT") == 0)
999 con->http.state = HTTP_PUT;
1000 else if (strcmp(operation, "POST") == 0)
1001 con->http.state = HTTP_POST;
1002 else if (strcmp(operation, "DELETE") == 0)
1003 con->http.state = HTTP_DELETE;
1004 else if (strcmp(operation, "TRACE") == 0)
1005 con->http.state = HTTP_TRACE;
a74b005d 1006 else if (strcmp(operation, "OPTIONS") == 0)
1007 con->http.state = HTTP_OPTIONS;
1008 else if (strcmp(operation, "HEAD") == 0)
1009 con->http.state = HTTP_HEAD;
1010 else
1011 {
434ddc80 1012 LogMessage(L_ERROR, "Bad operation \"%s\"!", operation);
a74b005d 1013 SendError(con, HTTP_BAD_REQUEST);
c3c5af5e 1014 ShutdownClient(con);
2d5e06dd 1015 return (1);
a74b005d 1016 }
1017
6a0c519d 1018 con->start = time(NULL);
1019 con->operation = con->http.state;
1020
5ea8888e 1021 LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
4a64fdb7 1022 operation, con->uri,
1023 con->http.version / 100, con->http.version % 100);
a74b005d 1024
1025 con->http.status = HTTP_OK;
a74b005d 1026
8f85ff77 1027 case HTTP_OPTIONS :
a74b005d 1028 case HTTP_DELETE :
1029 case HTTP_GET :
1030 case HTTP_HEAD :
1031 case HTTP_POST :
1032 case HTTP_PUT :
1033 case HTTP_TRACE :
1034 /*
1035 * Parse incoming parameters until the status changes...
1036 */
1037
1038 status = httpUpdate(HTTP(con));
1039
1040 if (status != HTTP_OK && status != HTTP_CONTINUE)
1041 {
1042 SendError(con, HTTP_BAD_REQUEST);
c3c5af5e 1043 ShutdownClient(con);
2d5e06dd 1044 return (1);
a74b005d 1045 }
1046 break;
d21a7597 1047
1048 default :
1049 break; /* Anti-compiler-warning-code */
a74b005d 1050 }
1051
1052 /*
1053 * Handle new transfers...
1054 */
1055
1056 if (status == HTTP_OK)
1057 {
5a617005 1058 if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
08c0ff15 1059 {
1060 /*
1061 * Figure out the locale from the Accept-Language and Content-Type
1062 * fields...
1063 */
1064
1065 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ',')) != NULL)
1066 *ptr = '\0';
1067
1068 if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ';')) != NULL)
1069 *ptr = '\0';
1070
1071 if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "charset=")) != NULL)
1072 {
1073 /*
1074 * Combine language and charset, and trim any extra params in the
1075 * content-type.
1076 */
1077
1078 snprintf(locale, sizeof(locale), "%s.%s",
1079 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8);
1080
1081 if ((ptr = strchr(locale, ',')) != NULL)
1082 *ptr = '\0';
1083 }
1084 else
1085 snprintf(locale, sizeof(locale), "%s.%s",
1086 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], DefaultCharset);
1087
1088 con->language = cupsLangGet(locale);
1089 }
5a617005 1090 else
08c0ff15 1091 con->language = cupsLangGet(DefaultLocale);
a74b005d 1092
d2122fde 1093 decode_auth(con);
a74b005d 1094
753453e4 1095 if (strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) == 0 &&
1096 KeepAlive)
b1ac1113 1097 con->http.keep_alive = HTTP_KEEPALIVE_ON;
1098
a74b005d 1099 if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
d558743f 1100 con->http.version >= HTTP_1_1)
a74b005d 1101 {
d558743f 1102 /*
1103 * HTTP/1.1 and higher require the "Host:" field...
1104 */
1105
a74b005d 1106 if (!SendError(con, HTTP_BAD_REQUEST))
1107 {
1108 CloseClient(con);
1109 return (0);
1110 }
1111 }
8f85ff77 1112 else if (con->operation == HTTP_OPTIONS)
1113 {
1114 /*
1115 * Do OPTIONS command...
1116 */
1117
753453e4 1118 if ((best = FindBest(con->uri, con->http.state)) != NULL &&
8f85ff77 1119 best->type != AUTH_NONE)
1120 {
1121 if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL))
1122 {
1123 CloseClient(con);
1124 return (0);
1125 }
1126 }
a75c006a 1127
b5cb0608 1128 if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
a75c006a 1129 con->http.tls == NULL)
8f85ff77 1130 {
bcf61448 1131#ifdef HAVE_SSL
a75c006a 1132 /*
1133 * Do encryption stuff...
1134 */
1135
b38fb7fc 1136 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
8f85ff77 1137 {
1138 CloseClient(con);
1139 return (0);
1140 }
a75c006a 1141
1142 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
b5cb0608 1143 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1c6682dd 1144 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
a75c006a 1145 httpPrintf(HTTP(con), "\r\n");
1146
1147 EncryptClient(con);
1148#else
1149 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
1150 {
1151 CloseClient(con);
1152 return (0);
1153 }
bcf61448 1154#endif /* HAVE_SSL */
a75c006a 1155 }
1156
65c098da 1157 if (con->http.expect)
1158 {
1159 /**** TODO: send expected header ****/
1160 }
1161
a75c006a 1162 if (!SendHeader(con, HTTP_OK, NULL))
1163 {
1164 CloseClient(con);
1165 return (0);
8f85ff77 1166 }
1167
753453e4 1168 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
1c6682dd 1169 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
8f85ff77 1170 httpPrintf(HTTP(con), "\r\n");
1171 }
bd84e0d1 1172 else if (strstr(con->uri, "..") != NULL)
a74b005d 1173 {
1174 /*
1175 * Protect against malicious users!
1176 */
1177
1178 if (!SendError(con, HTTP_FORBIDDEN))
1179 {
1180 CloseClient(con);
1181 return (0);
1182 }
1183 }
b38fb7fc 1184 else
a74b005d 1185 {
b5cb0608 1186 if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
1187 con->http.tls == NULL)
b38fb7fc 1188 {
bcf61448 1189#ifdef HAVE_SSL
b38fb7fc 1190 /*
1191 * Do encryption stuff...
1192 */
f3d580b9 1193
b38fb7fc 1194 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
1195 {
1196 CloseClient(con);
1197 return (0);
1198 }
f969a074 1199
b38fb7fc 1200 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
b5cb0608 1201 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1c6682dd 1202 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
b38fb7fc 1203 httpPrintf(HTTP(con), "\r\n");
1204
1205 EncryptClient(con);
77425121 1206
1207 status = IsAuthorized(con);
b38fb7fc 1208#else
1209 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
1210 {
1211 CloseClient(con);
1212 return (0);
1213 }
bcf61448 1214#endif /* HAVE_SSL */
b38fb7fc 1215 }
b5cb0608 1216
77425121 1217 if (status != HTTP_OK)
b38fb7fc 1218 {
2d5e06dd 1219 LogMessage(L_DEBUG2, "ReadClient: Unauthorized request for %s...\n",
1220 con->uri);
b38fb7fc 1221 SendError(con, status);
c3c5af5e 1222 ShutdownClient(con);
2d5e06dd 1223 return (1);
b38fb7fc 1224 }
1225
65c098da 1226 if (con->http.expect)
1227 {
1228 /**** TODO: send expected header ****/
1229 }
1230
b38fb7fc 1231 switch (con->http.state)
1232 {
1233 case HTTP_GET_SEND :
1234 if (strncmp(con->uri, "/printers/", 10) == 0 &&
1235 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
2a8fc30c 1236 {
b38fb7fc 1237 /*
1238 * Send PPD file - get the real printer name since printer
1239 * names are not case sensitive but filenames can be...
1240 */
2a8fc30c 1241
b38fb7fc 1242 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
f3d580b9 1243
b38fb7fc 1244 if ((p = FindPrinter(con->uri + 10)) != NULL)
1245 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1246 else
1247 {
1248 if (!SendError(con, HTTP_NOT_FOUND))
1249 {
1250 CloseClient(con);
1251 return (0);
1252 }
a74b005d 1253
b38fb7fc 1254 break;
1255 }
b14d90ba 1256 }
b38fb7fc 1257
753453e4 1258 if ((strncmp(con->uri, "/admin", 6) == 0 &&
1259 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
b38fb7fc 1260 strncmp(con->uri, "/printers", 9) == 0 ||
1261 strncmp(con->uri, "/classes", 8) == 0 ||
1262 strncmp(con->uri, "/jobs", 5) == 0)
b14d90ba 1263 {
b38fb7fc 1264 /*
1265 * Send CGI output...
1266 */
1267
1268 if (strncmp(con->uri, "/admin", 6) == 0)
1269 {
fc757c63 1270 SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
1271 SetString(&con->options, con->uri + 6);
b38fb7fc 1272 }
1273 else if (strncmp(con->uri, "/printers", 9) == 0)
1274 {
fc757c63 1275 SetStringf(&con->command, "%s/cgi-bin/printers.cgi", ServerBin);
1276 SetString(&con->options, con->uri + 9);
b38fb7fc 1277 }
1278 else if (strncmp(con->uri, "/classes", 8) == 0)
1279 {
fc757c63 1280 SetStringf(&con->command, "%s/cgi-bin/classes.cgi", ServerBin);
1281 SetString(&con->options, con->uri + 8);
b38fb7fc 1282 }
1283 else
1284 {
fc757c63 1285 SetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin);
1286 SetString(&con->options, con->uri + 5);
b38fb7fc 1287 }
1288
1289 if (con->options[0] == '/')
ff40b65e 1290 cups_strcpy(con->options, con->options + 1);
b38fb7fc 1291
1292 if (!SendCommand(con, con->command, con->options))
1293 {
1294 if (!SendError(con, HTTP_NOT_FOUND))
1295 {
1296 CloseClient(con);
1297 return (0);
1298 }
1299 }
1300 else
1301 LogRequest(con, HTTP_OK);
1302
1303 if (con->http.version <= HTTP_1_0)
1304 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
b14d90ba 1305 }
753453e4 1306 else if (strncmp(con->uri, "/admin/conf/", 12) == 0 &&
1307 (strchr(con->uri + 12, '/') != NULL ||
1308 strlen(con->uri) == 12))
1309 {
1310 /*
1311 * GET can only be done to configuration files under
1312 * /admin/conf...
1313 */
1314
1315 if (!SendError(con, HTTP_FORBIDDEN))
1316 {
1317 CloseClient(con);
1318 return (0);
1319 }
1320
1321 break;
1322 }
b14d90ba 1323 else
1324 {
b38fb7fc 1325 /*
1326 * Serve a file...
1327 */
1328
6db7190f 1329 if ((filename = get_file(con, &filestats, buf,
1330 sizeof(buf))) == NULL)
b38fb7fc 1331 {
1332 if (!SendError(con, HTTP_NOT_FOUND))
1333 {
1334 CloseClient(con);
1335 return (0);
1336 }
e6221033 1337
1338 break;
1339 }
1340
d59a189c 1341 type = mimeFileType(MimeDatabase, filename, NULL);
e6221033 1342
1343 if (IsCGI(con, filename, &filestats, type))
1344 {
1345 if (!SendCommand(con, con->command, con->options))
1346 {
1347 if (!SendError(con, HTTP_NOT_FOUND))
1348 {
1349 CloseClient(con);
1350 return (0);
1351 }
1352 }
1353 else
1354 LogRequest(con, HTTP_OK);
1355
1356 if (con->http.version <= HTTP_1_0)
1357 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1358 break;
b38fb7fc 1359 }
e6221033 1360
1361 if (!check_if_modified(con, &filestats))
b38fb7fc 1362 {
1363 if (!SendError(con, HTTP_NOT_MODIFIED))
1364 {
1365 CloseClient(con);
1366 return (0);
1367 }
1368 }
1369 else
1370 {
b38fb7fc 1371 if (type == NULL)
1372 strcpy(line, "text/plain");
1373 else
1374 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
1375
1376 if (!SendFile(con, HTTP_OK, filename, line, &filestats))
1377 {
1378 CloseClient(con);
1379 return (0);
1380 }
1381 }
b14d90ba 1382 }
b38fb7fc 1383 break;
1384
1385 case HTTP_POST_RECV :
1386 /*
1387 * See if the POST request includes a Content-Length field, and if
1388 * so check the length against any limits that are set...
1389 */
b14d90ba 1390
b38fb7fc 1391 LogMessage(L_DEBUG2, "POST %s", con->uri);
1392 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
b14d90ba 1393
b38fb7fc 1394 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1395 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
1396 MaxRequestSize > 0)
a74b005d 1397 {
b38fb7fc 1398 /*
1399 * Request too large...
1400 */
1401
1402 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
a74b005d 1403 {
b38fb7fc 1404 CloseClient(con);
a74b005d 1405 return (0);
1406 }
b38fb7fc 1407
1408 break;
a74b005d 1409 }
901b295d 1410 else if (atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) < 0)
1411 {
1412 /*
1413 * Negative content lengths are invalid!
1414 */
1415
1416 if (!SendError(con, HTTP_BAD_REQUEST))
1417 {
1418 CloseClient(con);
1419 return (0);
1420 }
1421
1422 break;
1423 }
a74b005d 1424
b38fb7fc 1425 /*
1426 * See what kind of POST request this is; for IPP requests the
1427 * content-type field will be "application/ipp"...
a74b005d 1428 */
1429
b38fb7fc 1430 if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
1431 con->request = ippNew();
753453e4 1432 else if ((strncmp(con->uri, "/admin", 6) == 0 &&
1433 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
b38fb7fc 1434 strncmp(con->uri, "/printers", 9) == 0 ||
1435 strncmp(con->uri, "/classes", 8) == 0 ||
1436 strncmp(con->uri, "/jobs", 5) == 0)
a74b005d 1437 {
b38fb7fc 1438 /*
1439 * CGI request...
1440 */
1441
1442 if (strncmp(con->uri, "/admin", 6) == 0)
a74b005d 1443 {
fc757c63 1444 SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
1445 SetString(&con->options, con->uri + 6);
a74b005d 1446 }
b38fb7fc 1447 else if (strncmp(con->uri, "/printers", 9) == 0)
a74b005d 1448 {
fc757c63 1449 SetStringf(&con->command, "%s/cgi-bin/printers.cgi", ServerBin);
1450 SetString(&con->options, con->uri + 9);
b38fb7fc 1451 }
1452 else if (strncmp(con->uri, "/classes", 8) == 0)
1453 {
fc757c63 1454 SetStringf(&con->command, "%s/cgi-bin/classes.cgi", ServerBin);
1455 SetString(&con->options, con->uri + 8);
a74b005d 1456 }
e31bfb6e 1457 else
a74b005d 1458 {
fc757c63 1459 SetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin);
1460 SetString(&con->options, con->uri + 5);
a74b005d 1461 }
8b43895a 1462
b38fb7fc 1463 if (con->options[0] == '/')
ff40b65e 1464 cups_strcpy(con->options, con->options + 1);
f63a2256 1465
b38fb7fc 1466 LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"",
1467 con->http.fd, con->command, con->options);
8b43895a 1468
b38fb7fc 1469 if (con->http.version <= HTTP_1_0)
1470 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1471 }
e6221033 1472 else
8b43895a 1473 {
e6221033 1474 /*
1475 * POST to a file...
1476 */
1477
1478 if ((filename = get_file(con, &filestats, buf,
1479 sizeof(buf))) == NULL)
1480 {
1481 if (!SendError(con, HTTP_NOT_FOUND))
1482 {
1483 CloseClient(con);
1484 return (0);
1485 }
1486
1487 break;
1488 }
1489
d59a189c 1490 type = mimeFileType(MimeDatabase, filename, NULL);
e6221033 1491
1492 if (!IsCGI(con, filename, &filestats, type))
1493 {
1494 /*
1495 * Only POST to CGI's...
1496 */
1497
1498 if (!SendError(con, HTTP_UNAUTHORIZED))
1499 {
1500 CloseClient(con);
1501 return (0);
1502 }
1503 }
8b43895a 1504 }
8b43895a 1505 break;
b14d90ba 1506
b38fb7fc 1507 case HTTP_PUT_RECV :
753453e4 1508 /*
1509 * Validate the resource name...
1510 */
1511
1512 if (strncmp(con->uri, "/admin/conf/", 12) != 0 ||
1513 strchr(con->uri + 12, '/') != NULL ||
1514 strlen(con->uri) == 12)
1515 {
1516 /*
1517 * PUT can only be done to configuration files under
1518 * /admin/conf...
1519 */
1520
1521 if (!SendError(con, HTTP_FORBIDDEN))
1522 {
1523 CloseClient(con);
1524 return (0);
1525 }
1526
1527 break;
1528 }
1529
1530 /*
1531 * See if the PUT request includes a Content-Length field, and if
1532 * so check the length against any limits that are set...
1533 */
1534
1535 LogMessage(L_DEBUG2, "PUT %s", con->uri);
1536 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1537
1538 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
1539 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
1540 MaxRequestSize > 0)
1541 {
1542 /*
1543 * Request too large...
1544 */
1545
1546 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1547 {
1548 CloseClient(con);
1549 return (0);
1550 }
1551
1552 break;
1553 }
0cd4970e 1554 else if (atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) < 0)
1555 {
1556 /*
1557 * Negative content lengths are invalid!
1558 */
1559
1560 if (!SendError(con, HTTP_BAD_REQUEST))
1561 {
1562 CloseClient(con);
1563 return (0);
1564 }
1565
1566 break;
1567 }
753453e4 1568
1569 /*
1570 * Open a temporary file to hold the request...
1571 */
1572
fc757c63 1573 SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
753453e4 1574 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1575 fchmod(con->file, 0640);
aa7b8dba 1576 fchown(con->file, getuid(), Group);
753453e4 1577
1578 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
1579 con->filename, con->file);
1580
1581 if (con->file < 0)
1582 {
1583 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1584 {
1585 CloseClient(con);
1586 return (0);
1587 }
1588 }
1589 break;
1590
b38fb7fc 1591 case HTTP_DELETE :
1592 case HTTP_TRACE :
1593 SendError(con, HTTP_NOT_IMPLEMENTED);
c3c5af5e 1594 ShutdownClient(con);
2d5e06dd 1595 return (1);
b14d90ba 1596
b38fb7fc 1597 case HTTP_HEAD :
d588a92e 1598 if (strncmp(con->uri, "/printers/", 10) == 0 &&
b38fb7fc 1599 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
bd84e0d1 1600 {
b38fb7fc 1601 /*
d588a92e 1602 * Send PPD file - get the real printer name since printer
1603 * names are not case sensitive but filenames can be...
b38fb7fc 1604 */
a74b005d 1605
d588a92e 1606 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1607
1608 if ((p = FindPrinter(con->uri + 10)) != NULL)
1609 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1610 else
1611 {
1612 if (!SendError(con, HTTP_NOT_FOUND))
1613 {
1614 CloseClient(con);
1615 return (0);
1616 }
1617
1618 break;
1619 }
b38fb7fc 1620 }
a74b005d 1621
753453e4 1622 if ((strncmp(con->uri, "/admin/", 7) == 0 &&
1623 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
b38fb7fc 1624 strncmp(con->uri, "/printers/", 10) == 0 ||
1625 strncmp(con->uri, "/classes/", 9) == 0 ||
1626 strncmp(con->uri, "/jobs/", 6) == 0)
1627 {
1628 /*
1629 * CGI output...
1630 */
f3d580b9 1631
b38fb7fc 1632 if (!SendHeader(con, HTTP_OK, "text/html"))
1633 {
1634 CloseClient(con);
1635 return (0);
1636 }
f3d580b9 1637
b38fb7fc 1638 if (httpPrintf(HTTP(con), "\r\n") < 0)
1639 {
1640 CloseClient(con);
1641 return (0);
1642 }
a74b005d 1643
b38fb7fc 1644 LogRequest(con, HTTP_OK);
a74b005d 1645 }
753453e4 1646 else if (strncmp(con->uri, "/admin/conf/", 12) == 0 &&
1647 (strchr(con->uri + 12, '/') != NULL ||
1648 strlen(con->uri) == 12))
1649 {
1650 /*
1651 * HEAD can only be done to configuration files under
1652 * /admin/conf...
1653 */
1654
1655 if (!SendError(con, HTTP_FORBIDDEN))
1656 {
1657 CloseClient(con);
1658 return (0);
1659 }
1660
1661 break;
1662 }
6db7190f 1663 else if ((filename = get_file(con, &filestats, buf,
1664 sizeof(buf))) == NULL)
a74b005d 1665 {
b38fb7fc 1666 if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
1667 {
1668 CloseClient(con);
1669 return (0);
1670 }
6a0c519d 1671
b38fb7fc 1672 LogRequest(con, HTTP_NOT_FOUND);
a74b005d 1673 }
b38fb7fc 1674 else if (!check_if_modified(con, &filestats))
1675 {
1676 if (!SendError(con, HTTP_NOT_MODIFIED))
1677 {
1678 CloseClient(con);
1679 return (0);
1680 }
6a0c519d 1681
b38fb7fc 1682 LogRequest(con, HTTP_NOT_MODIFIED);
a74b005d 1683 }
b38fb7fc 1684 else
1685 {
1686 /*
1687 * Serve a file...
1688 */
6a0c519d 1689
d59a189c 1690 type = mimeFileType(MimeDatabase, filename, NULL);
b38fb7fc 1691 if (type == NULL)
1692 strcpy(line, "text/plain");
1693 else
1694 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
a74b005d 1695
b38fb7fc 1696 if (!SendHeader(con, HTTP_OK, line))
1697 {
1698 CloseClient(con);
1699 return (0);
1700 }
a74b005d 1701
b38fb7fc 1702 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1703 httpGetDateString(filestats.st_mtime)) < 0)
1704 {
1705 CloseClient(con);
1706 return (0);
1707 }
a74b005d 1708
753453e4 1709 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1710 (unsigned long)filestats.st_size) < 0)
b38fb7fc 1711 {
1712 CloseClient(con);
1713 return (0);
1714 }
1715
1716 LogRequest(con, HTTP_OK);
a74b005d 1717 }
1718
b38fb7fc 1719 if (httpPrintf(HTTP(con), "\r\n") < 0)
a74b005d 1720 {
1721 CloseClient(con);
1722 return (0);
1723 }
6a0c519d 1724
b38fb7fc 1725 con->http.state = HTTP_WAITING;
1726 break;
a74b005d 1727
b38fb7fc 1728 default :
1729 break; /* Anti-compiler-warning-code */
1730 }
a74b005d 1731 }
1732 }
1733
1734 /*
1735 * Handle any incoming data...
1736 */
1737
1738 switch (con->http.state)
1739 {
1740 case HTTP_PUT_RECV :
753453e4 1741 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
1742 con->http.fd,
1743 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
1744 con->http.data_remaining, con->file);
1745
1746 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
1747 {
1748 CloseClient(con);
1749 return (0);
1750 }
1751 else if (bytes > 0)
1752 {
1753 con->bytes += bytes;
1754
1755 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
1756 con->http.fd, bytes, con->file);
1757
1758 if (write(con->file, line, bytes) < bytes)
1759 {
1760 LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s",
1761 bytes, con->filename, strerror(errno));
1762
6cd03542 1763 LogMessage(L_DEBUG2, "ReadClient: Closing data file %d...",
1764 con->file);
1765
753453e4 1766 close(con->file);
1767 con->file = 0;
1768 unlink(con->filename);
fc757c63 1769 ClearString(&con->filename);
753453e4 1770
1771 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1772 {
1773 CloseClient(con);
1774 return (0);
1775 }
1776 }
1777 }
1778
1779 if (con->http.state == HTTP_WAITING)
1780 {
1781 /*
1782 * End of file, see how big it is...
1783 */
1784
1785 fstat(con->file, &filestats);
1786
1787 LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
aa7e125a 1788 con->http.fd, con->file, (int)filestats.st_size);
753453e4 1789
1790 close(con->file);
1791 con->file = 0;
1792
1793 if (filestats.st_size > MaxRequestSize &&
1794 MaxRequestSize > 0)
1795 {
1796 /*
1797 * Request is too big; remove it and send an error...
1798 */
1799
1800 LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
1801 con->http.fd, con->filename);
1802 unlink(con->filename);
fc757c63 1803 ClearString(&con->filename);
753453e4 1804
1805 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1806 {
1807 CloseClient(con);
1808 return (0);
1809 }
1810 }
1811
1812 /*
1813 * Install the configuration file...
1814 */
1815
1816 status = install_conf_file(con);
1817
1818 /*
1819 * Return the status to the client...
1820 */
1821
1822 if (!SendError(con, status))
1823 {
1824 CloseClient(con);
1825 return (0);
1826 }
1827 }
a74b005d 1828 break;
1829
1830 case HTTP_POST_RECV :
58e8cf34 1831 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
93894a43 1832 con->http.fd,
a74b005d 1833 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
f63a2256 1834 con->http.data_remaining, con->file);
a74b005d 1835
b14d90ba 1836 if (con->request != NULL)
1837 {
1838 /*
1839 * Grab any request data from the connection...
1840 */
1841
bd176c9c 1842 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
1843 {
5ea8888e 1844 LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!",
bd176c9c 1845 con->http.fd);
3955a26e 1846
1847 if (!SendError(con, HTTP_BAD_REQUEST))
1848 {
1849 CloseClient(con);
1850 return (0);
1851 }
1852
1853 ShutdownClient(con);
1854 return (1);
bd176c9c 1855 }
1856 else if (ipp_state != IPP_DATA)
b14d90ba 1857 break;
68ff4f01 1858 else
1859 con->bytes += ippLength(con->request);
f63a2256 1860 }
b14d90ba 1861
f63a2256 1862 if (con->file == 0 && con->http.state != HTTP_POST_SEND)
1863 {
1864 /*
1865 * Create a file as needed for the request data...
1866 */
1d2c70a6 1867
fc757c63 1868 SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
f63a2256 1869 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1870 fchmod(con->file, 0640);
aa7b8dba 1871 fchown(con->file, getuid(), Group);
b14d90ba 1872
a61a2be6 1873 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
1874 con->filename, con->file);
b14d90ba 1875
f63a2256 1876 if (con->file < 0)
1877 {
1878 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
b14d90ba 1879 {
f63a2256 1880 CloseClient(con);
1881 return (0);
b14d90ba 1882 }
1883 }
f63a2256 1884 }
b14d90ba 1885
bfa1abf0 1886 if (con->http.state != HTTP_POST_SEND)
a74b005d 1887 {
bfa1abf0 1888 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
a74b005d 1889 {
bfa1abf0 1890 CloseClient(con);
1891 return (0);
1892 }
1893 else if (bytes > 0)
1894 {
1895 con->bytes += bytes;
a74b005d 1896
a61a2be6 1897 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
1898 con->http.fd, bytes, con->file);
bfa1abf0 1899
1900 if (write(con->file, line, bytes) < bytes)
a74b005d 1901 {
a61a2be6 1902 LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s",
1903 bytes, con->filename, strerror(errno));
1904
6cd03542 1905 LogMessage(L_DEBUG2, "ReadClient: Closing file %d...",
1906 con->file);
1907
bfa1abf0 1908 close(con->file);
1909 con->file = 0;
1910 unlink(con->filename);
fc757c63 1911 ClearString(&con->filename);
bfa1abf0 1912
1913 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1914 {
1915 CloseClient(con);
1916 return (0);
1917 }
a74b005d 1918 }
1919 }
6faac3f3 1920 else if (con->http.state == HTTP_POST_RECV)
1921 {
1922 return (0);
1923 }
bd176c9c 1924 else if (con->http.state != HTTP_POST_SEND)
1925 {
1926 CloseClient(con);
1927 return (0);
1928 }
a74b005d 1929 }
93894a43 1930
1931 if (con->http.state == HTTP_POST_SEND)
1932 {
bfa1abf0 1933 if (con->file)
1934 {
8b43895a 1935 fstat(con->file, &filestats);
bd917997 1936
1937 LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
aa7e125a 1938 con->http.fd, con->file, (int)filestats.st_size);
bd917997 1939
bfa1abf0 1940 close(con->file);
1941 con->file = 0;
8b43895a 1942
1943 if (filestats.st_size > MaxRequestSize &&
1944 MaxRequestSize > 0)
1945 {
1946 /*
1947 * Request is too big; remove it and send an error...
1948 */
1949
a61a2be6 1950 LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
1951 con->http.fd, con->filename);
8b43895a 1952 unlink(con->filename);
fc757c63 1953 ClearString(&con->filename);
8b43895a 1954
1955 if (con->request)
1956 {
1957 /*
1958 * Delete any IPP request data...
1959 */
1960
1961 ippDelete(con->request);
1962 con->request = NULL;
1963 }
1964
1965 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1966 {
1967 CloseClient(con);
1968 return (0);
1969 }
1970 }
f63a2256 1971
fc757c63 1972 if (con->command)
f63a2256 1973 {
1974 if (!SendCommand(con, con->command, con->options))
1975 {
1976 if (!SendError(con, HTTP_NOT_FOUND))
1977 {
1978 CloseClient(con);
1979 return (0);
1980 }
1981 }
1982 else
1983 LogRequest(con, HTTP_OK);
1984 }
bfa1abf0 1985 }
93894a43 1986
1987 if (con->request)
6e3d4276 1988 return (ProcessIPPRequest(con));
93894a43 1989 }
a74b005d 1990 break;
d21a7597 1991
1992 default :
1993 break; /* Anti-compiler-warning-code */
a74b005d 1994 }
1995
1996 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
1997 {
1998 CloseClient(con);
1999 return (0);
2000 }
2001 else
2002 return (1);
2003}
2004
2005
a74b005d 2006/*
2007 * 'SendCommand()' - Send output from a command via HTTP.
2008 */
2009
2010int
2011SendCommand(client_t *con,
a74b005d 2012 char *command,
b14d90ba 2013 char *options)
a74b005d 2014{
f63a2256 2015 int fd;
2016
2017
fc757c63 2018 if (con->filename)
f63a2256 2019 fd = open(con->filename, O_RDONLY);
2020 else
2021 fd = open("/dev/null", O_RDONLY);
2022
2023 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
2024
2025 close(fd);
a74b005d 2026
b5cb0608 2027 LogMessage(L_INFO, "Started \"%s\" (pid=%d)", command, con->pipe_pid);
2028
2029 LogMessage(L_DEBUG, "SendCommand() %d file=%d", con->http.fd, con->file);
a74b005d 2030
2031 if (con->pipe_pid == 0)
2032 return (0);
2033
2034 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2035
27eba2dd 2036 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to InputSet...", con->file);
e668e627 2037 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to OutputSet...",
2038 con->http.fd);
2039
f3bc1068 2040 FD_SET(con->file, InputSet);
2041 FD_SET(con->http.fd, OutputSet);
a74b005d 2042
96df88bb 2043 if (!SendHeader(con, HTTP_OK, NULL))
a74b005d 2044 return (0);
2045
2046 if (con->http.version == HTTP_1_1)
2047 {
2048 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
2049
2050 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
2051 return (0);
2052 }
2053
13486cb7 2054 con->got_fields = 0;
2055 con->field_col = 0;
2056
a74b005d 2057 return (1);
2058}
2059
2060
2061/*
2062 * 'SendError()' - Send an error message via HTTP.
2063 */
2064
2065int /* O - 1 if successful, 0 otherwise */
2066SendError(client_t *con, /* I - Connection */
2067 http_status_t code) /* I - Error code */
2068{
83e740a5 2069 char message[1024]; /* Message for user */
a74b005d 2070
2071
6a0c519d 2072 /*
2073 * Put the request in the access_log file...
2074 */
2075
993e15da 2076 if (con->operation > HTTP_WAITING)
2077 LogRequest(con, code);
6a0c519d 2078
753453e4 2079 LogMessage(L_DEBUG, "SendError() %d code=%d", con->http.fd, code);
a61a2be6 2080
a74b005d 2081 /*
2082 * To work around bugs in some proxies, don't use Keep-Alive for some
2083 * error messages...
2084 */
2085
2086 if (code >= HTTP_BAD_REQUEST)
2087 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
2088
2089 /*
2090 * Send an error message back to the client. If the error code is a
2091 * 400 or 500 series, make sure the message contains some text, too!
2092 */
2093
2094 if (!SendHeader(con, code, NULL))
2095 return (0);
2096
bcf61448 2097#ifdef HAVE_SSL
a75c006a 2098 if (code == HTTP_UPGRADE_REQUIRED)
a75c006a 2099 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
2100 return (0);
2101
b5cb0608 2102 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0)
2103 return (0);
bcf61448 2104#endif /* HAVE_SSL */
a75c006a 2105
55809c6f 2106 if ((con->http.version >= HTTP_1_1 && !con->http.keep_alive) ||
2107 (code >= HTTP_BAD_REQUEST && code != HTTP_UPGRADE_REQUIRED))
a74b005d 2108 {
2109 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
2110 return (0);
2111 }
2112
2113 if (code >= HTTP_BAD_REQUEST)
2114 {
2115 /*
2116 * Send a human-readable error message.
2117 */
2118
04de52f8 2119 snprintf(message, sizeof(message),
970017a4 2120 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
2121 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
2122 code, httpStatus(code), httpStatus(code),
2123 con->language ? con->language->messages[code] :
2124 httpStatus(code));
a74b005d 2125
2126 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
2127 return (0);
e0ba31cd 2128 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
2129 (int)strlen(message)) < 0)
a74b005d 2130 return (0);
2131 if (httpPrintf(HTTP(con), "\r\n") < 0)
2132 return (0);
97d73ddb 2133 if (httpPrintf(HTTP(con), "%s", message) < 0)
a74b005d 2134 return (0);
2135 }
2136 else if (httpPrintf(HTTP(con), "\r\n") < 0)
2137 return (0);
2138
2139 con->http.state = HTTP_WAITING;
2140
2141 return (1);
2142}
2143
2144
2145/*
2146 * 'SendFile()' - Send a file via HTTP.
2147 */
2148
2149int
2150SendFile(client_t *con,
2151 http_status_t code,
2152 char *filename,
2153 char *type,
2154 struct stat *filestats)
2155{
2156 con->file = open(filename, O_RDONLY);
2157
5ea8888e 2158 LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
a74b005d 2159
2160 if (con->file < 0)
2161 return (0);
2162
2163 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
2164
2165 con->pipe_pid = 0;
2166
2167 if (!SendHeader(con, code, type))
2168 return (0);
2169
2170 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
2171 return (0);
753453e4 2172 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
2173 (unsigned long)filestats->st_size) < 0)
a74b005d 2174 return (0);
2175 if (httpPrintf(HTTP(con), "\r\n") < 0)
2176 return (0);
2177
e668e627 2178 LogMessage(L_DEBUG2, "SendFile: Adding fd %d to OutputSet...", con->http.fd);
2179
f3bc1068 2180 FD_SET(con->http.fd, OutputSet);
a74b005d 2181
2182 return (1);
2183}
2184
2185
2186/*
93894a43 2187 * 'SendHeader()' - Send an HTTP request.
a74b005d 2188 */
2189
2190int /* O - 1 on success, 0 on failure */
2191SendHeader(client_t *con, /* I - Client to send to */
2192 http_status_t code, /* I - HTTP status code */
2193 char *type) /* I - MIME type of document */
2194{
83e740a5 2195 location_t *loc; /* Authentication location */
2196
2197
a74b005d 2198 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
ffe90a99 2199 con->http.version % 100, code, httpStatus(code)) < 0)
a74b005d 2200 return (0);
2201 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
2202 return (0);
bd84e0d1 2203 if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
a74b005d 2204 return (0);
2205 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
2206 {
2207 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
2208 return (0);
2209 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
2210 return (0);
2211 }
8f85ff77 2212 if (code == HTTP_METHOD_NOT_ALLOWED)
9bfcbffc 2213 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
8f85ff77 2214 return (0);
2215
5ea8888e 2216 if (code == HTTP_UNAUTHORIZED)
83e740a5 2217 {
753453e4 2218 /*
2219 * This already succeeded in IsAuthorized...
2220 */
2221
2222 loc = FindBest(con->uri, con->http.state);
83e740a5 2223
753453e4 2224 if (loc->type != AUTH_DIGEST)
83e740a5 2225 {
2226 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
2227 return (0);
2228 }
2229 else
2230 {
2231 if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" "
2232 "nonce=\"%s\"\r\n", con->http.hostname) < 0)
2233 return (0);
2234 }
2235 }
a74b005d 2236 if (con->language != NULL)
2237 {
2238 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
2239 con->language->language) < 0)
2240 return (0);
2241
2242 if (type != NULL)
2243 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
2244 cupsLangEncoding(con->language)) < 0)
2245 return (0);
2246 }
2247 else if (type != NULL)
2248 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
2249 return (0);
2250
2251 return (1);
2252}
2253
2254
c3c5af5e 2255/*
2256 * 'ShutdownClient()' - Shutdown the receiving end of a connection.
2257 */
2258
2259void
2260ShutdownClient(client_t *con) /* I - Client connection */
2261{
2262 /*
2263 * Shutdown the receiving end of the socket, since the client
2264 * still needs to read the error message...
2265 */
2266
2267 shutdown(con->http.fd, 0);
549d1888 2268 con->http.used = 0;
c3c5af5e 2269
de218675 2270 /*
2271 * Update the activity time so that we timeout after 30 seconds rather
2272 * then the current Timeout setting (300 by default). This prevents
2273 * some DoS situations...
2274 */
2275
2276 con->http.activity = time(NULL) - Timeout + 30;
2277
c3c5af5e 2278 LogMessage(L_DEBUG2, "ShutdownClient: Removing fd %d from InputSet...",
2279 con->http.fd);
2280
f3bc1068 2281 FD_CLR(con->http.fd, InputSet);
c3c5af5e 2282}
2283
2284
0a968cfb 2285/*
2286 * 'UpdateCGI()' - Read status messages from CGI scripts and programs.
2287 */
2288
2289void
2290UpdateCGI(void)
2291{
2292 int bytes; /* Number of bytes read */
2293 char *lineptr, /* Pointer to end of line in buffer */
2294 *message; /* Pointer to message text */
2295 int loglevel; /* Log level for message */
2296 static int bufused = 0; /* Number of bytes used in buffer */
2297 static char buffer[1024]; /* Status buffer */
2298
2299
2300 if ((bytes = read(CGIPipes[0], buffer + bufused,
2301 sizeof(buffer) - bufused - 1)) > 0)
2302 {
2303 bufused += bytes;
2304 buffer[bufused] = '\0';
2305 lineptr = strchr(buffer, '\n');
2306 }
2307 else if (bytes < 0 && errno == EINTR)
2308 return;
2309 else
2310 {
2311 lineptr = buffer + bufused;
2312 lineptr[1] = 0;
2313 }
2314
2315 if (bytes == 0 && bufused == 0)
2316 lineptr = NULL;
2317
2318 while (lineptr != NULL)
2319 {
2320 /*
2321 * Terminate each line and process it...
2322 */
2323
2324 *lineptr++ = '\0';
2325
2326 /*
2327 * Figure out the logging level...
2328 */
2329
2330 if (strncmp(buffer, "EMERG:", 6) == 0)
2331 {
2332 loglevel = L_EMERG;
2333 message = buffer + 6;
2334 }
2335 else if (strncmp(buffer, "ALERT:", 6) == 0)
2336 {
2337 loglevel = L_ALERT;
2338 message = buffer + 6;
2339 }
2340 else if (strncmp(buffer, "CRIT:", 5) == 0)
2341 {
2342 loglevel = L_CRIT;
2343 message = buffer + 5;
2344 }
2345 else if (strncmp(buffer, "ERROR:", 6) == 0)
2346 {
2347 loglevel = L_ERROR;
2348 message = buffer + 6;
2349 }
2350 else if (strncmp(buffer, "WARNING:", 8) == 0)
2351 {
2352 loglevel = L_WARN;
2353 message = buffer + 8;
2354 }
2355 else if (strncmp(buffer, "NOTICE:", 6) == 0)
2356 {
2357 loglevel = L_NOTICE;
2358 message = buffer + 6;
2359 }
2360 else if (strncmp(buffer, "INFO:", 5) == 0)
2361 {
2362 loglevel = L_INFO;
2363 message = buffer + 5;
2364 }
2365 else if (strncmp(buffer, "DEBUG:", 6) == 0)
2366 {
2367 loglevel = L_DEBUG;
2368 message = buffer + 6;
2369 }
2370 else if (strncmp(buffer, "DEBUG2:", 7) == 0)
2371 {
2372 loglevel = L_DEBUG2;
2373 message = buffer + 7;
2374 }
2375 else if (strncmp(buffer, "PAGE:", 5) == 0)
2376 {
2377 loglevel = L_PAGE;
2378 message = buffer + 5;
2379 }
2380 else
2381 {
2382 loglevel = L_DEBUG;
2383 message = buffer;
2384 }
2385
2386 /*
2387 * Skip leading whitespace in the message...
2388 */
2389
2390 while (isspace(*message))
2391 message ++;
2392
2393 LogMessage(loglevel, "[CGI] %s", message);
2394
2395 /*
2396 * Copy over the buffer data we've used up...
2397 */
2398
2399 strcpy(buffer, lineptr);
2400 bufused -= lineptr - buffer;
2401
2402 if (bufused < 0)
2403 bufused = 0;
2404
2405 lineptr = strchr(buffer, '\n');
2406 }
2407
2408 if (bytes <= 0)
2409 {
2410 /*
2411 * Fatal error on pipe - should never happen!
2412 */
2413
2414 LogMessage(L_ERROR, "UpdateCGI: error reading from CGI error pipe - %s",
2415 strerror(errno));
2416 }
2417}
2418
2419
a74b005d 2420/*
2421 * 'WriteClient()' - Write data to a client as needed.
2422 */
2423
ff49100f 2424int /* O - 1 if success, 0 if fail */
2425WriteClient(client_t *con) /* I - Client connection */
a74b005d 2426{
ff49100f 2427 int bytes; /* Number of bytes written */
13486cb7 2428 char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */
2429 char *bufptr; /* Pointer into buffer */
ff49100f 2430 ipp_state_t ipp_state; /* IPP state value */
a74b005d 2431
2432
2433 if (con->http.state != HTTP_GET_SEND &&
2434 con->http.state != HTTP_POST_SEND)
2435 return (1);
2436
ff49100f 2437 if (con->response != NULL)
bfa1abf0 2438 {
ff49100f 2439 ipp_state = ippWrite(&(con->http), con->response);
2440 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
bfa1abf0 2441 }
13486cb7 2442 else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
a74b005d 2443 {
13486cb7 2444 if (con->pipe_pid && !con->got_fields)
2445 {
2446 /*
2447 * Inspect the data for Content-Type and other fields.
2448 */
2449
2450 buf[bytes] = '\0';
2451
2452 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
2453 if (*bufptr == '\n')
2454 {
2455 /*
2456 * Send line to client...
2457 */
2458
2459 if (bufptr > buf && bufptr[-1] == '\r')
2460 bufptr[-1] = '\0';
2461 *bufptr++ = '\0';
2462
2463 httpPrintf(HTTP(con), "%s\r\n", buf);
58e8cf34 2464 LogMessage(L_DEBUG2, "WriteClient() %d %s", con->http.fd, buf);
13486cb7 2465
2466 /*
2467 * Update buffer...
2468 */
2469
2470 bytes -= (bufptr - buf);
2471 memcpy(buf, bufptr, bytes + 1);
2472 bufptr = buf - 1;
2473
2474 /*
2475 * See if the line was empty...
2476 */
2477
2478 if (con->field_col == 0)
2479 con->got_fields = 1;
2480 else
2481 con->field_col = 0;
2482 }
2483 else if (*bufptr != '\r')
2484 con->field_col ++;
2485
2486 if (bytes > 0 && !con->got_fields)
2487 {
2488 /*
2489 * Remaining text needs to go out...
2490 */
2491
2492 httpPrintf(HTTP(con), "%s", buf);
2493
2494 con->http.activity = time(NULL);
2495 return (1);
2496 }
2497 else if (bytes == 0)
2498 {
2499 con->http.activity = time(NULL);
2500 return (1);
2501 }
2502 }
2503
a74b005d 2504 if (httpWrite(HTTP(con), buf, bytes) < 0)
2505 {
2506 CloseClient(con);
2507 return (0);
2508 }
6a0c519d 2509
2510 con->bytes += bytes;
a74b005d 2511 }
b14d90ba 2512
2513 if (bytes <= 0)
a74b005d 2514 {
6a0c519d 2515 LogRequest(con, HTTP_OK);
2516
a74b005d 2517 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
2518 {
2519 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
2520 {
2521 CloseClient(con);
2522 return (0);
2523 }
2524 }
2525
96df88bb 2526 con->http.state = HTTP_WAITING;
2527
e668e627 2528 LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from OutputSet...",
2529 con->http.fd);
2530
f3bc1068 2531 FD_CLR(con->http.fd, OutputSet);
a74b005d 2532
bfa1abf0 2533 if (con->file)
a74b005d 2534 {
27eba2dd 2535 LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from InputSet...",
2536 con->file);
f3bc1068 2537 FD_CLR(con->file, InputSet);
a74b005d 2538
bfa1abf0 2539 if (con->pipe_pid)
96df88bb 2540 kill(con->pipe_pid, SIGTERM);
bfa1abf0 2541
bd917997 2542 LogMessage(L_DEBUG2, "WriteClient() %d Closing data file %d.",
2543 con->http.fd, con->file);
2544
bfa1abf0 2545 close(con->file);
96df88bb 2546 con->file = 0;
2547 con->pipe_pid = 0;
bfa1abf0 2548 }
a74b005d 2549
fc757c63 2550 if (con->filename)
a61a2be6 2551 {
2552 LogMessage(L_DEBUG2, "WriteClient() %d Removing temp file %s",
bd917997 2553 con->http.fd, con->filename);
d4c438d4 2554 unlink(con->filename);
fc757c63 2555 ClearString(&con->filename);
a61a2be6 2556 }
d4c438d4 2557
b14d90ba 2558 if (con->request != NULL)
2559 {
2560 ippDelete(con->request);
2561 con->request = NULL;
2562 }
2563
2564 if (con->response != NULL)
2565 {
2566 ippDelete(con->response);
2567 con->response = NULL;
2568 }
96df88bb 2569
fc757c63 2570 ClearString(&con->command);
2571 ClearString(&con->options);
2572
96df88bb 2573 if (!con->http.keep_alive)
2574 {
2575 CloseClient(con);
2576 return (0);
2577 }
a74b005d 2578 }
2579
997edb40 2580 if (bytes >= 1024)
58e8cf34 2581 LogMessage(L_DEBUG2, "WriteClient() %d %d bytes", con->http.fd, bytes);
a74b005d 2582
2583 con->http.activity = time(NULL);
2584
2585 return (1);
2586}
2587
2588
a74b005d 2589/*
2590 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
2591 */
2592
2593static int /* O - 1 if modified since */
2594check_if_modified(client_t *con, /* I - Client connection */
2595 struct stat *filestats) /* I - File information */
2596{
2597 char *ptr; /* Pointer into field */
2598 time_t date; /* Time/date value */
2599 int size; /* Size/length value */
2600
2601
2602 size = 0;
2603 date = 0;
2604 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
2605
2606 if (*ptr == '\0')
2607 return (1);
2608
58e8cf34 2609 LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"",
4a64fdb7 2610 con->http.fd, ptr);
2611
a74b005d 2612 while (*ptr != '\0')
2613 {
4a64fdb7 2614 while (isspace(*ptr) || *ptr == ';')
a74b005d 2615 ptr ++;
2616
2617 if (strncasecmp(ptr, "length=", 7) == 0)
2618 {
2619 ptr += 7;
2620 size = atoi(ptr);
2621
2622 while (isdigit(*ptr))
2623 ptr ++;
2624 }
4a64fdb7 2625 else if (isalpha(*ptr))
a74b005d 2626 {
2627 date = httpGetDateTime(ptr);
4a64fdb7 2628 while (*ptr != '\0' && *ptr != ';')
a74b005d 2629 ptr ++;
2630 }
2631 }
2632
58e8cf34 2633 LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
aa7e125a 2634 con->http.fd, size, (int)filestats->st_size, (int)date,
2635 (int)filestats->st_mtime);
4a64fdb7 2636
a74b005d 2637 return ((size != filestats->st_size && size != 0) ||
4a64fdb7 2638 (date < filestats->st_mtime && date != 0) ||
2639 (size == 0 && date == 0));
a74b005d 2640}
2641
2642
2643/*
d2122fde 2644 * 'decode_auth()' - Decode an authorization string.
a74b005d 2645 */
2646
93894a43 2647static void
d2122fde 2648decode_auth(client_t *con) /* I - Client to decode to */
a74b005d 2649{
d2122fde 2650 char *s, /* Authorization string */
2651 value[1024]; /* Value string */
2652 const char *username; /* Certificate username */
a74b005d 2653
a74b005d 2654
2655 /*
b0d58d07 2656 * Decode the string...
a74b005d 2657 */
2658
f3d580b9 2659 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
f3d580b9 2660
aa7e125a 2661 LogMessage(L_DEBUG2, "decode_auth(%p): Authorization string = \"%s\"",
27eba2dd 2662 con, s);
69f14ebb 2663
d2122fde 2664 if (strncmp(s, "Basic", 5) == 0)
2665 {
2666 s += 5;
2667 while (isspace(*s))
2668 s ++;
f3d580b9 2669
d2122fde 2670 httpDecode64(value, s);
a74b005d 2671
d2122fde 2672 /*
2673 * Pull the username and password out...
2674 */
b0d58d07 2675
d2122fde 2676 if ((s = strchr(value, ':')) == NULL)
2677 {
5ea8888e 2678 LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
d2122fde 2679 con->http.fd, value);
2680 return;
2681 }
2682
2683 *s++ = '\0';
b0d58d07 2684
def978d5 2685 strlcpy(con->username, value, sizeof(con->username));
2686 strlcpy(con->password, s, sizeof(con->password));
d2122fde 2687 }
2688 else if (strncmp(s, "Local", 5) == 0)
2689 {
2690 s += 5;
2691 while (isspace(*s))
2692 s ++;
b0d58d07 2693
d2122fde 2694 if ((username = FindCert(s)) != NULL)
def978d5 2695 strlcpy(con->username, username, sizeof(con->username));
d2122fde 2696 }
83e740a5 2697 else if (strncmp(s, "Digest", 5) == 0)
2698 {
2699 /*
2700 * Get the username and password from the Digest attributes...
2701 */
2702
2703 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username",
2704 value))
def978d5 2705 strlcpy(con->username, value, sizeof(con->username));
83e740a5 2706
2707 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response",
2708 value))
def978d5 2709 strlcpy(con->password, value, sizeof(con->password) - 1);
83e740a5 2710 }
41637133 2711
27eba2dd 2712 LogMessage(L_DEBUG2, "decode_auth() %d username=\"%s\"",
41637133 2713 con->http.fd, con->username);
a74b005d 2714}
2715
2716
e31bfb6e 2717/*
2718 * 'get_file()' - Get a filename and state info.
2719 */
2720
6db7190f 2721static char * /* O - Real filename */
2722get_file(client_t *con, /* I - Client connection */
2723 struct stat *filestats,/* O - File information */
2724 char *filename, /* IO - Filename buffer */
2725 int len) /* I - Buffer length */
e31bfb6e 2726{
2727 int status; /* Status of filesystem calls */
2728 char *params; /* Pointer to parameters in URI */
e31bfb6e 2729
2730
2731 /*
2732 * Need to add DocumentRoot global...
2733 */
2734
f3d580b9 2735 if (strncmp(con->uri, "/ppd/", 5) == 0)
6db7190f 2736 snprintf(filename, len, "%s%s", ServerRoot, con->uri);
753453e4 2737 else if (strncmp(con->uri, "/admin/conf/", 12) == 0)
6db7190f 2738 snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
f3d580b9 2739 else if (con->language != NULL)
6db7190f 2740 snprintf(filename, len, "%s/%s%s", DocumentRoot, con->language->language,
e31bfb6e 2741 con->uri);
2742 else
6db7190f 2743 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
e31bfb6e 2744
2745 if ((params = strchr(filename, '?')) != NULL)
2746 *params = '\0';
2747
2748 /*
2749 * Grab the status for this language; if there isn't a language-specific file
2750 * then fallback to the default one...
2751 */
2752
2753 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
2754 {
2755 /*
2756 * Drop the language prefix and try the current directory...
2757 */
2758
753453e4 2759 if (strncmp(con->uri, "/ppd/", 5) != 0 &&
2760 strncmp(con->uri, "/admin/conf/", 12) != 0)
f3d580b9 2761 {
6db7190f 2762 snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
e31bfb6e 2763
f3d580b9 2764 status = stat(filename, filestats);
2765 }
e31bfb6e 2766 }
2767
2768 /*
2769 * If we're found a directory, get the index.html file instead...
2770 */
2771
2772 if (!status && S_ISDIR(filestats->st_mode))
2773 {
2774 if (filename[strlen(filename) - 1] == '/')
6db7190f 2775 strlcat(filename, "index.html", len);
e31bfb6e 2776 else
6db7190f 2777 strlcat(filename, "/index.html", len);
e31bfb6e 2778
2779 status = stat(filename, filestats);
2780 }
2781
58e8cf34 2782 LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d",
aa7e125a 2783 con->http.fd, filename, status ? -1 : (int)filestats->st_size);
e31bfb6e 2784
2785 if (status)
2786 return (NULL);
2787 else
2788 return (filename);
2789}
2790
2791
753453e4 2792/*
2793 * 'install_conf_file()' - Install a configuration file.
2794 */
2795
2796static http_status_t /* O - Status */
2797install_conf_file(client_t *con) /* I - Connection */
2798{
7b0fde61 2799 cups_file_t *in, /* Input file */
753453e4 2800 *out; /* Output file */
2801 char buffer[1024]; /* Copy buffer */
2802 int bytes; /* Number of bytes */
2803 char conffile[1024], /* Configuration filename */
2804 newfile[1024], /* New config filename */
2805 oldfile[1024]; /* Old config filename */
2806 struct stat confinfo; /* Config file info */
2807
2808
2809 /*
2810 * First construct the filenames...
2811 */
2812
2813 snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11);
2814 snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11);
2815 snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11);
2816
2817 LogMessage(L_INFO, "Installing config file \"%s\"...", conffile);
2818
2819 /*
2820 * Get the owner, group, and permissions of the configuration file.
2821 * If it doesn't exist, assign it to the User and Group in the
2822 * cupsd.conf file with mode 0640 permissions.
2823 */
2824
2825 if (stat(conffile, &confinfo))
2826 {
2827 confinfo.st_uid = User;
2828 confinfo.st_gid = Group;
d59a189c 2829 confinfo.st_mode = ConfigFilePerm;
753453e4 2830 }
2831
2832 /*
2833 * Open the request file and new config file...
2834 */
2835
7b0fde61 2836 if ((in = cupsFileOpen(con->filename, "rb")) == NULL)
753453e4 2837 {
2838 LogMessage(L_ERROR, "Unable to open request file \"%s\" - %s",
2839 con->filename, strerror(errno));
2840 return (HTTP_SERVER_ERROR);
2841 }
2842
7b0fde61 2843 if ((out = cupsFileOpen(newfile, "wb")) == NULL)
753453e4 2844 {
7b0fde61 2845 cupsFileClose(in);
753453e4 2846 LogMessage(L_ERROR, "Unable to open config file \"%s\" - %s",
2847 newfile, strerror(errno));
2848 return (HTTP_SERVER_ERROR);
2849 }
2850
7b0fde61 2851 fchmod(cupsFileNumber(out), confinfo.st_mode);
2852 fchown(cupsFileNumber(out), confinfo.st_uid, confinfo.st_gid);
753453e4 2853
2854 /*
2855 * Copy from the request to the new config file...
2856 */
2857
7b0fde61 2858 while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
2859 if (cupsFileWrite(out, buffer, bytes) < bytes)
753453e4 2860 {
2861 LogMessage(L_ERROR, "Unable to copy to config file \"%s\" - %s",
2862 newfile, strerror(errno));
2863
7b0fde61 2864 cupsFileClose(in);
2865 cupsFileClose(out);
753453e4 2866 unlink(newfile);
2867
2868 return (HTTP_SERVER_ERROR);
2869 }
2870
2871 /*
2872 * Close the files...
2873 */
2874
7b0fde61 2875 cupsFileClose(in);
2876 if (cupsFileClose(out))
753453e4 2877 {
2878 LogMessage(L_ERROR, "Error file closing config file \"%s\" - %s",
2879 newfile, strerror(errno));
2880
2881 unlink(newfile);
2882
2883 return (HTTP_SERVER_ERROR);
2884 }
2885
2886 /*
2887 * Remove the request file...
2888 */
2889
2890 unlink(con->filename);
fc757c63 2891 ClearString(&con->filename);
753453e4 2892
2893 /*
2894 * Unlink the old backup, rename the current config file to the backup
2895 * filename, and rename the new config file to the config file name...
2896 */
2897
2898 if (unlink(oldfile))
2899 if (errno != ENOENT)
2900 {
2901 LogMessage(L_ERROR, "Unable to remove backup config file \"%s\" - %s",
2902 oldfile, strerror(errno));
2903
2904 unlink(newfile);
2905
2906 return (HTTP_SERVER_ERROR);
2907 }
2908
2909 if (rename(conffile, oldfile))
2910 if (errno != ENOENT)
2911 {
2912 LogMessage(L_ERROR, "Unable to rename old config file \"%s\" - %s",
2913 conffile, strerror(errno));
2914
2915 unlink(newfile);
2916
2917 return (HTTP_SERVER_ERROR);
2918 }
2919
2920 if (rename(newfile, conffile))
2921 {
2922 LogMessage(L_ERROR, "Unable to rename new config file \"%s\" - %s",
2923 newfile, strerror(errno));
2924
2925 rename(oldfile, conffile);
2926 unlink(newfile);
2927
2928 return (HTTP_SERVER_ERROR);
2929 }
2930
2931 /*
2932 * If the cupsd.conf file was updated, set the NeedReload flag...
2933 */
2934
2935 if (strcmp(con->uri, "/admin/conf/cupsd.conf") == 0)
53510eae 2936 NeedReload = RELOAD_CUPSD;
2937 else
2938 NeedReload = RELOAD_ALL;
753453e4 2939
a782f61f 2940 ReloadTime = time(NULL);
2941
753453e4 2942 /*
2943 * Return that the file was created successfully...
2944 */
2945
2946 return (HTTP_CREATED);
2947}
2948
2949
a74b005d 2950/*
2951 * 'pipe_command()' - Pipe the output of a command to the remote client.
2952 */
2953
879062a9 2954static int /* O - Process ID */
2955pipe_command(client_t *con, /* I - Client connection */
2956 int infile, /* I - Standard input for command */
2957 int *outfile, /* O - Standard output for command */
2958 char *command, /* I - Command to run */
2959 char *options) /* I - Options for command */
a74b005d 2960{
2ae14c39 2961 int i; /* Looping var */
879062a9 2962 int pid; /* Process ID */
2963 char *commptr; /* Command string pointer */
e420f467 2964 char *uriptr; /* URI string pointer */
879062a9 2965 int fd; /* Looping var */
2966 int fds[2]; /* Pipe FDs */
2967 int argc; /* Number of arguments */
2968 int envc; /* Number of environment variables */
2969 char argbuf[10240], /* Argument buffer */
2970 *argv[100], /* Argument strings */
2971 *envp[100]; /* Environment variables */
2ae14c39 2972 char content_length[1024], /* CONTENT_LENGTH environment variable */
2973 content_type[1024], /* CONTENT_TYPE environment variable */
2974 cups_datadir[1024], /* CUPS_DATADIR environment variable */
2975 cups_serverroot[1024], /* CUPS_SERVERROOT environment variable */
2976 http_cookie[1024], /* HTTP_COOKIE environment variable */
2977 http_user_agent[1024], /* HTTP_USER_AGENT environment variable */
2978 ipp_port[1024], /* IPP_PORT environment variable */
2979 lang[1024], /* LANG environment variable */
2980 ld_library_path[1024], /* LD_LIBRARY_PATH environment variable */
dd63ebe2 2981 ld_preload[1024], /* LD_PRELOAD environment variable */
2982 dyld_library_path[1024],/* DYLD_LIBRARY_PATH environment variable */
2983 shlib_path[1024], /* SHLIB_PATH environment variable */
879062a9 2984 nlspath[1024], /* NLSPATH environment variable */
2ae14c39 2985 query_string[10240], /* QUERY_STRING env variable */
2986 remote_addr[1024], /* REMOTE_ADDR environment variable */
2987 remote_host[1024], /* REMOTE_HOST environment variable */
2988 remote_user[1024], /* REMOTE_USER environment variable */
2989 script_name[1024], /* SCRIPT_NAME environment variable */
2990 server_name[1024], /* SERVER_NAME environment variable */
2991 server_port[1024], /* SERVER_PORT environment variable */
ff40b65e 2992 tmpdir[1024], /* TMPDIR environment variable */
1f4b7b03 2993 vg_args[1024], /* VG_ARGS environment variable */
2994 ld_assume_kernel[1024]; /* LD_ASSUME_KERNEL environment variable */
879062a9 2995#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
c7a9d594 2996 struct sigaction action; /* POSIX signal handler */
879062a9 2997#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
08c0ff15 2998 static const char * const locale_encodings[] =
2999 { /* Locale charset names */
3000 "ASCII", "ISO8859-1", "ISO8859-2", "ISO8859-3",
3001 "ISO8859-4", "ISO8859-5", "ISO8859-6", "ISO8859-7",
3002 "ISO8859-8", "ISO8859-9", "ISO8859-10", "UTF-8",
3003 "ISO8859-13", "ISO8859-14", "ISO8859-15", "CP874",
3004 "CP1250", "CP1251", "CP1252", "CP1253",
3005 "CP1254", "CP1255", "CP1256", "CP1257",
3006 "CP1258", "KOI8R", "KOI8U", "ISO8859-11",
3007 "ISO8859-16", "", "", "",
3008
3009 "", "", "", "",
3010 "", "", "", "",
3011 "", "", "", "",
3012 "", "", "", "",
3013 "", "", "", "",
3014 "", "", "", "",
3015 "", "", "", "",
3016 "", "", "", "",
3017
3018 "CP932", "CP936", "CP949", "CP950",
3019 "CP1361", "", "", "",
3020 "", "", "", "",
3021 "", "", "", "",
3022 "", "", "", "",
3023 "", "", "", "",
3024 "", "", "", "",
3025 "", "", "", "",
3026
3027 "", "", "", "",
3028 "", "", "", "",
3029 "", "", "", "",
3030 "", "", "", "",
3031 "", "", "", "",
3032 "", "", "", "",
3033 "", "", "", "",
3034 "", "", "", "",
3035
3036 "EUC-CN", "EUC-JP", "EUC-KR", "EUC-TW"
3037 };
a74b005d 3038
05e63c18 3039
a74b005d 3040 /*
3041 * Copy the command string...
3042 */
3043
def978d5 3044 strlcpy(argbuf, options, sizeof(argbuf));
a74b005d 3045
3046 /*
f63a2256 3047 * Parse the string; arguments can be separated by + and are terminated
3048 * by ?...
a74b005d 3049 */
3050
3051 argv[0] = argbuf;
3052
bfa1abf0 3053 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
f63a2256 3054 if (*commptr == ' ' || *commptr == '+')
a74b005d 3055 {
3056 *commptr++ = '\0';
3057
3058 while (*commptr == ' ')
3059 commptr ++;
3060
3061 if (*commptr != '\0')
3062 {
3063 argv[argc] = commptr;
3064 argc ++;
3065 }
3066
3067 commptr --;
3068 }
3069 else if (*commptr == '%')
3070 {
3071 if (commptr[1] >= '0' && commptr[1] <= '9')
3072 *commptr = (commptr[1] - '0') << 4;
3073 else
3074 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
3075
3076 if (commptr[2] >= '0' && commptr[2] <= '9')
3077 *commptr |= commptr[2] - '0';
3078 else
3079 *commptr |= tolower(commptr[2]) - 'a' + 10;
3080
ff40b65e 3081 cups_strcpy(commptr + 1, commptr + 3);
a74b005d 3082 }
f63a2256 3083 else if (*commptr == '?')
3084 break;
a74b005d 3085
3086 argv[argc] = NULL;
3087
bfa1abf0 3088 if (argv[0][0] == '\0')
3089 argv[0] = strrchr(command, '/') + 1;
b14d90ba 3090
3091 /*
3092 * Setup the environment variables as needed...
3093 */
3094
08c0ff15 3095 if (con->language)
3096 snprintf(lang, sizeof(lang), "LANG=%s.%s", con->language->language,
3097 locale_encodings[con->language->encoding]);
3098 else
3099 strcpy(lang, "LANG=C");
3100
0f71c41e 3101 sprintf(ipp_port, "IPP_PORT=%d", LocalPort);
99de6da0 3102#ifdef AF_INET6
3103 if (con->http.hostaddr.addr.sa_family == AF_INET6)
99de6da0 3104 sprintf(server_port, "SERVER_PORT=%d",
3105 ntohs(con->http.hostaddr.ipv6.sin6_port));
99de6da0 3106 else
3107#endif /* AF_INET6 */
99de6da0 3108 sprintf(server_port, "SERVER_PORT=%d",
3109 ntohs(con->http.hostaddr.ipv4.sin_port));
99de6da0 3110
c5d918d8 3111 if (strcmp(con->http.hostname, "localhost") == 0)
3112 strlcpy(server_name, "SERVER_NAME=localhost", sizeof(server_name));
3113 else
3114 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName);
04de52f8 3115 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname);
2ae14c39 3116 strcpy(remote_addr, "REMOTE_ADDR=");
3117 httpAddrString(&(con->http.hostaddr), remote_addr + 12,
3118 sizeof(remote_addr) - 12);
04de52f8 3119 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
3120 snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
2ae14c39 3121 snprintf(cups_datadir, sizeof(cups_datadir), "CUPS_DATADIR=%s", DataDir);
3122 snprintf(cups_serverroot, sizeof(cups_serverroot), "CUPS_SERVERROOT=%s", ServerRoot);
3123
3124 envc = 0;
3125
3126 envp[envc ++] = "PATH=/bin:/usr/bin";
3127 envp[envc ++] = "SERVER_SOFTWARE=CUPS/1.1";
3128 envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
3129 if (con->http.version == HTTP_1_1)
3130 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
3131 else if (con->http.version == HTTP_1_0)
3132 envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
3133 else
3134 envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
3135 envp[envc ++] = "REDIRECT_STATUS=1";
ff40b65e 3136 envp[envc ++] = "CUPS_SERVER=localhost";
2ae14c39 3137 envp[envc ++] = ipp_port;
3138 envp[envc ++] = server_name;
3139 envp[envc ++] = server_port;
3140 envp[envc ++] = remote_addr;
3141 envp[envc ++] = remote_host;
3142 envp[envc ++] = remote_user;
3143 envp[envc ++] = lang;
3144 envp[envc ++] = TZ;
3145 envp[envc ++] = tmpdir;
3146 envp[envc ++] = cups_datadir;
3147 envp[envc ++] = cups_serverroot;
df3f73ff 3148
ff40b65e 3149 if (getenv("VG_ARGS") != NULL)
3150 {
3151 snprintf(vg_args, sizeof(vg_args), "VG_ARGS=%s", getenv("VG_ARGS"));
3152 envp[envc ++] = vg_args;
3153 }
3154
1f4b7b03 3155 if (getenv("LD_ASSUME_KERNEL") != NULL)
3156 {
3157 snprintf(ld_assume_kernel, sizeof(ld_assume_kernel), "LD_ASSUME_KERNEL=%s",
3158 getenv("LD_ASSUME_KERNEL"));
3159 envp[envc ++] = ld_assume_kernel;
3160 }
3161
df3f73ff 3162 if (getenv("LD_LIBRARY_PATH") != NULL)
2ae14c39 3163 {
3164 snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s",
1cf29e95 3165 getenv("LD_LIBRARY_PATH"));
2ae14c39 3166 envp[envc ++] = ld_library_path;
3167 }
dd63ebe2 3168
3169 if (getenv("LD_PRELOAD") != NULL)
3170 {
3171 snprintf(ld_preload, sizeof(ld_preload), "LD_PRELOAD=%s",
3172 getenv("LD_PRELOAD"));
3173 envp[envc ++] = ld_preload;
3174 }
3175
3176 if (getenv("DYLD_LIBRARY_PATH") != NULL)
2ae14c39 3177 {
dd63ebe2 3178 snprintf(dyld_library_path, sizeof(dyld_library_path), "DYLD_LIBRARY_PATH=%s",
1cf29e95 3179 getenv("DYLD_LIBRARY_PATH"));
dd63ebe2 3180 envp[envc ++] = dyld_library_path;
2ae14c39 3181 }
dd63ebe2 3182
3183 if (getenv("SHLIB_PATH") != NULL)
2ae14c39 3184 {
dd63ebe2 3185 snprintf(shlib_path, sizeof(shlib_path), "SHLIB_PATH=%s",
103abc03 3186 getenv("SHLIB_PATH"));
dd63ebe2 3187 envp[envc ++] = shlib_path;
2ae14c39 3188 }
7f0679f5 3189
1cf29e95 3190 if (getenv("NLSPATH") != NULL)
2ae14c39 3191 {
1cf29e95 3192 snprintf(nlspath, sizeof(nlspath), "NLSPATH=%s", getenv("NLSPATH"));
1cf29e95 3193 envp[envc ++] = nlspath;
2ae14c39 3194 }
3195
3196 if (con->http.cookie)
3197 {
3198 snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
3199 con->http.cookie);
3200 envp[envc ++] = http_cookie;
3201 }
3202
3203 if (con->http.fields[HTTP_FIELD_USER_AGENT][0])
3204 {
3205 snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s",
3206 con->http.fields[HTTP_FIELD_USER_AGENT]);
3207 envp[envc ++] = http_user_agent;
3208 }
3209
3210 snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri);
e420f467 3211 if ((uriptr = strchr(script_name, '?')) != NULL)
3212 *uriptr = '\0';
2ae14c39 3213 envp[envc ++] = script_name;
1cf29e95 3214
b14d90ba 3215 if (con->operation == HTTP_GET)
3216 {
43ed090d 3217 for (i = 0; i < argc; i ++)
3218 LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
df3f73ff 3219 envp[envc ++] = "REQUEST_METHOD=GET";
f63a2256 3220
3221 if (*commptr)
3222 {
3223 /*
3224 * Add GET form variables after ?...
3225 */
3226
3227 *commptr++ = '\0';
3228
04de52f8 3229 snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
df3f73ff 3230 envp[envc ++] = query_string;
f63a2256 3231 }
b14d90ba 3232 }
3233 else
3234 {
f63a2256 3235 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
04de52f8 3236 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
970017a4 3237 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
b14d90ba 3238
df3f73ff 3239 envp[envc ++] = "REQUEST_METHOD=POST";
3240 envp[envc ++] = content_length;
3241 envp[envc ++] = content_type;
b14d90ba 3242 }
3243
30d60919 3244 /*
3245 * Tell the CGI if we are using encryption...
3246 */
3247
b92c5a4f 3248 if (con->http.encryption == HTTP_ENCRYPT_ALWAYS)
3249 {
30d60919 3250 envp[envc ++] = "HTTPS=ON";
b92c5a4f 3251 envp[envc ++] = "CUPS_ENCRYPTION=Always";
3252 }
30d60919 3253
df3f73ff 3254 envp[envc] = NULL;
3255
2ae14c39 3256 if (LogLevel == L_DEBUG2)
3257 {
3258 for (i = 0; i < argc; i ++)
3259 LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
3260 for (i = 0; i < envc; i ++)
3261 LogMessage(L_DEBUG2, "envp[%d] = \"%s\"", i, envp[i]);
3262 }
3263
a74b005d 3264 /*
3265 * Create a pipe for the output...
3266 */
3267
3268 if (pipe(fds))
1d6c68fb 3269 {
5ea8888e 3270 LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
1d6c68fb 3271 argv[0], strerror(errno));
a74b005d 3272 return (0);
1d6c68fb 3273 }
a74b005d 3274
879062a9 3275 /*
3276 * Block signals before forking...
3277 */
3278
38743560 3279 HoldSignals();
4d520ad4 3280
a74b005d 3281 /*
1d6c68fb 3282 * Then execute the command...
a74b005d 3283 */
3284
3285 if ((pid = fork()) == 0)
3286 {
3287 /*
3288 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
3289 */
3290
5aeb433c 3291 if (getuid() == 0)
3292 {
3293 /*
753453e4 3294 * Running as root, so change to a non-priviledged user...
5aeb433c 3295 */
3296
3297 if (setgid(Group))
3298 exit(errno);
3299
4342fddc 3300 if (setgroups(1, &Group))
daf8f5f0 3301 exit(errno);
3302
5aeb433c 3303 if (setuid(User))
3304 exit(errno);
3305 }
8914cc4b 3306 else
3307 {
3308 /*
3309 * Reset group membership to just the main one we belong to.
3310 */
5aeb433c 3311
4342fddc 3312 setgroups(1, &Group);
8914cc4b 3313 }
5aeb433c 3314
3315 /*
a9775e85 3316 * Update stdin/stdout/stderr...
5aeb433c 3317 */
b14d90ba 3318
a74b005d 3319 if (infile)
3320 {
3321 close(0);
753453e4 3322 if (dup(infile) < 0)
3323 exit(errno);
a74b005d 3324 }
3325
3326 close(1);
753453e4 3327 if (dup(fds[1]) < 0)
3328 exit(errno);
a74b005d 3329
a9775e85 3330 close(2);
0a968cfb 3331 dup(CGIPipes[1]);
a9775e85 3332
082b40d2 3333 /*
3334 * Close extra file descriptors...
3335 */
3336
93336dbc 3337 for (fd = 3; fd < MaxFDs; fd ++)
082b40d2 3338 close(fd);
3339
3340 /*
3341 * Change umask to restrict permissions on created files...
3342 */
3343
3344 umask(077);
a74b005d 3345
5f152161 3346 /*
3347 * Unblock signals before doing the exec...
3348 */
3349
3350#ifdef HAVE_SIGSET
c7a9d594 3351 sigset(SIGTERM, SIG_DFL);
38743560 3352 sigset(SIGCHLD, SIG_DFL);
5f152161 3353#elif defined(HAVE_SIGACTION)
c7a9d594 3354 memset(&action, 0, sizeof(action));
3355
3356 sigemptyset(&action.sa_mask);
3357 action.sa_handler = SIG_DFL;
3358
3359 sigaction(SIGTERM, &action, NULL);
38743560 3360 sigaction(SIGCHLD, &action, NULL);
c7a9d594 3361#else
3362 signal(SIGTERM, SIG_DFL);
38743560 3363 signal(SIGCHLD, SIG_DFL);
5f152161 3364#endif /* HAVE_SIGSET */
3365
38743560 3366 ReleaseSignals();
3367
a74b005d 3368 /*
3369 * Execute the pipe program; if an error occurs, exit with status 1...
3370 */
3371
b14d90ba 3372 execve(command, argv, envp);
96df88bb 3373 exit(errno);
a74b005d 3374 return (0);
3375 }
3376 else if (pid < 0)
3377 {
3378 /*
3379 * Error - can't fork!
3380 */
3381
5ea8888e 3382 LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
1d6c68fb 3383 strerror(errno));
3384
a74b005d 3385 close(fds[0]);
3386 close(fds[1]);
5c8eff27 3387 pid = 0;
a74b005d 3388 }
3389 else
3390 {
3391 /*
3392 * Fork successful - return the PID...
3393 */
3394
f63a2256 3395 AddCert(pid, con->username);
3396
753453e4 3397 LogMessage(L_DEBUG, "CGI %s started - PID = %d", command, pid);
1d6c68fb 3398
a74b005d 3399 *outfile = fds[0];
3400 close(fds[1]);
5c8eff27 3401 }
a74b005d 3402
38743560 3403 ReleaseSignals();
4d520ad4 3404
5c8eff27 3405 return (pid);
a74b005d 3406}
3407
3408
dcfcaeac 3409#if defined(HAVE_CDSASSL)
3410/*
3411 * 'CDSAReadFunc()' - Read function for CDSA decryption code.
3412 */
3413
3414static OSStatus /* O - -1 on error, 0 on success */
3415CDSAReadFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
3416 void *data, /* I - Data buffer */
3417 size_t *dataLength) /* IO - Number of bytes */
3418{
3419 ssize_t bytes; /* Number of bytes read */
3420
3421
3422 bytes = recv((int)connection, data, *dataLength, 0);
3423 if (bytes >= 0)
3424 {
3425 *dataLength = bytes;
3426 return (0);
3427 }
3428 else
3429 return (-1);
3430}
3431
3432
3433/*
3434 * 'CDSAWriteFunc()' - Write function for CDSA encryption code.
3435 */
3436
3437static OSStatus /* O - -1 on error, 0 on success */
3438CDSAWriteFunc(SSLConnectionRef connection, /* I - SSL/TLS connection */
3439 const void *data, /* I - Data buffer */
3440 size_t *dataLength) /* IO - Number of bytes */
3441{
3442 ssize_t bytes;
3443
3444
3445 bytes = write((int)connection, data, *dataLength);
3446 if (bytes >= 0)
3447 {
3448 *dataLength = bytes;
3449 return (0);
3450 }
3451 else
3452 return (-1);
3453}
3454#endif /* HAVE_CDSASSL */
3455
3456
93894a43 3457/*
aa7b8dba 3458 * End of "$Id: client.c,v 1.91.2.71 2003/09/15 20:11:08 mike Exp $".
a74b005d 3459 */