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