]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/client.c
Copyright update...
[thirdparty/cups.git] / scheduler / client.c
CommitLineData
a74b005d 1/*
efb2f309 2 * "$Id: client.c,v 1.107 2002/01/02 17:59:14 mike Exp $"
a74b005d 3 *
4 * Client routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
efb2f309 6 * Copyright 1997-2002 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...
93894a43 30 * ReadClient() - Read data from a client.
31 * SendCommand() - Send output from a command via HTTP.
32 * SendError() - Send an error message via HTTP.
33 * SendFile() - Send a file via HTTP.
34 * SendHeader() - Send an HTTP request.
93894a43 35 * WriteClient() - Write data to a client as needed.
36 * check_if_modified() - Decode an "If-Modified-Since" line.
d2122fde 37 * decode_auth() - Decode an authorization string.
93894a43 38 * get_file() - Get a filename and state info.
93de21d5 39 * install_conf_file() - Install a configuration file.
93894a43 40 * pipe_command() - Pipe the output of a command to the remote client.
a74b005d 41 */
42
43/*
44 * Include necessary headers...
45 */
46
47#include "cupsd.h"
48
bd917997 49#include <grp.h>
50
a75c006a 51#ifdef HAVE_LIBSSL
1c6682dd 52# include <openssl/err.h>
a75c006a 53# include <openssl/ssl.h>
54# include <openssl/rand.h>
55#endif /* HAVE_LIBSSL */
56
a74b005d 57
58/*
59 * Local functions...
60 */
61
93de21d5 62static int check_if_modified(client_t *con,
63 struct stat *filestats);
64static void decode_auth(client_t *con);
65static char *get_file(client_t *con, struct stat *filestats);
66static http_status_t install_conf_file(client_t *con);
67static int pipe_command(client_t *con, int infile, int *outfile,
68 char *command, char *options);
a74b005d 69
70
71/*
72 * 'AcceptClient()' - Accept a new client.
73 */
74
75void
76AcceptClient(listener_t *lis) /* I - Listener socket */
77{
27eba2dd 78 int i; /* Looping var */
a74b005d 79 int val; /* Parameter value */
80 client_t *con; /* New client pointer */
81 unsigned address;/* Address of client */
82 struct hostent *host; /* Host entry for address */
83
84
27eba2dd 85 LogMessage(L_DEBUG2, "AcceptClient(%08x) %d NumClients = %d",
86 lis, lis->fd, NumClients);
bfa1abf0 87
12aa0c1f 88 /*
89 * Make sure we don't have a full set of clients already...
90 */
91
92 if (NumClients == MaxClients)
93 return;
94
a74b005d 95 /*
96 * Get a pointer to the next available client...
97 */
98
99 con = Clients + NumClients;
100
101 memset(con, 0, sizeof(client_t));
102 con->http.activity = time(NULL);
103
104 /*
105 * Accept the client and get the remote address...
106 */
107
108 val = sizeof(struct sockaddr_in);
109
2f6d083c 110 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr),
111 &val)) < 0)
a74b005d 112 {
27eba2dd 113 LogMessage(L_ERROR, "Unable to accept client connection - %s.",
114 strerror(errno));
a74b005d 115 return;
116 }
117
2f6d083c 118 con->http.hostaddr.sin_port = lis->address.sin_port;
119
a74b005d 120 /*
121 * Get the hostname or format the IP address as needed...
122 */
123
124 address = ntohl(con->http.hostaddr.sin_addr.s_addr);
125
126 if (HostNameLookups)
590a0a40 127#ifndef __sgi
e58c04b5 128 host = gethostbyaddr((char *)&(con->http.hostaddr.sin_addr),
129 sizeof(struct in_addr), AF_INET);
42d48bd2 130#else
e58c04b5 131 host = gethostbyaddr(&(con->http.hostaddr.sin_addr),
132 sizeof(struct in_addr), AF_INET);
590a0a40 133#endif /* !__sgi */
a74b005d 134 else
135 host = NULL;
136
2bdd1992 137 if (con->http.hostaddr.sin_addr.s_addr == ServerAddr.sin_addr.s_addr)
138 {
139 /*
140 * Map accesses from the same host to the server name.
141 */
142
143 strncpy(con->http.hostname, ServerName, sizeof(con->http.hostname) - 1);
144 }
145 else if (host == NULL)
27eba2dd 146 {
a74b005d 147 sprintf(con->http.hostname, "%d.%d.%d.%d", (address >> 24) & 255,
148 (address >> 16) & 255, (address >> 8) & 255, address & 255);
27eba2dd 149
150 if (HostNameLookups == 2)
151 {
152 /*
153 * Can't have an unresolved IP address with double-lookups enabled...
154 */
155
156#ifdef WIN32
157 closesocket(con->http.fd);
158#else
159 close(con->http.fd);
160#endif /* WIN32 */
161
162 LogMessage(L_WARN, "Name lookup failed - connection from %s closed!",
163 con->http.hostname);
164 return;
165 }
166 }
a74b005d 167 else
168 strncpy(con->http.hostname, host->h_name, sizeof(con->http.hostname) - 1);
169
27eba2dd 170 if (HostNameLookups == 2)
171 {
172 /*
173 * Do double lookups as needed...
174 */
175
0e0ac99a 176 if ((host = httpGetHostByName(con->http.hostname)) != NULL)
27eba2dd 177 {
178 /*
179 * See if the hostname maps to the IP address...
180 */
181
46490d9d 182 if (host->h_length != 4 || host->h_addrtype != AF_INET)
27eba2dd 183 {
184 /*
185 * Not an IPv4 address...
186 */
187
46490d9d 188 host = NULL;
27eba2dd 189 }
190 else
191 {
192 /*
193 * Compare all of the addresses against this one...
194 */
195
46490d9d 196 for (i = 0; host->h_addr_list[i]; i ++)
197 if (memcmp(&(con->http.hostaddr.sin_addr), host->h_addr_list[i], 4) == 0)
27eba2dd 198 break;
199
46490d9d 200 if (!host->h_addr_list[i])
201 host = NULL;
27eba2dd 202 }
203 }
204
46490d9d 205 if (host == NULL)
27eba2dd 206 {
207 /*
208 * Can't have a hostname that doesn't resolve to the same IP address
209 * with double-lookups enabled...
210 */
211
212#ifdef WIN32
213 closesocket(con->http.fd);
214#else
215 close(con->http.fd);
216#endif /* WIN32 */
217
218 LogMessage(L_WARN, "IP lookup failed - connection from %s closed!",
219 con->http.hostname);
220 return;
221 }
222 }
223
58e8cf34 224 LogMessage(L_DEBUG, "AcceptClient() %d from %s:%d.", con->http.fd,
2f6d083c 225 con->http.hostname, ntohs(con->http.hostaddr.sin_port));
a74b005d 226
227 /*
228 * Add the socket to the select() input mask.
229 */
230
231 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
232
4aca0e1b 233 LogMessage(L_DEBUG2, "AcceptClient: Adding fd %d to InputSet...",
27eba2dd 234 con->http.fd);
a74b005d 235 FD_SET(con->http.fd, &InputSet);
236
237 NumClients ++;
238
239 /*
240 * Temporarily suspend accept()'s until we lose a client...
241 */
242
997edb40 243 if (NumClients == MaxClients)
d236cb49 244 PauseListening();
2a0ef17a 245
246#ifdef HAVE_LIBSSL
247 /*
0b50ddbc 248 * See if we are connecting on a secure port...
2a0ef17a 249 */
250
0b50ddbc 251 if (lis->encryption == HTTP_ENCRYPT_ALWAYS)
2a0ef17a 252 {
253 /*
254 * https connection; go secure...
255 */
256
257 EncryptClient(con);
258 }
259#endif /* HAVE_LIBSSL */
a74b005d 260}
261
262
263/*
264 * 'CloseAllClients()' - Close all remote clients immediately.
265 */
266
267void
268CloseAllClients(void)
269{
270 while (NumClients > 0)
271 CloseClient(Clients);
272}
273
274
275/*
276 * 'CloseClient()' - Close a remote client.
277 */
278
279void
280CloseClient(client_t *con) /* I - Client to close */
281{
a74b005d 282 int status; /* Exit status of pipe command */
2a0ef17a 283#ifdef HAVE_LIBSSL
284 SSL_CTX *context; /* Context for encryption */
285 SSL *conn; /* Connection for encryption */
286#endif /* HAVE_LIBSSL */
a74b005d 287
288
5ea8888e 289 LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd);
a74b005d 290
2a0ef17a 291#ifdef HAVE_LIBSSL
292 /*
293 * Shutdown encryption as needed...
294 */
295
296 if (con->http.tls)
297 {
298 conn = (SSL *)(con->http.tls);
299 context = SSL_get_SSL_CTX(conn);
300
301 SSL_shutdown(conn);
302 SSL_CTX_free(context);
303 SSL_free(conn);
304
305 con->http.tls = NULL;
306 }
307#endif /* HAVE_LIBSSL */
308
a74b005d 309 /*
310 * Close the socket and clear the file from the input set for select()...
311 */
312
882031b3 313 if (con->http.fd > 0)
314 {
e668e627 315 LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...",
27eba2dd 316 con->http.fd);
882031b3 317 close(con->http.fd);
318 FD_CLR(con->http.fd, &InputSet);
319 FD_CLR(con->http.fd, &OutputSet);
320 con->http.fd = 0;
321 }
a74b005d 322
a74b005d 323 if (con->pipe_pid != 0)
bfa1abf0 324 {
27eba2dd 325 LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet...",
326 con->file);
a74b005d 327 FD_CLR(con->file, &InputSet);
bfa1abf0 328 }
329
bfa1abf0 330 if (con->file)
a74b005d 331 {
26bb6ac6 332 /*
333 * Close the open data file...
334 */
335
a74b005d 336 if (con->pipe_pid)
337 {
338 kill(con->pipe_pid, SIGKILL);
339 waitpid(con->pipe_pid, &status, WNOHANG);
340 }
341
4aca0e1b 342 LogMessage(L_DEBUG2, "CloseClient: %d Closing data file %d.",
bd917997 343 con->http.fd, con->file);
4aca0e1b 344 LogMessage(L_DEBUG2, "CloseClient: %d Removing fd %d from InputSet.",
e668e627 345 con->http.fd, con->file);
bd917997 346
882031b3 347 FD_CLR(con->file, &InputSet);
a74b005d 348 close(con->file);
bfa1abf0 349 con->file = 0;
a74b005d 350 }
351
25135af7 352 if (con->request)
353 {
354 ippDelete(con->request);
355 con->request = NULL;
356 }
357
358 if (con->response)
359 {
360 ippDelete(con->response);
361 con->response = NULL;
362 }
363
364 if (con->language)
365 {
366 cupsLangFree(con->language);
367 con->language = NULL;
368 }
369
26bb6ac6 370 /*
371 * Re-enable new client connections if we are going back under the
372 * limit...
373 */
374
375 if (NumClients == MaxClients)
d236cb49 376 ResumeListening();
26bb6ac6 377
a74b005d 378 /*
379 * Compact the list of clients as necessary...
380 */
381
382 NumClients --;
383
384 if (con < (Clients + NumClients))
385 memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
386}
387
388
a75c006a 389/*
390 * 'EncryptClient()' - Enable encryption for the client...
391 */
392
393int /* O - 1 on success, 0 on error */
394EncryptClient(client_t *con) /* I - Client to encrypt */
395{
396#ifdef HAVE_LIBSSL
397 SSL_CTX *context; /* Context for encryption */
398 SSL *conn; /* Connection for encryption */
e7d8b50f 399 unsigned long error; /* Error code */
1c6682dd 400
401
402 /*
403 * Create the SSL context and accept the connection...
404 */
405
52cf768e 406 context = SSL_CTX_new(SSLv23_method());
a75c006a 407 conn = SSL_new(context);
408
409 SSL_use_PrivateKey_file(conn, ServerKey, SSL_FILETYPE_PEM);
410 SSL_use_certificate_file(conn, ServerCertificate, SSL_FILETYPE_PEM);
411
412 SSL_set_fd(conn, con->http.fd);
413 if (SSL_accept(conn) != 1)
414 {
1c6682dd 415 while ((error = ERR_get_error()) != 0)
416 LogMessage(L_ERROR, "EncryptClient: %s", ERR_error_string(error, NULL));
417
a75c006a 418 SSL_CTX_free(context);
419 SSL_free(conn);
420 return (0);
421 }
422
bb4d84d0 423 LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.",
424 con->http.fd);
425
a75c006a 426 con->http.tls = conn;
427 return (1);
428#else
429 return (0);
430#endif /* HAVE_LIBSSL */
431}
432
433
a74b005d 434/*
435 * 'ReadClient()' - Read data from a client.
436 */
437
438int /* O - 1 on success, 0 on error */
439ReadClient(client_t *con) /* I - Client to read from */
440{
57887694 441 char line[32768], /* Line from client... */
a74b005d 442 operation[64], /* Operation code from socket */
443 version[64]; /* HTTP version number string */
444 int major, minor; /* HTTP version numbers */
445 http_status_t status; /* Transfer status */
bd176c9c 446 ipp_state_t ipp_state; /* State of IPP transfer */
a74b005d 447 int bytes; /* Number of bytes to POST */
e31bfb6e 448 char *filename; /* Name of file for GET/HEAD */
a74b005d 449 struct stat filestats; /* File information */
e31bfb6e 450 mime_type_t *type; /* MIME type of file */
2a8fc30c 451 printer_t *p; /* Printer */
8f85ff77 452 location_t *best; /* Best match for authentication */
5d99df62 453 static unsigned request_id = 0;/* Request ID for temp files */
a74b005d 454
bfa1abf0 455
a74b005d 456 status = HTTP_CONTINUE;
457
458 switch (con->http.state)
459 {
460 case HTTP_WAITING :
461 /*
462 * See if we've received a request line...
463 */
464
465 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
93894a43 466 {
467 CloseClient(con);
468 return (0);
469 }
a74b005d 470
471 /*
472 * Ignore blank request lines...
473 */
474
475 if (line[0] == '\0')
476 break;
477
478 /*
479 * Clear other state variables...
480 */
481
482 httpClearFields(HTTP(con));
483
484 con->http.activity = time(NULL);
485 con->http.version = HTTP_1_0;
486 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
487 con->http.data_encoding = HTTP_ENCODE_LENGTH;
488 con->http.data_remaining = 0;
6a0c519d 489 con->operation = HTTP_WAITING;
490 con->bytes = 0;
a74b005d 491 con->file = 0;
492 con->pipe_pid = 0;
f63a2256 493 con->command[0] = '\0';
a74b005d 494 con->username[0] = '\0';
495 con->password[0] = '\0';
496 con->uri[0] = '\0';
497
498 if (con->language != NULL)
499 {
500 cupsLangFree(con->language);
501 con->language = NULL;
502 }
503
504 /*
505 * Grab the request line...
506 */
507
970017a4 508 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
a74b005d 509 {
510 case 1 :
511 SendError(con, HTTP_BAD_REQUEST);
512 CloseClient(con);
513 return (0);
514 case 2 :
515 con->http.version = HTTP_0_9;
516 break;
517 case 3 :
518 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
519 {
520 SendError(con, HTTP_BAD_REQUEST);
521 CloseClient(con);
522 return (0);
523 }
524
525 if (major < 2)
526 {
527 con->http.version = (http_version_t)(major * 100 + minor);
d368929e 528 if (con->http.version == HTTP_1_1 && KeepAlive)
a74b005d 529 con->http.keep_alive = HTTP_KEEPALIVE_ON;
530 else
531 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
532 }
533 else
534 {
535 SendError(con, HTTP_NOT_SUPPORTED);
536 CloseClient(con);
537 return (0);
538 }
539 break;
540 }
541
542 /*
543 * Process the request...
544 */
545
546 if (strcmp(operation, "GET") == 0)
547 con->http.state = HTTP_GET;
548 else if (strcmp(operation, "PUT") == 0)
549 con->http.state = HTTP_PUT;
550 else if (strcmp(operation, "POST") == 0)
551 con->http.state = HTTP_POST;
552 else if (strcmp(operation, "DELETE") == 0)
553 con->http.state = HTTP_DELETE;
554 else if (strcmp(operation, "TRACE") == 0)
555 con->http.state = HTTP_TRACE;
a74b005d 556 else if (strcmp(operation, "OPTIONS") == 0)
557 con->http.state = HTTP_OPTIONS;
558 else if (strcmp(operation, "HEAD") == 0)
559 con->http.state = HTTP_HEAD;
560 else
561 {
562 SendError(con, HTTP_BAD_REQUEST);
563 CloseClient(con);
564 return (0);
565 }
566
6a0c519d 567 con->start = time(NULL);
568 con->operation = con->http.state;
569
5ea8888e 570 LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
4a64fdb7 571 operation, con->uri,
572 con->http.version / 100, con->http.version % 100);
a74b005d 573
574 con->http.status = HTTP_OK;
a74b005d 575
8f85ff77 576 case HTTP_OPTIONS :
a74b005d 577 case HTTP_DELETE :
578 case HTTP_GET :
579 case HTTP_HEAD :
580 case HTTP_POST :
581 case HTTP_PUT :
582 case HTTP_TRACE :
583 /*
584 * Parse incoming parameters until the status changes...
585 */
586
587 status = httpUpdate(HTTP(con));
588
589 if (status != HTTP_OK && status != HTTP_CONTINUE)
590 {
591 SendError(con, HTTP_BAD_REQUEST);
592 CloseClient(con);
593 return (0);
594 }
595 break;
d21a7597 596
597 default :
598 break; /* Anti-compiler-warning-code */
a74b005d 599 }
600
601 /*
602 * Handle new transfers...
603 */
604
605 if (status == HTTP_OK)
606 {
607 con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
608
d2122fde 609 decode_auth(con);
a74b005d 610
d368929e 611 if (strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) == 0 &&
612 KeepAlive)
b1ac1113 613 con->http.keep_alive = HTTP_KEEPALIVE_ON;
614
a74b005d 615 if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
d558743f 616 con->http.version >= HTTP_1_1)
a74b005d 617 {
d558743f 618 /*
619 * HTTP/1.1 and higher require the "Host:" field...
620 */
621
a74b005d 622 if (!SendError(con, HTTP_BAD_REQUEST))
623 {
624 CloseClient(con);
625 return (0);
626 }
627 }
8f85ff77 628 else if (con->operation == HTTP_OPTIONS)
629 {
630 /*
631 * Do OPTIONS command...
632 */
633
7c7e9bbc 634 if ((best = FindBest(con->uri, con->http.state)) != NULL &&
8f85ff77 635 best->type != AUTH_NONE)
636 {
637 if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL))
638 {
639 CloseClient(con);
640 return (0);
641 }
642 }
a75c006a 643
4c6f986f 644 if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
a75c006a 645 con->http.tls == NULL)
8f85ff77 646 {
a75c006a 647#ifdef HAVE_LIBSSL
648 /*
649 * Do encryption stuff...
650 */
651
b38fb7fc 652 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
8f85ff77 653 {
654 CloseClient(con);
655 return (0);
656 }
a75c006a 657
658 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
4c6f986f 659 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1c6682dd 660 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
a75c006a 661 httpPrintf(HTTP(con), "\r\n");
662
663 EncryptClient(con);
664#else
665 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
666 {
667 CloseClient(con);
668 return (0);
669 }
670#endif /* HAVE_LIBSSL */
671 }
672
673 if (!SendHeader(con, HTTP_OK, NULL))
674 {
675 CloseClient(con);
676 return (0);
8f85ff77 677 }
678
93de21d5 679 httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
1c6682dd 680 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
8f85ff77 681 httpPrintf(HTTP(con), "\r\n");
682 }
bd84e0d1 683 else if (strstr(con->uri, "..") != NULL)
a74b005d 684 {
685 /*
686 * Protect against malicious users!
687 */
688
689 if (!SendError(con, HTTP_FORBIDDEN))
690 {
691 CloseClient(con);
692 return (0);
693 }
694 }
695 else if (con->uri[0] != '/')
696 {
697 /*
698 * Don't allow proxying (yet)...
699 */
700
701 if (!SendError(con, HTTP_METHOD_NOT_ALLOWED))
702 {
703 CloseClient(con);
704 return (0);
705 }
706 }
b38fb7fc 707 else
a74b005d 708 {
4c6f986f 709 if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
710 con->http.tls == NULL)
b38fb7fc 711 {
712#ifdef HAVE_LIBSSL
713 /*
714 * Do encryption stuff...
715 */
f3d580b9 716
b38fb7fc 717 if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
718 {
719 CloseClient(con);
720 return (0);
721 }
f969a074 722
b38fb7fc 723 httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
4c6f986f 724 httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
1c6682dd 725 httpPrintf(HTTP(con), "Content-Length: 0\r\n");
b38fb7fc 726 httpPrintf(HTTP(con), "\r\n");
727
728 EncryptClient(con);
729#else
730 if (!SendError(con, HTTP_NOT_IMPLEMENTED))
731 {
732 CloseClient(con);
733 return (0);
734 }
735#endif /* HAVE_LIBSSL */
736 }
aa530ae8 737
4c6f986f 738 if ((status = IsAuthorized(con)) != HTTP_OK)
b38fb7fc 739 {
740 SendError(con, status);
741 CloseClient(con);
742 return (0);
743 }
744
745 switch (con->http.state)
746 {
747 case HTTP_GET_SEND :
748 if (strncmp(con->uri, "/printers/", 10) == 0 &&
749 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
2a8fc30c 750 {
b38fb7fc 751 /*
752 * Send PPD file - get the real printer name since printer
753 * names are not case sensitive but filenames can be...
754 */
2a8fc30c 755
b38fb7fc 756 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
f3d580b9 757
b38fb7fc 758 if ((p = FindPrinter(con->uri + 10)) != NULL)
759 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
760 else
761 {
762 if (!SendError(con, HTTP_NOT_FOUND))
763 {
764 CloseClient(con);
765 return (0);
766 }
a74b005d 767
b38fb7fc 768 break;
769 }
b14d90ba 770 }
b38fb7fc 771
93de21d5 772 if ((strncmp(con->uri, "/admin", 6) == 0 &&
773 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
b38fb7fc 774 strncmp(con->uri, "/printers", 9) == 0 ||
775 strncmp(con->uri, "/classes", 8) == 0 ||
776 strncmp(con->uri, "/jobs", 5) == 0)
b14d90ba 777 {
b38fb7fc 778 /*
779 * Send CGI output...
780 */
781
782 if (strncmp(con->uri, "/admin", 6) == 0)
783 {
784 snprintf(con->command, sizeof(con->command),
785 "%s/cgi-bin/admin.cgi", ServerBin);
786 con->options = con->uri + 6;
787 }
788 else if (strncmp(con->uri, "/printers", 9) == 0)
789 {
790 snprintf(con->command, sizeof(con->command),
791 "%s/cgi-bin/printers.cgi", ServerBin);
792 con->options = con->uri + 9;
793 }
794 else if (strncmp(con->uri, "/classes", 8) == 0)
795 {
796 snprintf(con->command, sizeof(con->command),
797 "%s/cgi-bin/classes.cgi", ServerBin);
798 con->options = con->uri + 8;
799 }
800 else
801 {
802 snprintf(con->command, sizeof(con->command),
803 "%s/cgi-bin/jobs.cgi", ServerBin);
804 con->options = con->uri + 5;
805 }
806
807 if (con->options[0] == '/')
808 con->options ++;
809
810 if (!SendCommand(con, con->command, con->options))
811 {
812 if (!SendError(con, HTTP_NOT_FOUND))
813 {
814 CloseClient(con);
815 return (0);
816 }
817 }
818 else
819 LogRequest(con, HTTP_OK);
820
821 if (con->http.version <= HTTP_1_0)
822 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
b14d90ba 823 }
93de21d5 824 else if (strncmp(con->uri, "/admin/conf/", 12) == 0 &&
825 (strchr(con->uri + 12, '/') != NULL ||
826 strlen(con->uri) == 12))
827 {
828 /*
829 * GET can only be done to configuration files under
830 * /admin/conf...
831 */
832
833 if (!SendError(con, HTTP_FORBIDDEN))
834 {
835 CloseClient(con);
836 return (0);
837 }
838
839 break;
840 }
b14d90ba 841 else
842 {
b38fb7fc 843 /*
844 * Serve a file...
845 */
846
847 if ((filename = get_file(con, &filestats)) == NULL)
848 {
849 if (!SendError(con, HTTP_NOT_FOUND))
850 {
851 CloseClient(con);
852 return (0);
853 }
854 }
855 else if (!check_if_modified(con, &filestats))
856 {
857 if (!SendError(con, HTTP_NOT_MODIFIED))
858 {
859 CloseClient(con);
860 return (0);
861 }
862 }
863 else
864 {
865 type = mimeFileType(MimeDatabase, filename);
866 if (type == NULL)
867 strcpy(line, "text/plain");
868 else
869 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
870
871 if (!SendFile(con, HTTP_OK, filename, line, &filestats))
872 {
873 CloseClient(con);
874 return (0);
875 }
876 }
b14d90ba 877 }
b38fb7fc 878 break;
879
880 case HTTP_POST_RECV :
881 /*
882 * See if the POST request includes a Content-Length field, and if
883 * so check the length against any limits that are set...
884 */
b14d90ba 885
b38fb7fc 886 LogMessage(L_DEBUG2, "POST %s", con->uri);
887 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
b14d90ba 888
b38fb7fc 889 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
890 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
891 MaxRequestSize > 0)
a74b005d 892 {
b38fb7fc 893 /*
894 * Request too large...
895 */
896
897 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
a74b005d 898 {
b38fb7fc 899 CloseClient(con);
a74b005d 900 return (0);
901 }
b38fb7fc 902
903 break;
a74b005d 904 }
905
b38fb7fc 906 /*
907 * See what kind of POST request this is; for IPP requests the
908 * content-type field will be "application/ipp"...
a74b005d 909 */
910
b38fb7fc 911 if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
912 con->request = ippNew();
93de21d5 913 else if ((strncmp(con->uri, "/admin", 6) == 0 &&
914 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
b38fb7fc 915 strncmp(con->uri, "/printers", 9) == 0 ||
916 strncmp(con->uri, "/classes", 8) == 0 ||
917 strncmp(con->uri, "/jobs", 5) == 0)
a74b005d 918 {
b38fb7fc 919 /*
920 * CGI request...
921 */
922
923 if (strncmp(con->uri, "/admin", 6) == 0)
a74b005d 924 {
b38fb7fc 925 snprintf(con->command, sizeof(con->command),
926 "%s/cgi-bin/admin.cgi", ServerBin);
927 con->options = con->uri + 6;
a74b005d 928 }
b38fb7fc 929 else if (strncmp(con->uri, "/printers", 9) == 0)
a74b005d 930 {
b38fb7fc 931 snprintf(con->command, sizeof(con->command),
932 "%s/cgi-bin/printers.cgi", ServerBin);
933 con->options = con->uri + 9;
934 }
935 else if (strncmp(con->uri, "/classes", 8) == 0)
936 {
937 snprintf(con->command, sizeof(con->command),
938 "%s/cgi-bin/classes.cgi", ServerBin);
939 con->options = con->uri + 8;
a74b005d 940 }
e31bfb6e 941 else
a74b005d 942 {
b38fb7fc 943 snprintf(con->command, sizeof(con->command),
944 "%s/cgi-bin/jobs.cgi", ServerBin);
945 con->options = con->uri + 5;
a74b005d 946 }
8b43895a 947
b38fb7fc 948 if (con->options[0] == '/')
949 con->options ++;
f63a2256 950
b38fb7fc 951 LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"",
952 con->http.fd, con->command, con->options);
8b43895a 953
b38fb7fc 954 if (con->http.version <= HTTP_1_0)
955 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
956 }
957 else if (!SendError(con, HTTP_UNAUTHORIZED))
8b43895a 958 {
959 CloseClient(con);
960 return (0);
961 }
8b43895a 962 break;
b14d90ba 963
b38fb7fc 964 case HTTP_PUT_RECV :
93de21d5 965 /*
966 * Validate the resource name...
967 */
968
969 if (strncmp(con->uri, "/admin/conf/", 12) != 0 ||
970 strchr(con->uri + 12, '/') != NULL ||
971 strlen(con->uri) == 12)
972 {
973 /*
974 * PUT can only be done to configuration files under
975 * /admin/conf...
976 */
977
978 if (!SendError(con, HTTP_FORBIDDEN))
979 {
980 CloseClient(con);
981 return (0);
982 }
983
984 break;
985 }
986
987 /*
988 * See if the PUT request includes a Content-Length field, and if
989 * so check the length against any limits that are set...
990 */
991
992 LogMessage(L_DEBUG2, "PUT %s", con->uri);
993 LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
994
995 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
996 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
997 MaxRequestSize > 0)
998 {
999 /*
1000 * Request too large...
1001 */
1002
1003 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1004 {
1005 CloseClient(con);
1006 return (0);
1007 }
1008
1009 break;
1010 }
1011
1012 /*
1013 * Open a temporary file to hold the request...
1014 */
1015
1016 snprintf(con->filename, sizeof(con->filename), "%s/%08x",
1017 RequestRoot, request_id ++);
1018 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1019 fchmod(con->file, 0640);
1020 fchown(con->file, User, Group);
1021
1022 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
1023 con->filename, con->file);
1024
1025 if (con->file < 0)
1026 {
1027 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1028 {
1029 CloseClient(con);
1030 return (0);
1031 }
1032 }
1033 break;
1034
b38fb7fc 1035 case HTTP_DELETE :
1036 case HTTP_TRACE :
1037 SendError(con, HTTP_NOT_IMPLEMENTED);
1038 CloseClient(con);
1039 return (0);
b14d90ba 1040
b38fb7fc 1041 case HTTP_HEAD :
d588a92e 1042 if (strncmp(con->uri, "/printers/", 10) == 0 &&
b38fb7fc 1043 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
bd84e0d1 1044 {
b38fb7fc 1045 /*
d588a92e 1046 * Send PPD file - get the real printer name since printer
1047 * names are not case sensitive but filenames can be...
b38fb7fc 1048 */
a74b005d 1049
d588a92e 1050 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
1051
1052 if ((p = FindPrinter(con->uri + 10)) != NULL)
1053 snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
1054 else
1055 {
1056 if (!SendError(con, HTTP_NOT_FOUND))
1057 {
1058 CloseClient(con);
1059 return (0);
1060 }
1061
1062 break;
1063 }
b38fb7fc 1064 }
a74b005d 1065
93de21d5 1066 if ((strncmp(con->uri, "/admin/", 7) == 0 &&
1067 strncmp(con->uri, "/admin/conf/", 12) != 0) ||
b38fb7fc 1068 strncmp(con->uri, "/printers/", 10) == 0 ||
1069 strncmp(con->uri, "/classes/", 9) == 0 ||
1070 strncmp(con->uri, "/jobs/", 6) == 0)
1071 {
1072 /*
1073 * CGI output...
1074 */
f3d580b9 1075
b38fb7fc 1076 if (!SendHeader(con, HTTP_OK, "text/html"))
1077 {
1078 CloseClient(con);
1079 return (0);
1080 }
f3d580b9 1081
b38fb7fc 1082 if (httpPrintf(HTTP(con), "\r\n") < 0)
1083 {
1084 CloseClient(con);
1085 return (0);
1086 }
a74b005d 1087
b38fb7fc 1088 LogRequest(con, HTTP_OK);
a74b005d 1089 }
93de21d5 1090 else if (strncmp(con->uri, "/admin/conf/", 12) == 0 &&
1091 (strchr(con->uri + 12, '/') != NULL ||
1092 strlen(con->uri) == 12))
1093 {
1094 /*
1095 * HEAD can only be done to configuration files under
1096 * /admin/conf...
1097 */
1098
1099 if (!SendError(con, HTTP_FORBIDDEN))
1100 {
1101 CloseClient(con);
1102 return (0);
1103 }
1104
1105 break;
1106 }
b38fb7fc 1107 else if ((filename = get_file(con, &filestats)) == NULL)
a74b005d 1108 {
b38fb7fc 1109 if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
1110 {
1111 CloseClient(con);
1112 return (0);
1113 }
6a0c519d 1114
b38fb7fc 1115 LogRequest(con, HTTP_NOT_FOUND);
a74b005d 1116 }
b38fb7fc 1117 else if (!check_if_modified(con, &filestats))
1118 {
1119 if (!SendError(con, HTTP_NOT_MODIFIED))
1120 {
1121 CloseClient(con);
1122 return (0);
1123 }
6a0c519d 1124
b38fb7fc 1125 LogRequest(con, HTTP_NOT_MODIFIED);
a74b005d 1126 }
b38fb7fc 1127 else
1128 {
1129 /*
1130 * Serve a file...
1131 */
6a0c519d 1132
b38fb7fc 1133 type = mimeFileType(MimeDatabase, filename);
1134 if (type == NULL)
1135 strcpy(line, "text/plain");
1136 else
1137 snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
a74b005d 1138
b38fb7fc 1139 if (!SendHeader(con, HTTP_OK, line))
1140 {
1141 CloseClient(con);
1142 return (0);
1143 }
a74b005d 1144
b38fb7fc 1145 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
1146 httpGetDateString(filestats.st_mtime)) < 0)
1147 {
1148 CloseClient(con);
1149 return (0);
1150 }
a74b005d 1151
0e0ac99a 1152 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1153 (unsigned long)filestats.st_size) < 0)
b38fb7fc 1154 {
1155 CloseClient(con);
1156 return (0);
1157 }
1158
1159 LogRequest(con, HTTP_OK);
a74b005d 1160 }
1161
b38fb7fc 1162 if (httpPrintf(HTTP(con), "\r\n") < 0)
a74b005d 1163 {
1164 CloseClient(con);
1165 return (0);
1166 }
6a0c519d 1167
b38fb7fc 1168 con->http.state = HTTP_WAITING;
1169 break;
a74b005d 1170
b38fb7fc 1171 default :
1172 break; /* Anti-compiler-warning-code */
1173 }
a74b005d 1174 }
1175 }
1176
1177 /*
1178 * Handle any incoming data...
1179 */
1180
1181 switch (con->http.state)
1182 {
1183 case HTTP_PUT_RECV :
93de21d5 1184 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
1185 con->http.fd,
1186 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
1187 con->http.data_remaining, con->file);
1188
1189 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
1190 {
1191 CloseClient(con);
1192 return (0);
1193 }
1194 else if (bytes > 0)
1195 {
1196 con->bytes += bytes;
1197
1198 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
1199 con->http.fd, bytes, con->file);
1200
1201 if (write(con->file, line, bytes) < bytes)
1202 {
1203 LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s",
1204 bytes, con->filename, strerror(errno));
1205
1206 close(con->file);
1207 con->file = 0;
1208 unlink(con->filename);
0e0ac99a 1209 con->filename[0] = '\0';
93de21d5 1210
1211 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1212 {
1213 CloseClient(con);
1214 return (0);
1215 }
1216 }
1217 }
ae3c9f2d 1218
1219 if (con->http.state == HTTP_WAITING)
93de21d5 1220 {
1221 /*
1222 * End of file, see how big it is...
1223 */
1224
1225 fstat(con->file, &filestats);
1226
1227 LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
1228 con->http.fd, con->file, filestats.st_size);
1229
1230 close(con->file);
1231 con->file = 0;
1232
1233 if (filestats.st_size > MaxRequestSize &&
1234 MaxRequestSize > 0)
1235 {
1236 /*
1237 * Request is too big; remove it and send an error...
1238 */
1239
1240 LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
1241 con->http.fd, con->filename);
1242 unlink(con->filename);
0e0ac99a 1243 con->filename[0] = '\0';
93de21d5 1244
1245 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1246 {
1247 CloseClient(con);
1248 return (0);
1249 }
1250 }
1251
1252 /*
1253 * Install the configuration file...
1254 */
1255
1256 status = install_conf_file(con);
1257
1258 /*
1259 * Return the status to the client...
1260 */
1261
1262 if (!SendError(con, status))
1263 {
1264 CloseClient(con);
1265 return (0);
1266 }
1267 }
a74b005d 1268 break;
1269
1270 case HTTP_POST_RECV :
58e8cf34 1271 LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
93894a43 1272 con->http.fd,
a74b005d 1273 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
f63a2256 1274 con->http.data_remaining, con->file);
a74b005d 1275
b14d90ba 1276 if (con->request != NULL)
1277 {
1278 /*
1279 * Grab any request data from the connection...
1280 */
1281
bd176c9c 1282 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
1283 {
5ea8888e 1284 LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!",
bd176c9c 1285 con->http.fd);
1286 CloseClient(con);
1287 return (0);
1288 }
1289 else if (ipp_state != IPP_DATA)
b14d90ba 1290 break;
68ff4f01 1291 else
1292 con->bytes += ippLength(con->request);
f63a2256 1293 }
b14d90ba 1294
f63a2256 1295 if (con->file == 0 && con->http.state != HTTP_POST_SEND)
1296 {
1297 /*
1298 * Create a file as needed for the request data...
1299 */
1d2c70a6 1300
04de52f8 1301 snprintf(con->filename, sizeof(con->filename), "%s/%08x",
f63a2256 1302 RequestRoot, request_id ++);
1303 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
1304 fchmod(con->file, 0640);
1305 fchown(con->file, User, Group);
b14d90ba 1306
a61a2be6 1307 LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
1308 con->filename, con->file);
b14d90ba 1309
f63a2256 1310 if (con->file < 0)
1311 {
1312 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
b14d90ba 1313 {
f63a2256 1314 CloseClient(con);
1315 return (0);
b14d90ba 1316 }
1317 }
f63a2256 1318 }
b14d90ba 1319
bfa1abf0 1320 if (con->http.state != HTTP_POST_SEND)
a74b005d 1321 {
bfa1abf0 1322 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
a74b005d 1323 {
bfa1abf0 1324 CloseClient(con);
1325 return (0);
1326 }
1327 else if (bytes > 0)
1328 {
1329 con->bytes += bytes;
a74b005d 1330
a61a2be6 1331 LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
1332 con->http.fd, bytes, con->file);
bfa1abf0 1333
1334 if (write(con->file, line, bytes) < bytes)
a74b005d 1335 {
a61a2be6 1336 LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s",
1337 bytes, con->filename, strerror(errno));
1338
bfa1abf0 1339 close(con->file);
1340 con->file = 0;
1341 unlink(con->filename);
0e0ac99a 1342 con->filename[0] = '\0';
bfa1abf0 1343
1344 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1345 {
1346 CloseClient(con);
1347 return (0);
1348 }
a74b005d 1349 }
1350 }
bd176c9c 1351 else if (con->http.state != HTTP_POST_SEND)
1352 {
1353 CloseClient(con);
1354 return (0);
1355 }
a74b005d 1356 }
93894a43 1357
1358 if (con->http.state == HTTP_POST_SEND)
1359 {
bfa1abf0 1360 if (con->file)
1361 {
8b43895a 1362 fstat(con->file, &filestats);
bd917997 1363
1364 LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
1365 con->http.fd, con->file, filestats.st_size);
1366
bfa1abf0 1367 close(con->file);
1368 con->file = 0;
8b43895a 1369
1370 if (filestats.st_size > MaxRequestSize &&
1371 MaxRequestSize > 0)
1372 {
1373 /*
1374 * Request is too big; remove it and send an error...
1375 */
1376
a61a2be6 1377 LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
1378 con->http.fd, con->filename);
8b43895a 1379 unlink(con->filename);
0e0ac99a 1380 con->filename[0] = '\0';
8b43895a 1381
1382 if (con->request)
1383 {
1384 /*
1385 * Delete any IPP request data...
1386 */
1387
1388 ippDelete(con->request);
1389 con->request = NULL;
1390 }
1391
1392 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
1393 {
1394 CloseClient(con);
1395 return (0);
1396 }
1397 }
f63a2256 1398
1399 if (con->command[0])
1400 {
1401 if (!SendCommand(con, con->command, con->options))
1402 {
1403 if (!SendError(con, HTTP_NOT_FOUND))
1404 {
1405 CloseClient(con);
1406 return (0);
1407 }
1408 }
1409 else
1410 LogRequest(con, HTTP_OK);
1411 }
bfa1abf0 1412 }
93894a43 1413
1414 if (con->request)
e31bfb6e 1415 ProcessIPPRequest(con);
93894a43 1416 }
a74b005d 1417 break;
d21a7597 1418
1419 default :
1420 break; /* Anti-compiler-warning-code */
a74b005d 1421 }
1422
1423 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
1424 {
1425 CloseClient(con);
1426 return (0);
1427 }
1428 else
1429 return (1);
1430}
1431
1432
a74b005d 1433/*
1434 * 'SendCommand()' - Send output from a command via HTTP.
1435 */
1436
1437int
1438SendCommand(client_t *con,
a74b005d 1439 char *command,
b14d90ba 1440 char *options)
a74b005d 1441{
f63a2256 1442 int fd;
1443
1444
1445 if (con->filename[0])
1446 fd = open(con->filename, O_RDONLY);
1447 else
1448 fd = open("/dev/null", O_RDONLY);
1449
1450 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
1451
1452 close(fd);
a74b005d 1453
aa530ae8 1454 LogMessage(L_INFO, "Started \"%s\" (pid=%d)", command, con->pipe_pid);
1455
1456 LogMessage(L_DEBUG, "SendCommand() %d file=%d", con->http.fd, con->file);
a74b005d 1457
1458 if (con->pipe_pid == 0)
1459 return (0);
1460
1461 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1462
27eba2dd 1463 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to InputSet...", con->file);
e668e627 1464 LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to OutputSet...",
1465 con->http.fd);
1466
a74b005d 1467 FD_SET(con->file, &InputSet);
1468 FD_SET(con->http.fd, &OutputSet);
1469
96df88bb 1470 if (!SendHeader(con, HTTP_OK, NULL))
a74b005d 1471 return (0);
1472
1473 if (con->http.version == HTTP_1_1)
1474 {
1475 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
1476
1477 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
1478 return (0);
1479 }
1480
13486cb7 1481 con->got_fields = 0;
1482 con->field_col = 0;
1483
a74b005d 1484 return (1);
1485}
1486
1487
1488/*
1489 * 'SendError()' - Send an error message via HTTP.
1490 */
1491
1492int /* O - 1 if successful, 0 otherwise */
1493SendError(client_t *con, /* I - Connection */
1494 http_status_t code) /* I - Error code */
1495{
83e740a5 1496 char message[1024]; /* Message for user */
a74b005d 1497
1498
6a0c519d 1499 /*
1500 * Put the request in the access_log file...
1501 */
1502
993e15da 1503 if (con->operation > HTTP_WAITING)
1504 LogRequest(con, code);
6a0c519d 1505
ae3c9f2d 1506 LogMessage(L_DEBUG, "SendError() %d code=%d", con->http.fd, code);
a61a2be6 1507
a74b005d 1508 /*
1509 * To work around bugs in some proxies, don't use Keep-Alive for some
1510 * error messages...
1511 */
1512
1513 if (code >= HTTP_BAD_REQUEST)
1514 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
1515
1516 /*
1517 * Send an error message back to the client. If the error code is a
1518 * 400 or 500 series, make sure the message contains some text, too!
1519 */
1520
1521 if (!SendHeader(con, code, NULL))
1522 return (0);
1523
a75c006a 1524#ifdef HAVE_LIBSSL
1525 if (code == HTTP_UPGRADE_REQUIRED)
a75c006a 1526 if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0)
1527 return (0);
1528
4c6f986f 1529 if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0)
1530 return (0);
a75c006a 1531#endif /* HAVE_LIBSSL */
1532
a74b005d 1533 if (con->http.version >= HTTP_1_1 && !con->http.keep_alive)
1534 {
1535 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
1536 return (0);
1537 }
1538
1539 if (code >= HTTP_BAD_REQUEST)
1540 {
1541 /*
1542 * Send a human-readable error message.
1543 */
1544
04de52f8 1545 snprintf(message, sizeof(message),
970017a4 1546 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
1547 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
1548 code, httpStatus(code), httpStatus(code),
1549 con->language ? con->language->messages[code] :
1550 httpStatus(code));
a74b005d 1551
1552 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
1553 return (0);
1554 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", strlen(message)) < 0)
1555 return (0);
1556 if (httpPrintf(HTTP(con), "\r\n") < 0)
1557 return (0);
97d73ddb 1558 if (httpPrintf(HTTP(con), "%s", message) < 0)
a74b005d 1559 return (0);
1560 }
1561 else if (httpPrintf(HTTP(con), "\r\n") < 0)
1562 return (0);
1563
1564 con->http.state = HTTP_WAITING;
1565
1566 return (1);
1567}
1568
1569
1570/*
1571 * 'SendFile()' - Send a file via HTTP.
1572 */
1573
1574int
1575SendFile(client_t *con,
1576 http_status_t code,
1577 char *filename,
1578 char *type,
1579 struct stat *filestats)
1580{
1581 con->file = open(filename, O_RDONLY);
1582
5ea8888e 1583 LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
a74b005d 1584
1585 if (con->file < 0)
1586 return (0);
1587
1588 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1589
1590 con->pipe_pid = 0;
1591
1592 if (!SendHeader(con, code, type))
1593 return (0);
1594
1595 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
1596 return (0);
0e0ac99a 1597 if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
1598 (unsigned long)filestats->st_size) < 0)
a74b005d 1599 return (0);
1600 if (httpPrintf(HTTP(con), "\r\n") < 0)
1601 return (0);
1602
e668e627 1603 LogMessage(L_DEBUG2, "SendFile: Adding fd %d to OutputSet...", con->http.fd);
1604
a74b005d 1605 FD_SET(con->http.fd, &OutputSet);
1606
1607 return (1);
1608}
1609
1610
1611/*
93894a43 1612 * 'SendHeader()' - Send an HTTP request.
a74b005d 1613 */
1614
1615int /* O - 1 on success, 0 on failure */
1616SendHeader(client_t *con, /* I - Client to send to */
1617 http_status_t code, /* I - HTTP status code */
1618 char *type) /* I - MIME type of document */
1619{
83e740a5 1620 location_t *loc; /* Authentication location */
1621
1622
a74b005d 1623 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
ffe90a99 1624 con->http.version % 100, code, httpStatus(code)) < 0)
a74b005d 1625 return (0);
1626 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
1627 return (0);
bd84e0d1 1628 if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
a74b005d 1629 return (0);
1630 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
1631 {
1632 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
1633 return (0);
1634 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
1635 return (0);
1636 }
8f85ff77 1637 if (code == HTTP_METHOD_NOT_ALLOWED)
9bfcbffc 1638 if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
8f85ff77 1639 return (0);
1640
5ea8888e 1641 if (code == HTTP_UNAUTHORIZED)
83e740a5 1642 {
7c7e9bbc 1643 /*
1644 * This already succeeded in IsAuthorized...
1645 */
1646
1647 loc = FindBest(con->uri, con->http.state);
83e740a5 1648
61858bb4 1649 if (loc->type != AUTH_DIGEST)
83e740a5 1650 {
1651 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
1652 return (0);
1653 }
1654 else
1655 {
1656 if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" "
1657 "nonce=\"%s\"\r\n", con->http.hostname) < 0)
1658 return (0);
1659 }
1660 }
a74b005d 1661 if (con->language != NULL)
1662 {
1663 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
1664 con->language->language) < 0)
1665 return (0);
1666
1667 if (type != NULL)
1668 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
1669 cupsLangEncoding(con->language)) < 0)
1670 return (0);
1671 }
1672 else if (type != NULL)
1673 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
1674 return (0);
1675
1676 return (1);
1677}
1678
1679
a74b005d 1680/*
1681 * 'WriteClient()' - Write data to a client as needed.
1682 */
1683
ff49100f 1684int /* O - 1 if success, 0 if fail */
1685WriteClient(client_t *con) /* I - Client connection */
a74b005d 1686{
ff49100f 1687 int bytes; /* Number of bytes written */
13486cb7 1688 char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */
1689 char *bufptr; /* Pointer into buffer */
ff49100f 1690 ipp_state_t ipp_state; /* IPP state value */
a74b005d 1691
1692
1693 if (con->http.state != HTTP_GET_SEND &&
1694 con->http.state != HTTP_POST_SEND)
1695 return (1);
1696
ff49100f 1697 if (con->response != NULL)
bfa1abf0 1698 {
ff49100f 1699 ipp_state = ippWrite(&(con->http), con->response);
1700 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
bfa1abf0 1701 }
13486cb7 1702 else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
a74b005d 1703 {
13486cb7 1704 if (con->pipe_pid && !con->got_fields)
1705 {
1706 /*
1707 * Inspect the data for Content-Type and other fields.
1708 */
1709
1710 buf[bytes] = '\0';
1711
1712 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
1713 if (*bufptr == '\n')
1714 {
1715 /*
1716 * Send line to client...
1717 */
1718
1719 if (bufptr > buf && bufptr[-1] == '\r')
1720 bufptr[-1] = '\0';
1721 *bufptr++ = '\0';
1722
1723 httpPrintf(HTTP(con), "%s\r\n", buf);
58e8cf34 1724 LogMessage(L_DEBUG2, "WriteClient() %d %s", con->http.fd, buf);
13486cb7 1725
1726 /*
1727 * Update buffer...
1728 */
1729
1730 bytes -= (bufptr - buf);
1731 memcpy(buf, bufptr, bytes + 1);
1732 bufptr = buf - 1;
1733
1734 /*
1735 * See if the line was empty...
1736 */
1737
1738 if (con->field_col == 0)
1739 con->got_fields = 1;
1740 else
1741 con->field_col = 0;
1742 }
1743 else if (*bufptr != '\r')
1744 con->field_col ++;
1745
1746 if (bytes > 0 && !con->got_fields)
1747 {
1748 /*
1749 * Remaining text needs to go out...
1750 */
1751
1752 httpPrintf(HTTP(con), "%s", buf);
1753
1754 con->http.activity = time(NULL);
1755 return (1);
1756 }
1757 else if (bytes == 0)
1758 {
1759 con->http.activity = time(NULL);
1760 return (1);
1761 }
1762 }
1763
a74b005d 1764 if (httpWrite(HTTP(con), buf, bytes) < 0)
1765 {
1766 CloseClient(con);
1767 return (0);
1768 }
6a0c519d 1769
1770 con->bytes += bytes;
a74b005d 1771 }
b14d90ba 1772
1773 if (bytes <= 0)
a74b005d 1774 {
6a0c519d 1775 LogRequest(con, HTTP_OK);
1776
a74b005d 1777 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
1778 {
1779 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
1780 {
1781 CloseClient(con);
1782 return (0);
1783 }
1784 }
1785
96df88bb 1786 con->http.state = HTTP_WAITING;
1787
4aca0e1b 1788 LogMessage(L_DEBUG2, "WriteClient: Removing fd %d from OutputSet...",
e668e627 1789 con->http.fd);
1790
a74b005d 1791 FD_CLR(con->http.fd, &OutputSet);
a74b005d 1792
bfa1abf0 1793 if (con->file)
a74b005d 1794 {
4aca0e1b 1795 LogMessage(L_DEBUG2, "WriteClient: Removing fd %d from InputSet...",
27eba2dd 1796 con->file);
bfa1abf0 1797 FD_CLR(con->file, &InputSet);
a74b005d 1798
bfa1abf0 1799 if (con->pipe_pid)
96df88bb 1800 kill(con->pipe_pid, SIGTERM);
bfa1abf0 1801
4aca0e1b 1802 LogMessage(L_DEBUG2, "WriteClient: %d Closing data file %d.",
bd917997 1803 con->http.fd, con->file);
1804
bfa1abf0 1805 close(con->file);
96df88bb 1806 con->file = 0;
1807 con->pipe_pid = 0;
bfa1abf0 1808 }
a74b005d 1809
d4c438d4 1810 if (con->filename[0])
a61a2be6 1811 {
4aca0e1b 1812 LogMessage(L_DEBUG2, "WriteClient: %d Removing temp file %s",
bd917997 1813 con->http.fd, con->filename);
d4c438d4 1814 unlink(con->filename);
0e0ac99a 1815 con->filename[0] = '\0';
a61a2be6 1816 }
d4c438d4 1817
b14d90ba 1818 if (con->request != NULL)
1819 {
1820 ippDelete(con->request);
1821 con->request = NULL;
1822 }
1823
1824 if (con->response != NULL)
1825 {
1826 ippDelete(con->response);
1827 con->response = NULL;
1828 }
96df88bb 1829
1830 if (!con->http.keep_alive)
1831 {
1832 CloseClient(con);
1833 return (0);
1834 }
a74b005d 1835 }
1836
997edb40 1837 if (bytes >= 1024)
58e8cf34 1838 LogMessage(L_DEBUG2, "WriteClient() %d %d bytes", con->http.fd, bytes);
a74b005d 1839
1840 con->http.activity = time(NULL);
1841
1842 return (1);
1843}
1844
1845
a74b005d 1846/*
1847 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
1848 */
1849
1850static int /* O - 1 if modified since */
1851check_if_modified(client_t *con, /* I - Client connection */
1852 struct stat *filestats) /* I - File information */
1853{
1854 char *ptr; /* Pointer into field */
1855 time_t date; /* Time/date value */
1856 int size; /* Size/length value */
1857
1858
1859 size = 0;
1860 date = 0;
1861 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
1862
1863 if (*ptr == '\0')
1864 return (1);
1865
58e8cf34 1866 LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"",
4a64fdb7 1867 con->http.fd, ptr);
1868
a74b005d 1869 while (*ptr != '\0')
1870 {
4a64fdb7 1871 while (isspace(*ptr) || *ptr == ';')
a74b005d 1872 ptr ++;
1873
1874 if (strncasecmp(ptr, "length=", 7) == 0)
1875 {
1876 ptr += 7;
1877 size = atoi(ptr);
1878
1879 while (isdigit(*ptr))
1880 ptr ++;
1881 }
4a64fdb7 1882 else if (isalpha(*ptr))
a74b005d 1883 {
1884 date = httpGetDateTime(ptr);
4a64fdb7 1885 while (*ptr != '\0' && *ptr != ';')
a74b005d 1886 ptr ++;
1887 }
1888 }
1889
58e8cf34 1890 LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
4a64fdb7 1891 con->http.fd, size, filestats->st_size, date, filestats->st_mtime);
1892
a74b005d 1893 return ((size != filestats->st_size && size != 0) ||
4a64fdb7 1894 (date < filestats->st_mtime && date != 0) ||
1895 (size == 0 && date == 0));
a74b005d 1896}
1897
1898
1899/*
d2122fde 1900 * 'decode_auth()' - Decode an authorization string.
a74b005d 1901 */
1902
93894a43 1903static void
d2122fde 1904decode_auth(client_t *con) /* I - Client to decode to */
a74b005d 1905{
d2122fde 1906 char *s, /* Authorization string */
1907 value[1024]; /* Value string */
1908 const char *username; /* Certificate username */
a74b005d 1909
a74b005d 1910
1911 /*
b0d58d07 1912 * Decode the string...
a74b005d 1913 */
1914
f3d580b9 1915 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
f3d580b9 1916
27eba2dd 1917 LogMessage(L_DEBUG2, "decode_auth(%08x): Authorization string = \"%s\"",
1918 con, s);
69f14ebb 1919
d2122fde 1920 if (strncmp(s, "Basic", 5) == 0)
1921 {
1922 s += 5;
1923 while (isspace(*s))
1924 s ++;
f3d580b9 1925
d2122fde 1926 httpDecode64(value, s);
a74b005d 1927
d2122fde 1928 /*
1929 * Pull the username and password out...
1930 */
b0d58d07 1931
d2122fde 1932 if ((s = strchr(value, ':')) == NULL)
1933 {
5ea8888e 1934 LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
d2122fde 1935 con->http.fd, value);
1936 return;
1937 }
1938
1939 *s++ = '\0';
b0d58d07 1940
d2122fde 1941 strncpy(con->username, value, sizeof(con->username) - 1);
1942 con->username[sizeof(con->username) - 1] = '\0';
b0d58d07 1943
d2122fde 1944 strncpy(con->password, s, sizeof(con->password) - 1);
1945 con->password[sizeof(con->password) - 1] = '\0';
1946 }
1947 else if (strncmp(s, "Local", 5) == 0)
1948 {
1949 s += 5;
1950 while (isspace(*s))
1951 s ++;
b0d58d07 1952
d2122fde 1953 if ((username = FindCert(s)) != NULL)
a6988fb1 1954 {
1955 strncpy(con->username, username, sizeof(con->username) - 1);
1956 con->username[sizeof(con->username) - 1] = '\0';
1957 }
d2122fde 1958 }
83e740a5 1959 else if (strncmp(s, "Digest", 5) == 0)
1960 {
1961 /*
1962 * Get the username and password from the Digest attributes...
1963 */
1964
83a2ea50 1965 if (httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "username",
83e740a5 1966 value))
1967 {
1968 strncpy(con->username, value, sizeof(con->username) - 1);
1969 con->username[sizeof(con->username) - 1] = '\0';
1970 }
1971
83a2ea50 1972 if (httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "response",
83e740a5 1973 value))
1974 {
1975 strncpy(con->password, value, sizeof(con->password) - 1);
1976 con->password[sizeof(con->password) - 1] = '\0';
1977 }
1978 }
41637133 1979
27eba2dd 1980 LogMessage(L_DEBUG2, "decode_auth() %d username=\"%s\"",
41637133 1981 con->http.fd, con->username);
a74b005d 1982}
1983
1984
e31bfb6e 1985/*
1986 * 'get_file()' - Get a filename and state info.
1987 */
1988
1989static char * /* O - Real filename */
1990get_file(client_t *con, /* I - Client connection */
1991 struct stat *filestats)/* O - File information */
1992{
1993 int status; /* Status of filesystem calls */
1994 char *params; /* Pointer to parameters in URI */
1995 static char filename[1024]; /* Filename buffer */
1996
1997
1998 /*
1999 * Need to add DocumentRoot global...
2000 */
2001
f3d580b9 2002 if (strncmp(con->uri, "/ppd/", 5) == 0)
04de52f8 2003 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri);
93de21d5 2004 else if (strncmp(con->uri, "/admin/conf/", 12) == 0)
2005 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri + 11);
f3d580b9 2006 else if (con->language != NULL)
04de52f8 2007 snprintf(filename, sizeof(filename), "%s/%s%s", DocumentRoot, con->language->language,
e31bfb6e 2008 con->uri);
2009 else
04de52f8 2010 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
e31bfb6e 2011
2012 if ((params = strchr(filename, '?')) != NULL)
2013 *params = '\0';
2014
2015 /*
2016 * Grab the status for this language; if there isn't a language-specific file
2017 * then fallback to the default one...
2018 */
2019
2020 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
2021 {
2022 /*
2023 * Drop the language prefix and try the current directory...
2024 */
2025
93de21d5 2026 if (strncmp(con->uri, "/ppd/", 5) != 0 &&
2027 strncmp(con->uri, "/admin/conf/", 12) != 0)
f3d580b9 2028 {
04de52f8 2029 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
e31bfb6e 2030
f3d580b9 2031 status = stat(filename, filestats);
2032 }
e31bfb6e 2033 }
2034
2035 /*
2036 * If we're found a directory, get the index.html file instead...
2037 */
2038
2039 if (!status && S_ISDIR(filestats->st_mode))
2040 {
2041 if (filename[strlen(filename) - 1] == '/')
d588a92e 2042 strncat(filename, "index.html", sizeof(filename));
e31bfb6e 2043 else
d588a92e 2044 strncat(filename, "/index.html", sizeof(filename));
2045
2046 filename[sizeof(filename) - 1] = '\0';
e31bfb6e 2047
2048 status = stat(filename, filestats);
2049 }
2050
58e8cf34 2051 LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d",
997edb40 2052 con->http.fd, filename, status ? -1 : filestats->st_size);
e31bfb6e 2053
2054 if (status)
2055 return (NULL);
2056 else
2057 return (filename);
2058}
2059
2060
93de21d5 2061/*
2062 * 'install_conf_file()' - Install a configuration file.
2063 */
2064
2065static http_status_t /* O - Status */
2066install_conf_file(client_t *con) /* I - Connection */
2067{
2068 FILE *in, /* Input file */
2069 *out; /* Output file */
2070 char buffer[1024]; /* Copy buffer */
2071 int bytes; /* Number of bytes */
2072 char conffile[1024], /* Configuration filename */
2073 newfile[1024], /* New config filename */
2074 oldfile[1024]; /* Old config filename */
2075 struct stat confinfo; /* Config file info */
2076
2077
2078 /*
2079 * First construct the filenames...
2080 */
2081
ae3c9f2d 2082 snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11);
2083 snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11);
2084 snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11);
93de21d5 2085
2086 LogMessage(L_INFO, "Installing config file \"%s\"...", conffile);
2087
2088 /*
2089 * Get the owner, group, and permissions of the configuration file.
2090 * If it doesn't exist, assign it to the User and Group in the
ae3c9f2d 2091 * cupsd.conf file with mode 0640 permissions.
93de21d5 2092 */
2093
2094 if (stat(conffile, &confinfo))
2095 {
2096 confinfo.st_uid = User;
2097 confinfo.st_gid = Group;
ae3c9f2d 2098 confinfo.st_mode = 0640;
93de21d5 2099 }
2100
2101 /*
2102 * Open the request file and new config file...
2103 */
2104
2105 if ((in = fopen(con->filename, "rb")) == NULL)
2106 {
2107 LogMessage(L_ERROR, "Unable to open request file \"%s\" - %s",
2108 con->filename, strerror(errno));
2109 return (HTTP_SERVER_ERROR);
2110 }
2111
2112 if ((out = fopen(newfile, "wb")) == NULL)
2113 {
2114 fclose(in);
2115 LogMessage(L_ERROR, "Unable to open config file \"%s\" - %s",
2116 newfile, strerror(errno));
2117 return (HTTP_SERVER_ERROR);
2118 }
2119
2120 fchmod(fileno(out), confinfo.st_mode);
2121 fchown(fileno(out), confinfo.st_uid, confinfo.st_gid);
2122
2123 /*
2124 * Copy from the request to the new config file...
2125 */
2126
2127 while ((bytes = fread(buffer, 1, sizeof(buffer), in)) > 0)
2128 if (fwrite(buffer, 1, bytes, out) < bytes)
2129 {
2130 LogMessage(L_ERROR, "Unable to copy to config file \"%s\" - %s",
2131 newfile, strerror(errno));
2132
2133 fclose(in);
2134 fclose(out);
2135 unlink(newfile);
2136
2137 return (HTTP_SERVER_ERROR);
2138 }
2139
2140 /*
2141 * Close the files...
2142 */
2143
2144 fclose(in);
2145 if (fclose(out))
2146 {
2147 LogMessage(L_ERROR, "Error file closing config file \"%s\" - %s",
2148 newfile, strerror(errno));
2149
2150 unlink(newfile);
2151
2152 return (HTTP_SERVER_ERROR);
2153 }
2154
05be9805 2155 /*
2156 * Remove the request file...
2157 */
2158
2159 unlink(con->filename);
2160 con->filename[0] = '\0';
2161
93de21d5 2162 /*
2163 * Unlink the old backup, rename the current config file to the backup
2164 * filename, and rename the new config file to the config file name...
2165 */
2166
2167 if (unlink(oldfile))
ae3c9f2d 2168 if (errno != ENOENT)
2169 {
2170 LogMessage(L_ERROR, "Unable to remove backup config file \"%s\" - %s",
2171 oldfile, strerror(errno));
93de21d5 2172
ae3c9f2d 2173 unlink(newfile);
93de21d5 2174
ae3c9f2d 2175 return (HTTP_SERVER_ERROR);
2176 }
93de21d5 2177
2178 if (rename(conffile, oldfile))
ae3c9f2d 2179 if (errno != ENOENT)
2180 {
2181 LogMessage(L_ERROR, "Unable to rename old config file \"%s\" - %s",
2182 conffile, strerror(errno));
93de21d5 2183
ae3c9f2d 2184 unlink(newfile);
93de21d5 2185
ae3c9f2d 2186 return (HTTP_SERVER_ERROR);
2187 }
93de21d5 2188
2189 if (rename(newfile, conffile))
2190 {
2191 LogMessage(L_ERROR, "Unable to rename new config file \"%s\" - %s",
2192 newfile, strerror(errno));
2193
2194 rename(oldfile, conffile);
2195 unlink(newfile);
2196
2197 return (HTTP_SERVER_ERROR);
2198 }
2199
b1e1ae04 2200 /*
2201 * If the cupsd.conf file was updated, set the NeedReload flag...
2202 */
2203
2204 if (strcmp(con->uri, "/admin/conf/cupsd.conf") == 0)
2205 NeedReload = TRUE;
2206
2207 /*
2208 * Return that the file was created successfully...
2209 */
2210
93de21d5 2211 return (HTTP_CREATED);
2212}
2213
2214
a74b005d 2215/*
2216 * 'pipe_command()' - Pipe the output of a command to the remote client.
2217 */
2218
2219static int /* O - Process ID */
b14d90ba 2220pipe_command(client_t *con, /* I - Client connection */
2221 int infile, /* I - Standard input for command */
2222 int *outfile, /* O - Standard output for command */
2223 char *command, /* I - Command to run */
2224 char *options) /* I - Options for command */
a74b005d 2225{
2226 int pid; /* Process ID */
2227 char *commptr; /* Command string pointer */
082b40d2 2228 int fd; /* Looping var */
a74b005d 2229 int fds[2]; /* Pipe FDs */
2230 int argc; /* Number of arguments */
df3f73ff 2231 int envc; /* Number of environment variables */
f63a2256 2232 char argbuf[10240], /* Argument buffer */
b14d90ba 2233 *argv[100], /* Argument strings */
2234 *envp[100]; /* Environment variables */
20b85438 2235 char lang[1024], /* LANG env variable */
df3f73ff 2236 content_length[1024], /* CONTENT_LENGTH env variable */
2237 content_type[1024], /* CONTENT_TYPE env variable */
2238 ipp_port[1024], /* Default listen port */
167f2f52 2239 server_port[1024], /* Default server port */
df3f73ff 2240 server_name[1024], /* Default listen hostname */
2241 remote_host[1024], /* REMOTE_HOST env variable */
167f2f52 2242 remote_user[1024], /* REMOTE_USER env variable */
df3f73ff 2243 tmpdir[1024], /* TMPDIR environment variable */
2244 ldpath[1024], /* LD_LIBRARY_PATH environment variable */
cbb56a9b 2245 datadir[1024], /* CUPS_DATADIR environment variable */
245bf65d 2246 root[1024], /* CUPS_SERVERROOT environment variable */
df3f73ff 2247 query_string[10240]; /* QUERY_STRING env variable */
a74b005d 2248
05e63c18 2249
a74b005d 2250 /*
2251 * Copy the command string...
2252 */
2253
b14d90ba 2254 strncpy(argbuf, options, sizeof(argbuf) - 1);
a74b005d 2255 argbuf[sizeof(argbuf) - 1] = '\0';
2256
2257 /*
f63a2256 2258 * Parse the string; arguments can be separated by + and are terminated
2259 * by ?...
a74b005d 2260 */
2261
2262 argv[0] = argbuf;
2263
bfa1abf0 2264 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
f63a2256 2265 if (*commptr == ' ' || *commptr == '+')
a74b005d 2266 {
2267 *commptr++ = '\0';
2268
2269 while (*commptr == ' ')
2270 commptr ++;
2271
2272 if (*commptr != '\0')
2273 {
2274 argv[argc] = commptr;
2275 argc ++;
2276 }
2277
2278 commptr --;
2279 }
2280 else if (*commptr == '%')
2281 {
2282 if (commptr[1] >= '0' && commptr[1] <= '9')
2283 *commptr = (commptr[1] - '0') << 4;
2284 else
2285 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
2286
2287 if (commptr[2] >= '0' && commptr[2] <= '9')
2288 *commptr |= commptr[2] - '0';
2289 else
2290 *commptr |= tolower(commptr[2]) - 'a' + 10;
2291
2292 strcpy(commptr + 1, commptr + 3);
2293 }
f63a2256 2294 else if (*commptr == '?')
2295 break;
a74b005d 2296
2297 argv[argc] = NULL;
2298
bfa1abf0 2299 if (argv[0][0] == '\0')
2300 argv[0] = strrchr(command, '/') + 1;
b14d90ba 2301
2302 /*
2303 * Setup the environment variables as needed...
2304 */
2305
a6988fb1 2306 snprintf(lang, sizeof(lang), "LANG=%s",
2307 con->language ? con->language->language : "C");
57c77867 2308 sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.sin_port));
b14d90ba 2309 sprintf(server_port, "SERVER_PORT=%d", ntohs(con->http.hostaddr.sin_port));
04de52f8 2310 snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName);
2311 snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname);
2312 snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
2313 snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir);
2314 snprintf(datadir, sizeof(datadir), "CUPS_DATADIR=%s", DataDir);
2315 snprintf(root, sizeof(root), "CUPS_SERVERROOT=%s", ServerRoot);
df3f73ff 2316
2317 if (getenv("LD_LIBRARY_PATH") != NULL)
04de52f8 2318 snprintf(ldpath, sizeof(ldpath), "LD_LIBRARY_PATH=%s", getenv("LD_LIBRARY_PATH"));
df3f73ff 2319 else
2320 ldpath[0] = '\0';
7f0679f5 2321
2322 envp[0] = "PATH=/bin:/usr/bin";
bd84e0d1 2323 envp[1] = "SERVER_SOFTWARE=CUPS/1.1";
7f0679f5 2324 envp[2] = "GATEWAY_INTERFACE=CGI/1.1";
2325 envp[3] = "SERVER_PROTOCOL=HTTP/1.1";
57c77867 2326 envp[4] = ipp_port;
2327 envp[5] = server_name;
2328 envp[6] = server_port;
2329 envp[7] = remote_host;
2330 envp[8] = remote_user;
2331 envp[9] = lang;
05e63c18 2332 envp[10] = TZ;
57c77867 2333 envp[11] = tmpdir;
df3f73ff 2334 envp[12] = datadir;
245bf65d 2335 envp[13] = root;
df3f73ff 2336
245bf65d 2337 envc = 14;
df3f73ff 2338
2339 if (ldpath[0])
2340 envp[envc ++] = ldpath;
b14d90ba 2341
2342 if (con->operation == HTTP_GET)
2343 {
df3f73ff 2344 envp[envc ++] = "REQUEST_METHOD=GET";
f63a2256 2345
2346 if (*commptr)
2347 {
2348 /*
2349 * Add GET form variables after ?...
2350 */
2351
2352 *commptr++ = '\0';
2353
04de52f8 2354 snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
df3f73ff 2355 envp[envc ++] = query_string;
f63a2256 2356 }
b14d90ba 2357 }
2358 else
2359 {
f63a2256 2360 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
04de52f8 2361 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
970017a4 2362 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
b14d90ba 2363
df3f73ff 2364 envp[envc ++] = "REQUEST_METHOD=POST";
2365 envp[envc ++] = content_length;
2366 envp[envc ++] = content_type;
b14d90ba 2367 }
2368
30d60919 2369 /*
2370 * Tell the CGI if we are using encryption...
2371 */
2372
2373 if (con->http.encryption >= HTTP_ENCRYPT_REQUIRED)
2374 envp[envc ++] = "HTTPS=ON";
2375
df3f73ff 2376 envp[envc] = NULL;
2377
a74b005d 2378 /*
2379 * Create a pipe for the output...
2380 */
2381
2382 if (pipe(fds))
1d6c68fb 2383 {
5ea8888e 2384 LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
1d6c68fb 2385 argv[0], strerror(errno));
a74b005d 2386 return (0);
1d6c68fb 2387 }
a74b005d 2388
2389 /*
1d6c68fb 2390 * Then execute the command...
a74b005d 2391 */
2392
2393 if ((pid = fork()) == 0)
2394 {
2395 /*
2396 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
2397 */
2398
5aeb433c 2399 if (getuid() == 0)
2400 {
2401 /*
0e0ac99a 2402 * Running as root, so change to a non-priviledged user...
5aeb433c 2403 */
2404
2405 if (setgid(Group))
2406 exit(errno);
2407
2408 if (setuid(User))
2409 exit(errno);
2410 }
2411
2412 /*
2413 * Reset group membership to just the main one we belong to.
2414 */
2415
2416 setgroups(0, NULL);
2417
2418 /*
2419 * Update stdin/stdout...
2420 */
b14d90ba 2421
a74b005d 2422 if (infile)
2423 {
2424 close(0);
0e0ac99a 2425 if (dup(infile) < 0)
2426 exit(errno);
a74b005d 2427 }
2428
2429 close(1);
0e0ac99a 2430 if (dup(fds[1]) < 0)
2431 exit(errno);
a74b005d 2432
082b40d2 2433 /*
2434 * Close extra file descriptors...
2435 */
2436
93336dbc 2437 for (fd = 3; fd < MaxFDs; fd ++)
082b40d2 2438 close(fd);
2439
2440 /*
2441 * Change umask to restrict permissions on created files...
2442 */
2443
2444 umask(077);
a74b005d 2445
2446 /*
2447 * Execute the pipe program; if an error occurs, exit with status 1...
2448 */
2449
b14d90ba 2450 execve(command, argv, envp);
96df88bb 2451 exit(errno);
a74b005d 2452 return (0);
2453 }
2454 else if (pid < 0)
2455 {
2456 /*
2457 * Error - can't fork!
2458 */
2459
5ea8888e 2460 LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
1d6c68fb 2461 strerror(errno));
2462
a74b005d 2463 close(fds[0]);
2464 close(fds[1]);
2465 return (0);
2466 }
2467 else
2468 {
2469 /*
2470 * Fork successful - return the PID...
2471 */
2472
f63a2256 2473 AddCert(pid, con->username);
2474
93de21d5 2475 LogMessage(L_DEBUG, "CGI %s started - PID = %d", command, pid);
1d6c68fb 2476
a74b005d 2477 *outfile = fds[0];
2478 close(fds[1]);
2479
2480 return (pid);
2481 }
2482}
2483
2484
93894a43 2485/*
efb2f309 2486 * End of "$Id: client.c,v 1.107 2002/01/02 17:59:14 mike Exp $".
a74b005d 2487 */