]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/client.c
Added lpinfo and lpmove commands.
[thirdparty/cups.git] / scheduler / client.c
CommitLineData
a74b005d 1/*
83e740a5 2 * "$Id: client.c,v 1.55 2000/05/01 19:50:26 mike Exp $"
a74b005d 3 *
4 * Client routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
71fe22b7 6 * Copyright 1997-2000 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.
29 * ReadClient() - Read data from a client.
30 * SendCommand() - Send output from a command via HTTP.
31 * SendError() - Send an error message via HTTP.
32 * SendFile() - Send a file via HTTP.
33 * SendHeader() - Send an HTTP request.
93894a43 34 * WriteClient() - Write data to a client as needed.
35 * check_if_modified() - Decode an "If-Modified-Since" line.
d2122fde 36 * decode_auth() - Decode an authorization string.
93894a43 37 * get_file() - Get a filename and state info.
38 * pipe_command() - Pipe the output of a command to the remote client.
a74b005d 39 */
40
41/*
42 * Include necessary headers...
43 */
44
45#include "cupsd.h"
46
47
48/*
49 * Local functions...
50 */
51
a74b005d 52static int check_if_modified(client_t *con, struct stat *filestats);
d2122fde 53static void decode_auth(client_t *con);
a74b005d 54static char *get_file(client_t *con, struct stat *filestats);
b14d90ba 55static int pipe_command(client_t *con, int infile, int *outfile, char *command, char *options);
a74b005d 56
57
58/*
59 * 'AcceptClient()' - Accept a new client.
60 */
61
62void
63AcceptClient(listener_t *lis) /* I - Listener socket */
64{
65 int i; /* Looping var */
66 int val; /* Parameter value */
67 client_t *con; /* New client pointer */
68 unsigned address;/* Address of client */
69 struct hostent *host; /* Host entry for address */
70
71
bfa1abf0 72 DEBUG_printf(("AcceptClient(%08x) %d NumClients = %d\n",
73 lis, lis->fd, NumClients));
74
a74b005d 75 /*
76 * Get a pointer to the next available client...
77 */
78
79 con = Clients + NumClients;
80
81 memset(con, 0, sizeof(client_t));
82 con->http.activity = time(NULL);
83
84 /*
85 * Accept the client and get the remote address...
86 */
87
88 val = sizeof(struct sockaddr_in);
89
2f6d083c 90 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr),
91 &val)) < 0)
a74b005d 92 {
5ea8888e 93 LogMessage(L_ERROR, "accept() failed - %s.", strerror(errno));
a74b005d 94 return;
95 }
96
2f6d083c 97 con->http.hostaddr.sin_port = lis->address.sin_port;
98
a74b005d 99 /*
100 * Get the hostname or format the IP address as needed...
101 */
102
103 address = ntohl(con->http.hostaddr.sin_addr.s_addr);
104
105 if (HostNameLookups)
590a0a40 106#ifndef __sgi
e58c04b5 107 host = gethostbyaddr((char *)&(con->http.hostaddr.sin_addr),
108 sizeof(struct in_addr), AF_INET);
42d48bd2 109#else
e58c04b5 110 host = gethostbyaddr(&(con->http.hostaddr.sin_addr),
111 sizeof(struct in_addr), AF_INET);
590a0a40 112#endif /* !__sgi */
a74b005d 113 else
114 host = NULL;
115
116 if (host == NULL)
117 sprintf(con->http.hostname, "%d.%d.%d.%d", (address >> 24) & 255,
118 (address >> 16) & 255, (address >> 8) & 255, address & 255);
119 else
120 strncpy(con->http.hostname, host->h_name, sizeof(con->http.hostname) - 1);
121
5ea8888e 122 LogMessage(L_DEBUG, "accept() %d from %s:%d.", con->http.fd,
2f6d083c 123 con->http.hostname, ntohs(con->http.hostaddr.sin_port));
a74b005d 124
125 /*
126 * Add the socket to the select() input mask.
127 */
128
129 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
130
bfa1abf0 131 DEBUG_printf(("AcceptClient: Adding fd %d to InputSet...\n", con->http.fd));
a74b005d 132 FD_SET(con->http.fd, &InputSet);
133
134 NumClients ++;
135
136 /*
137 * Temporarily suspend accept()'s until we lose a client...
138 */
139
997edb40 140 if (NumClients == MaxClients)
a74b005d 141 for (i = 0; i < NumListeners; i ++)
bfa1abf0 142 {
143 DEBUG_printf(("AcceptClient: Removing fd %d from InputSet...\n", Listeners[i].fd));
a74b005d 144 FD_CLR(Listeners[i].fd, &InputSet);
bfa1abf0 145 }
a74b005d 146}
147
148
149/*
150 * 'CloseAllClients()' - Close all remote clients immediately.
151 */
152
153void
154CloseAllClients(void)
155{
156 while (NumClients > 0)
157 CloseClient(Clients);
158}
159
160
161/*
162 * 'CloseClient()' - Close a remote client.
163 */
164
165void
166CloseClient(client_t *con) /* I - Client to close */
167{
168 int i; /* Looping var */
169 int status; /* Exit status of pipe command */
170
171
5ea8888e 172 LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd);
a74b005d 173
174 /*
175 * Close the socket and clear the file from the input set for select()...
176 */
177
882031b3 178 if (con->http.fd > 0)
179 {
180 DEBUG_printf(("CloseClient: Removing fd %d from InputSet...\n", con->http.fd));
181 close(con->http.fd);
182 FD_CLR(con->http.fd, &InputSet);
183 FD_CLR(con->http.fd, &OutputSet);
184 con->http.fd = 0;
185 }
a74b005d 186
187 for (i = 0; i < NumListeners; i ++)
bfa1abf0 188 {
189 DEBUG_printf(("CloseClient: Adding fd %d to InputSet...\n", Listeners[i].fd));
a74b005d 190 FD_SET(Listeners[i].fd, &InputSet);
bfa1abf0 191 }
a74b005d 192
a74b005d 193 if (con->pipe_pid != 0)
bfa1abf0 194 {
195 DEBUG_printf(("CloseClient: Removing fd %d from InputSet...\n", con->file));
a74b005d 196 FD_CLR(con->file, &InputSet);
bfa1abf0 197 }
198
a74b005d 199 /*
200 * If we have a data file open, close it...
201 */
202
bfa1abf0 203 if (con->file)
a74b005d 204 {
205 if (con->pipe_pid)
206 {
207 kill(con->pipe_pid, SIGKILL);
208 waitpid(con->pipe_pid, &status, WNOHANG);
209 }
210
882031b3 211 FD_CLR(con->file, &InputSet);
a74b005d 212 close(con->file);
bfa1abf0 213 con->file = 0;
a74b005d 214 }
215
216 /*
217 * Compact the list of clients as necessary...
218 */
219
220 NumClients --;
221
222 if (con < (Clients + NumClients))
223 memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
224}
225
226
227/*
228 * 'ReadClient()' - Read data from a client.
229 */
230
231int /* O - 1 on success, 0 on error */
232ReadClient(client_t *con) /* I - Client to read from */
233{
234 char line[8192], /* Line from client... */
235 operation[64], /* Operation code from socket */
236 version[64]; /* HTTP version number string */
237 int major, minor; /* HTTP version numbers */
238 http_status_t status; /* Transfer status */
bd176c9c 239 ipp_state_t ipp_state; /* State of IPP transfer */
a74b005d 240 int bytes; /* Number of bytes to POST */
e31bfb6e 241 char *filename; /* Name of file for GET/HEAD */
a74b005d 242 struct stat filestats; /* File information */
e31bfb6e 243 mime_type_t *type; /* MIME type of file */
2a8fc30c 244 printer_t *p; /* Printer */
5d99df62 245 static unsigned request_id = 0;/* Request ID for temp files */
a74b005d 246
bfa1abf0 247
a74b005d 248 status = HTTP_CONTINUE;
249
250 switch (con->http.state)
251 {
252 case HTTP_WAITING :
253 /*
254 * See if we've received a request line...
255 */
256
257 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
93894a43 258 {
259 CloseClient(con);
260 return (0);
261 }
a74b005d 262
263 /*
264 * Ignore blank request lines...
265 */
266
267 if (line[0] == '\0')
268 break;
269
270 /*
271 * Clear other state variables...
272 */
273
274 httpClearFields(HTTP(con));
275
276 con->http.activity = time(NULL);
277 con->http.version = HTTP_1_0;
278 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
279 con->http.data_encoding = HTTP_ENCODE_LENGTH;
280 con->http.data_remaining = 0;
6a0c519d 281 con->operation = HTTP_WAITING;
282 con->bytes = 0;
a74b005d 283 con->file = 0;
284 con->pipe_pid = 0;
f63a2256 285 con->command[0] = '\0';
a74b005d 286 con->username[0] = '\0';
287 con->password[0] = '\0';
288 con->uri[0] = '\0';
289
290 if (con->language != NULL)
291 {
292 cupsLangFree(con->language);
293 con->language = NULL;
294 }
295
296 /*
297 * Grab the request line...
298 */
299
970017a4 300 switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
a74b005d 301 {
302 case 1 :
303 SendError(con, HTTP_BAD_REQUEST);
304 CloseClient(con);
305 return (0);
306 case 2 :
307 con->http.version = HTTP_0_9;
308 break;
309 case 3 :
310 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
311 {
312 SendError(con, HTTP_BAD_REQUEST);
313 CloseClient(con);
314 return (0);
315 }
316
317 if (major < 2)
318 {
319 con->http.version = (http_version_t)(major * 100 + minor);
320 if (con->http.version == HTTP_1_1)
321 con->http.keep_alive = HTTP_KEEPALIVE_ON;
322 else
323 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
324 }
325 else
326 {
327 SendError(con, HTTP_NOT_SUPPORTED);
328 CloseClient(con);
329 return (0);
330 }
331 break;
332 }
333
334 /*
335 * Process the request...
336 */
337
338 if (strcmp(operation, "GET") == 0)
339 con->http.state = HTTP_GET;
340 else if (strcmp(operation, "PUT") == 0)
341 con->http.state = HTTP_PUT;
342 else if (strcmp(operation, "POST") == 0)
343 con->http.state = HTTP_POST;
344 else if (strcmp(operation, "DELETE") == 0)
345 con->http.state = HTTP_DELETE;
346 else if (strcmp(operation, "TRACE") == 0)
347 con->http.state = HTTP_TRACE;
348 else if (strcmp(operation, "CLOSE") == 0)
349 con->http.state = HTTP_CLOSE;
350 else if (strcmp(operation, "OPTIONS") == 0)
351 con->http.state = HTTP_OPTIONS;
352 else if (strcmp(operation, "HEAD") == 0)
353 con->http.state = HTTP_HEAD;
354 else
355 {
356 SendError(con, HTTP_BAD_REQUEST);
357 CloseClient(con);
358 return (0);
359 }
360
6a0c519d 361 con->start = time(NULL);
362 con->operation = con->http.state;
363
5ea8888e 364 LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
4a64fdb7 365 operation, con->uri,
366 con->http.version / 100, con->http.version % 100);
a74b005d 367
368 con->http.status = HTTP_OK;
369 break;
370
371 case HTTP_CLOSE :
372 case HTTP_DELETE :
373 case HTTP_GET :
374 case HTTP_HEAD :
375 case HTTP_POST :
376 case HTTP_PUT :
377 case HTTP_TRACE :
378 /*
379 * Parse incoming parameters until the status changes...
380 */
381
382 status = httpUpdate(HTTP(con));
383
384 if (status != HTTP_OK && status != HTTP_CONTINUE)
385 {
386 SendError(con, HTTP_BAD_REQUEST);
387 CloseClient(con);
388 return (0);
389 }
390 break;
d21a7597 391
392 default :
393 break; /* Anti-compiler-warning-code */
a74b005d 394 }
395
396 /*
397 * Handle new transfers...
398 */
399
400 if (status == HTTP_OK)
401 {
402 con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
403
d2122fde 404 decode_auth(con);
a74b005d 405
406 if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
d558743f 407 con->http.version >= HTTP_1_1)
a74b005d 408 {
d558743f 409 /*
410 * HTTP/1.1 and higher require the "Host:" field...
411 */
412
a74b005d 413 if (!SendError(con, HTTP_BAD_REQUEST))
414 {
415 CloseClient(con);
416 return (0);
417 }
418 }
bd84e0d1 419 else if (strstr(con->uri, "..") != NULL)
a74b005d 420 {
421 /*
422 * Protect against malicious users!
423 */
424
425 if (!SendError(con, HTTP_FORBIDDEN))
426 {
427 CloseClient(con);
428 return (0);
429 }
430 }
431 else if (con->uri[0] != '/')
432 {
433 /*
434 * Don't allow proxying (yet)...
435 */
436
437 if (!SendError(con, HTTP_METHOD_NOT_ALLOWED))
438 {
439 CloseClient(con);
440 return (0);
441 }
442 }
443 else if ((status = IsAuthorized(con)) != HTTP_OK)
444 {
c1918ec5 445 SendError(con, status);
446 CloseClient(con);
447 return (0);
a74b005d 448 }
449 else switch (con->http.state)
450 {
451 case HTTP_GET_SEND :
997edb40 452 if (strncmp(con->uri, "/printers/", 10) == 0 &&
f3d580b9 453 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
454 {
455 /*
2a8fc30c 456 * Send PPD file - get the real printer name since printer
457 * names are not case sensitive but filename can be...
f3d580b9 458 */
459
f969a074 460 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
461
2a8fc30c 462 if ((p = FindPrinter(con->uri + 10)) != NULL)
463 sprintf(con->uri, "/ppd/%s.ppd", p->name);
464 else
465 {
466 if (!SendError(con, HTTP_NOT_FOUND))
467 {
468 CloseClient(con);
469 return (0);
470 }
471
472 break;
473 }
f3d580b9 474 }
475
bd84e0d1 476 if (strncmp(con->uri, "/admin", 6) == 0 ||
477 strncmp(con->uri, "/printers", 9) == 0 ||
b14d90ba 478 strncmp(con->uri, "/classes", 8) == 0 ||
479 strncmp(con->uri, "/jobs", 5) == 0)
a74b005d 480 {
481 /*
b14d90ba 482 * Send CGI output...
a74b005d 483 */
484
bd84e0d1 485 if (strncmp(con->uri, "/admin", 6) == 0)
b14d90ba 486 {
f63a2256 487 snprintf(con->command, sizeof(con->command),
488 "%s/cgi-bin/admin.cgi", ServerBin);
489 con->options = con->uri + 6;
bd84e0d1 490 }
491 else if (strncmp(con->uri, "/printers", 9) == 0)
492 {
f63a2256 493 snprintf(con->command, sizeof(con->command),
494 "%s/cgi-bin/printers.cgi", ServerBin);
495 con->options = con->uri + 9;
b14d90ba 496 }
497 else if (strncmp(con->uri, "/classes", 8) == 0)
498 {
f63a2256 499 snprintf(con->command, sizeof(con->command),
500 "%s/cgi-bin/classes.cgi", ServerBin);
501 con->options = con->uri + 8;
b14d90ba 502 }
503 else
504 {
f63a2256 505 snprintf(con->command, sizeof(con->command),
506 "%s/cgi-bin/jobs.cgi", ServerBin);
507 con->options = con->uri + 5;
b14d90ba 508 }
509
f63a2256 510 if (con->options[0] == '/')
511 con->options ++;
b14d90ba 512
f63a2256 513 if (!SendCommand(con, con->command, con->options))
a74b005d 514 {
515 if (!SendError(con, HTTP_NOT_FOUND))
516 {
517 CloseClient(con);
518 return (0);
519 }
520 }
6a0c519d 521 else
522 LogRequest(con, HTTP_OK);
a74b005d 523
b14d90ba 524 if (con->http.version <= HTTP_1_0)
a74b005d 525 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
526 }
527 else
528 {
529 /*
530 * Serve a file...
531 */
532
533 if ((filename = get_file(con, &filestats)) == NULL)
534 {
535 if (!SendError(con, HTTP_NOT_FOUND))
536 {
537 CloseClient(con);
538 return (0);
539 }
540 }
541 else if (!check_if_modified(con, &filestats))
542 {
543 if (!SendError(con, HTTP_NOT_MODIFIED))
544 {
545 CloseClient(con);
546 return (0);
547 }
548 }
549 else
550 {
e31bfb6e 551 type = mimeFileType(MimeDatabase, filename);
552 if (type == NULL)
553 strcpy(line, "text/plain");
554 else
555 sprintf(line, "%s/%s", type->super, type->type);
a74b005d 556
e31bfb6e 557 if (!SendFile(con, HTTP_OK, filename, line, &filestats))
a74b005d 558 {
559 CloseClient(con);
560 return (0);
561 }
562 }
563 }
564 break;
565
566 case HTTP_POST_RECV :
8b43895a 567 /*
568 * See if the POST request includes a Content-Length field, and if
569 * so check the length against any limits that are set...
570 */
571
f63a2256 572 LogMessage(L_DEBUG, "POST %s", con->uri);
573 LogMessage(L_DEBUG, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
574
8b43895a 575 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
576 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
577 MaxRequestSize > 0)
578 {
579 /*
580 * Request too large...
581 */
582
583 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
584 {
585 CloseClient(con);
586 return (0);
587 }
588
589 break;
590 }
591
b14d90ba 592 /*
593 * See what kind of POST request this is; for IPP requests the
594 * content-type field will be "application/ipp"...
595 */
596
93894a43 597 if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
b14d90ba 598 con->request = ippNew();
f63a2256 599 else if (strncmp(con->uri, "/admin", 6) == 0 ||
600 strncmp(con->uri, "/printers", 9) == 0 ||
601 strncmp(con->uri, "/classes", 8) == 0 ||
602 strncmp(con->uri, "/jobs", 5) == 0)
a74b005d 603 {
b14d90ba 604 /*
605 * CGI request...
606 */
607
f63a2256 608 if (strncmp(con->uri, "/admin", 6) == 0)
bd84e0d1 609 {
f63a2256 610 snprintf(con->command, sizeof(con->command),
611 "%s/cgi-bin/admin.cgi", ServerBin);
612 con->options = con->uri + 6;
bd84e0d1 613 }
614 else if (strncmp(con->uri, "/printers", 9) == 0)
a74b005d 615 {
f63a2256 616 snprintf(con->command, sizeof(con->command),
617 "%s/cgi-bin/printers.cgi", ServerBin);
618 con->options = con->uri + 9;
b14d90ba 619 }
620 else if (strncmp(con->uri, "/classes", 8) == 0)
621 {
f63a2256 622 snprintf(con->command, sizeof(con->command),
623 "%s/cgi-bin/classes.cgi", ServerBin);
624 con->options = con->uri + 8;
b14d90ba 625 }
626 else
627 {
f63a2256 628 snprintf(con->command, sizeof(con->command),
629 "%s/cgi-bin/jobs.cgi", ServerBin);
630 con->options = con->uri + 5;
a74b005d 631 }
b14d90ba 632
f63a2256 633 if (con->options[0] == '/')
634 con->options ++;
b14d90ba 635
f63a2256 636 LogMessage(L_DEBUG, "ReadClient() %d command=\"%s\", options = \"%s\"",
637 con->http.fd, con->command, con->options);
b14d90ba 638
639 if (con->http.version <= HTTP_1_0)
640 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
641 }
642 else if (!SendError(con, HTTP_UNAUTHORIZED))
643 {
644 CloseClient(con);
645 return (0);
a74b005d 646 }
647 break;
648
649 case HTTP_PUT_RECV :
650 case HTTP_DELETE :
651 case HTTP_TRACE :
652 SendError(con, HTTP_NOT_IMPLEMENTED);
653
654 case HTTP_CLOSE :
655 CloseClient(con);
656 return (0);
657
658 case HTTP_HEAD :
f3d580b9 659 if (strncmp(con->uri, "/printers", 9) == 0 &&
660 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
661 {
662 /*
663 * Send PPD file...
664 */
665
f63a2256 666 snprintf(con->command, sizeof(con->command),
667 "/ppd/%s", con->uri + 10);
668 strcpy(con->uri, con->command);
669 con->command[0] = '\0';
f3d580b9 670 }
671
bd84e0d1 672 if (strncmp(con->uri, "/admin/", 7) == 0 ||
673 strncmp(con->uri, "/printers/", 10) == 0 ||
b14d90ba 674 strncmp(con->uri, "/classes/", 9) == 0 ||
675 strncmp(con->uri, "/jobs/", 6) == 0)
a74b005d 676 {
677 /*
b14d90ba 678 * CGI output...
a74b005d 679 */
680
681 if (!SendHeader(con, HTTP_OK, "text/html"))
682 {
683 CloseClient(con);
684 return (0);
685 }
686
687 if (httpPrintf(HTTP(con), "\r\n") < 0)
688 {
689 CloseClient(con);
690 return (0);
691 }
6a0c519d 692
693 LogRequest(con, HTTP_OK);
a74b005d 694 }
695 else if ((filename = get_file(con, &filestats)) == NULL)
696 {
697 if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
698 {
699 CloseClient(con);
700 return (0);
701 }
6a0c519d 702
703 LogRequest(con, HTTP_NOT_FOUND);
a74b005d 704 }
705 else if (!check_if_modified(con, &filestats))
706 {
707 if (!SendError(con, HTTP_NOT_MODIFIED))
708 {
709 CloseClient(con);
710 return (0);
711 }
6a0c519d 712
713 LogRequest(con, HTTP_NOT_MODIFIED);
a74b005d 714 }
715 else
716 {
717 /*
718 * Serve a file...
719 */
720
e31bfb6e 721 type = mimeFileType(MimeDatabase, filename);
722 if (type == NULL)
723 strcpy(line, "text/plain");
724 else
725 sprintf(line, "%s/%s", type->super, type->type);
a74b005d 726
e31bfb6e 727 if (!SendHeader(con, HTTP_OK, line))
a74b005d 728 {
729 CloseClient(con);
730 return (0);
731 }
732
733 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
734 httpGetDateString(filestats.st_mtime)) < 0)
735 {
736 CloseClient(con);
737 return (0);
738 }
739
740 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
741 filestats.st_size) < 0)
742 {
743 CloseClient(con);
744 return (0);
745 }
6a0c519d 746
747 LogRequest(con, HTTP_OK);
a74b005d 748 }
749
97d73ddb 750 if (httpPrintf(HTTP(con), "\r\n") < 0)
a74b005d 751 {
752 CloseClient(con);
753 return (0);
754 }
755
756 con->http.state = HTTP_WAITING;
757 break;
d21a7597 758
759 default :
760 break; /* Anti-compiler-warning-code */
a74b005d 761 }
762 }
763
764 /*
765 * Handle any incoming data...
766 */
767
768 switch (con->http.state)
769 {
770 case HTTP_PUT_RECV :
771 break;
772
773 case HTTP_POST_RECV :
f63a2256 774 LogMessage(L_DEBUG, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
93894a43 775 con->http.fd,
a74b005d 776 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
f63a2256 777 con->http.data_remaining, con->file);
a74b005d 778
b14d90ba 779 if (con->request != NULL)
780 {
781 /*
782 * Grab any request data from the connection...
783 */
784
bd176c9c 785 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
786 {
5ea8888e 787 LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!",
bd176c9c 788 con->http.fd);
789 CloseClient(con);
790 return (0);
791 }
792 else if (ipp_state != IPP_DATA)
b14d90ba 793 break;
f63a2256 794 }
b14d90ba 795
f63a2256 796 if (con->file == 0 && con->http.state != HTTP_POST_SEND)
797 {
798 /*
799 * Create a file as needed for the request data...
800 */
1d2c70a6 801
f63a2256 802 snprintf(con->filename, sizeof(con->filename), "%s/%08x",
803 RequestRoot, request_id ++);
804 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
805 fchmod(con->file, 0640);
806 fchown(con->file, User, Group);
b14d90ba 807
f63a2256 808 LogMessage(L_DEBUG, "ReadClient() %d REQUEST %s", con->http.fd,
809 con->filename);
b14d90ba 810
f63a2256 811 if (con->file < 0)
812 {
813 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
b14d90ba 814 {
f63a2256 815 CloseClient(con);
816 return (0);
b14d90ba 817 }
818 }
f63a2256 819 }
b14d90ba 820
bfa1abf0 821 if (con->http.state != HTTP_POST_SEND)
a74b005d 822 {
bfa1abf0 823 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
a74b005d 824 {
bfa1abf0 825 CloseClient(con);
826 return (0);
827 }
828 else if (bytes > 0)
829 {
830 con->bytes += bytes;
a74b005d 831
5ea8888e 832 LogMessage(L_DEBUG, "ReadClient() %d writing %d bytes",
5d99df62 833 con->http.fd, bytes);
bfa1abf0 834
835 if (write(con->file, line, bytes) < bytes)
a74b005d 836 {
bfa1abf0 837 close(con->file);
838 con->file = 0;
839 unlink(con->filename);
840
841 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
842 {
843 CloseClient(con);
844 return (0);
845 }
a74b005d 846 }
847 }
bd176c9c 848 else if (con->http.state != HTTP_POST_SEND)
849 {
850 CloseClient(con);
851 return (0);
852 }
a74b005d 853 }
93894a43 854
855 if (con->http.state == HTTP_POST_SEND)
856 {
bfa1abf0 857 if (con->file)
858 {
8b43895a 859 fstat(con->file, &filestats);
bfa1abf0 860 close(con->file);
861 con->file = 0;
8b43895a 862
863 if (filestats.st_size > MaxRequestSize &&
864 MaxRequestSize > 0)
865 {
866 /*
867 * Request is too big; remove it and send an error...
868 */
869
870 unlink(con->filename);
871
872 if (con->request)
873 {
874 /*
875 * Delete any IPP request data...
876 */
877
878 ippDelete(con->request);
879 con->request = NULL;
880 }
881
882 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
883 {
884 CloseClient(con);
885 return (0);
886 }
887 }
f63a2256 888
889 if (con->command[0])
890 {
891 if (!SendCommand(con, con->command, con->options))
892 {
893 if (!SendError(con, HTTP_NOT_FOUND))
894 {
895 CloseClient(con);
896 return (0);
897 }
898 }
899 else
900 LogRequest(con, HTTP_OK);
901 }
bfa1abf0 902 }
93894a43 903
904 if (con->request)
e31bfb6e 905 ProcessIPPRequest(con);
93894a43 906 }
a74b005d 907 break;
d21a7597 908
909 default :
910 break; /* Anti-compiler-warning-code */
a74b005d 911 }
912
913 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
914 {
915 CloseClient(con);
916 return (0);
917 }
918 else
919 return (1);
920}
921
922
a74b005d 923/*
924 * 'SendCommand()' - Send output from a command via HTTP.
925 */
926
927int
928SendCommand(client_t *con,
a74b005d 929 char *command,
b14d90ba 930 char *options)
a74b005d 931{
f63a2256 932 int fd;
933
934
935 if (con->filename[0])
936 fd = open(con->filename, O_RDONLY);
937 else
938 fd = open("/dev/null", O_RDONLY);
939
940 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
941
942 close(fd);
a74b005d 943
5ea8888e 944 LogMessage(L_DEBUG, "SendCommand() %d command=\"%s\" file=%d pipe_pid=%d",
a74b005d 945 con->http.fd, command, con->file, con->pipe_pid);
946
947 if (con->pipe_pid == 0)
948 return (0);
949
950 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
951
bfa1abf0 952 DEBUG_printf(("SendCommand: Adding fd %d to InputSet...\n", con->file));
a74b005d 953 FD_SET(con->file, &InputSet);
954 FD_SET(con->http.fd, &OutputSet);
955
96df88bb 956 if (!SendHeader(con, HTTP_OK, NULL))
a74b005d 957 return (0);
958
959 if (con->http.version == HTTP_1_1)
960 {
961 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
962
963 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
964 return (0);
965 }
966
13486cb7 967 con->got_fields = 0;
968 con->field_col = 0;
969
a74b005d 970 return (1);
971}
972
973
974/*
975 * 'SendError()' - Send an error message via HTTP.
976 */
977
978int /* O - 1 if successful, 0 otherwise */
979SendError(client_t *con, /* I - Connection */
980 http_status_t code) /* I - Error code */
981{
83e740a5 982 char message[1024]; /* Message for user */
a74b005d 983
984
6a0c519d 985 /*
986 * Put the request in the access_log file...
987 */
988
993e15da 989 if (con->operation > HTTP_WAITING)
990 LogRequest(con, code);
6a0c519d 991
a74b005d 992 /*
993 * To work around bugs in some proxies, don't use Keep-Alive for some
994 * error messages...
995 */
996
997 if (code >= HTTP_BAD_REQUEST)
998 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
999
1000 /*
1001 * Send an error message back to the client. If the error code is a
1002 * 400 or 500 series, make sure the message contains some text, too!
1003 */
1004
1005 if (!SendHeader(con, code, NULL))
1006 return (0);
1007
a74b005d 1008 if (con->http.version >= HTTP_1_1 && !con->http.keep_alive)
1009 {
1010 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
1011 return (0);
1012 }
1013
1014 if (code >= HTTP_BAD_REQUEST)
1015 {
1016 /*
1017 * Send a human-readable error message.
1018 */
1019
970017a4 1020 snprintf(message, sizeof(message),
1021 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
1022 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
1023 code, httpStatus(code), httpStatus(code),
1024 con->language ? con->language->messages[code] :
1025 httpStatus(code));
a74b005d 1026
1027 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
1028 return (0);
1029 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", strlen(message)) < 0)
1030 return (0);
1031 if (httpPrintf(HTTP(con), "\r\n") < 0)
1032 return (0);
97d73ddb 1033 if (httpPrintf(HTTP(con), "%s", message) < 0)
a74b005d 1034 return (0);
1035 }
1036 else if (httpPrintf(HTTP(con), "\r\n") < 0)
1037 return (0);
1038
1039 con->http.state = HTTP_WAITING;
1040
1041 return (1);
1042}
1043
1044
1045/*
1046 * 'SendFile()' - Send a file via HTTP.
1047 */
1048
1049int
1050SendFile(client_t *con,
1051 http_status_t code,
1052 char *filename,
1053 char *type,
1054 struct stat *filestats)
1055{
1056 con->file = open(filename, O_RDONLY);
1057
5ea8888e 1058 LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
a74b005d 1059
1060 if (con->file < 0)
1061 return (0);
1062
1063 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1064
1065 con->pipe_pid = 0;
1066
1067 if (!SendHeader(con, code, type))
1068 return (0);
1069
1070 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
1071 return (0);
1072 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", filestats->st_size) < 0)
1073 return (0);
1074 if (httpPrintf(HTTP(con), "\r\n") < 0)
1075 return (0);
1076
1077 FD_SET(con->http.fd, &OutputSet);
1078
1079 return (1);
1080}
1081
1082
1083/*
93894a43 1084 * 'SendHeader()' - Send an HTTP request.
a74b005d 1085 */
1086
1087int /* O - 1 on success, 0 on failure */
1088SendHeader(client_t *con, /* I - Client to send to */
1089 http_status_t code, /* I - HTTP status code */
1090 char *type) /* I - MIME type of document */
1091{
83e740a5 1092 location_t *loc; /* Authentication location */
1093
1094
a74b005d 1095 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
1096 con->http.version % 100, code, httpStatus(code)) < 0)
1097 return (0);
1098 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
1099 return (0);
bd84e0d1 1100 if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
a74b005d 1101 return (0);
1102 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
1103 {
1104 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
1105 return (0);
1106 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
1107 return (0);
1108 }
5ea8888e 1109 if (code == HTTP_UNAUTHORIZED)
83e740a5 1110 {
1111 loc = FindBest(con); /* This already succeeded in IsAuthorized */
1112
1113 if (loc->type == AUTH_BASIC)
1114 {
1115 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
1116 return (0);
1117 }
1118 else
1119 {
1120 if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" "
1121 "nonce=\"%s\"\r\n", con->http.hostname) < 0)
1122 return (0);
1123 }
1124 }
a74b005d 1125 if (con->language != NULL)
1126 {
1127 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
1128 con->language->language) < 0)
1129 return (0);
1130
1131 if (type != NULL)
1132 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
1133 cupsLangEncoding(con->language)) < 0)
1134 return (0);
1135 }
1136 else if (type != NULL)
1137 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
1138 return (0);
1139
1140 return (1);
1141}
1142
1143
a74b005d 1144/*
1145 * 'WriteClient()' - Write data to a client as needed.
1146 */
1147
ff49100f 1148int /* O - 1 if success, 0 if fail */
1149WriteClient(client_t *con) /* I - Client connection */
a74b005d 1150{
ff49100f 1151 int bytes; /* Number of bytes written */
13486cb7 1152 char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */
1153 char *bufptr; /* Pointer into buffer */
ff49100f 1154 ipp_state_t ipp_state; /* IPP state value */
a74b005d 1155
1156
1157 if (con->http.state != HTTP_GET_SEND &&
1158 con->http.state != HTTP_POST_SEND)
1159 return (1);
1160
ff49100f 1161 if (con->response != NULL)
bfa1abf0 1162 {
ff49100f 1163 ipp_state = ippWrite(&(con->http), con->response);
1164 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
bfa1abf0 1165 }
13486cb7 1166 else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
a74b005d 1167 {
13486cb7 1168 if (con->pipe_pid && !con->got_fields)
1169 {
1170 /*
1171 * Inspect the data for Content-Type and other fields.
1172 */
1173
1174 buf[bytes] = '\0';
1175
1176 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
1177 if (*bufptr == '\n')
1178 {
1179 /*
1180 * Send line to client...
1181 */
1182
1183 if (bufptr > buf && bufptr[-1] == '\r')
1184 bufptr[-1] = '\0';
1185 *bufptr++ = '\0';
1186
1187 httpPrintf(HTTP(con), "%s\r\n", buf);
f63a2256 1188 LogMessage(L_DEBUG, "WriteClient() %d %s", con->http.fd, buf);
13486cb7 1189
1190 /*
1191 * Update buffer...
1192 */
1193
1194 bytes -= (bufptr - buf);
1195 memcpy(buf, bufptr, bytes + 1);
1196 bufptr = buf - 1;
1197
1198 /*
1199 * See if the line was empty...
1200 */
1201
1202 if (con->field_col == 0)
1203 con->got_fields = 1;
1204 else
1205 con->field_col = 0;
1206 }
1207 else if (*bufptr != '\r')
1208 con->field_col ++;
1209
1210 if (bytes > 0 && !con->got_fields)
1211 {
1212 /*
1213 * Remaining text needs to go out...
1214 */
1215
1216 httpPrintf(HTTP(con), "%s", buf);
1217
1218 con->http.activity = time(NULL);
1219 return (1);
1220 }
1221 else if (bytes == 0)
1222 {
1223 con->http.activity = time(NULL);
1224 return (1);
1225 }
1226 }
1227
a74b005d 1228 if (httpWrite(HTTP(con), buf, bytes) < 0)
1229 {
1230 CloseClient(con);
1231 return (0);
1232 }
6a0c519d 1233
1234 con->bytes += bytes;
a74b005d 1235 }
b14d90ba 1236
1237 if (bytes <= 0)
a74b005d 1238 {
6a0c519d 1239 LogRequest(con, HTTP_OK);
1240
a74b005d 1241 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
1242 {
1243 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
1244 {
1245 CloseClient(con);
1246 return (0);
1247 }
1248 }
1249
96df88bb 1250 con->http.state = HTTP_WAITING;
1251
a74b005d 1252 FD_CLR(con->http.fd, &OutputSet);
a74b005d 1253
bfa1abf0 1254 if (con->file)
a74b005d 1255 {
bfa1abf0 1256 DEBUG_printf(("WriteClient: Removing fd %d from InputSet...\n", con->file));
1257 FD_CLR(con->file, &InputSet);
a74b005d 1258
bfa1abf0 1259 if (con->pipe_pid)
96df88bb 1260 kill(con->pipe_pid, SIGTERM);
bfa1abf0 1261
1262 close(con->file);
96df88bb 1263 con->file = 0;
1264 con->pipe_pid = 0;
bfa1abf0 1265 }
a74b005d 1266
d4c438d4 1267 if (con->filename[0])
1268 unlink(con->filename);
1269
b14d90ba 1270 if (con->request != NULL)
1271 {
1272 ippDelete(con->request);
1273 con->request = NULL;
1274 }
1275
1276 if (con->response != NULL)
1277 {
1278 ippDelete(con->response);
1279 con->response = NULL;
1280 }
96df88bb 1281
1282 if (!con->http.keep_alive)
1283 {
1284 CloseClient(con);
1285 return (0);
1286 }
a74b005d 1287 }
1288
997edb40 1289 if (bytes >= 1024)
5ea8888e 1290 LogMessage(L_DEBUG, "WriteClient() %d %d bytes", con->http.fd, bytes);
a74b005d 1291
1292 con->http.activity = time(NULL);
1293
1294 return (1);
1295}
1296
1297
a74b005d 1298/*
1299 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
1300 */
1301
1302static int /* O - 1 if modified since */
1303check_if_modified(client_t *con, /* I - Client connection */
1304 struct stat *filestats) /* I - File information */
1305{
1306 char *ptr; /* Pointer into field */
1307 time_t date; /* Time/date value */
1308 int size; /* Size/length value */
1309
1310
1311 size = 0;
1312 date = 0;
1313 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
1314
1315 if (*ptr == '\0')
1316 return (1);
1317
5ea8888e 1318 LogMessage(L_DEBUG, "check_if_modified() %d If-Modified-Since=\"%s\"",
4a64fdb7 1319 con->http.fd, ptr);
1320
a74b005d 1321 while (*ptr != '\0')
1322 {
4a64fdb7 1323 while (isspace(*ptr) || *ptr == ';')
a74b005d 1324 ptr ++;
1325
1326 if (strncasecmp(ptr, "length=", 7) == 0)
1327 {
1328 ptr += 7;
1329 size = atoi(ptr);
1330
1331 while (isdigit(*ptr))
1332 ptr ++;
1333 }
4a64fdb7 1334 else if (isalpha(*ptr))
a74b005d 1335 {
1336 date = httpGetDateTime(ptr);
4a64fdb7 1337 while (*ptr != '\0' && *ptr != ';')
a74b005d 1338 ptr ++;
1339 }
1340 }
1341
5ea8888e 1342 LogMessage(L_DEBUG, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
4a64fdb7 1343 con->http.fd, size, filestats->st_size, date, filestats->st_mtime);
1344
a74b005d 1345 return ((size != filestats->st_size && size != 0) ||
4a64fdb7 1346 (date < filestats->st_mtime && date != 0) ||
1347 (size == 0 && date == 0));
a74b005d 1348}
1349
1350
1351/*
d2122fde 1352 * 'decode_auth()' - Decode an authorization string.
a74b005d 1353 */
1354
93894a43 1355static void
d2122fde 1356decode_auth(client_t *con) /* I - Client to decode to */
a74b005d 1357{
d2122fde 1358 char *s, /* Authorization string */
1359 value[1024]; /* Value string */
1360 const char *username; /* Certificate username */
a74b005d 1361
a74b005d 1362
1363 /*
b0d58d07 1364 * Decode the string...
a74b005d 1365 */
1366
f3d580b9 1367 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
f3d580b9 1368
69f14ebb 1369 DEBUG_printf(("decode_auth(%08x): Authorization string = \"%s\"\n",
1370 con, s));
1371
d2122fde 1372 if (strncmp(s, "Basic", 5) == 0)
1373 {
1374 s += 5;
1375 while (isspace(*s))
1376 s ++;
f3d580b9 1377
d2122fde 1378 httpDecode64(value, s);
a74b005d 1379
d2122fde 1380 /*
1381 * Pull the username and password out...
1382 */
b0d58d07 1383
d2122fde 1384 if ((s = strchr(value, ':')) == NULL)
1385 {
5ea8888e 1386 LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
d2122fde 1387 con->http.fd, value);
1388 return;
1389 }
1390
1391 *s++ = '\0';
b0d58d07 1392
d2122fde 1393 strncpy(con->username, value, sizeof(con->username) - 1);
1394 con->username[sizeof(con->username) - 1] = '\0';
b0d58d07 1395
d2122fde 1396 strncpy(con->password, s, sizeof(con->password) - 1);
1397 con->password[sizeof(con->password) - 1] = '\0';
1398 }
1399 else if (strncmp(s, "Local", 5) == 0)
1400 {
1401 s += 5;
1402 while (isspace(*s))
1403 s ++;
b0d58d07 1404
d2122fde 1405 if ((username = FindCert(s)) != NULL)
1406 strcpy(con->username, username);
1407 }
83e740a5 1408 else if (strncmp(s, "Digest", 5) == 0)
1409 {
1410 /*
1411 * Get the username and password from the Digest attributes...
1412 */
1413
1414 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username",
1415 value))
1416 {
1417 strncpy(con->username, value, sizeof(con->username) - 1);
1418 con->username[sizeof(con->username) - 1] = '\0';
1419 }
1420
1421 if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response",
1422 value))
1423 {
1424 strncpy(con->password, value, sizeof(con->password) - 1);
1425 con->password[sizeof(con->password) - 1] = '\0';
1426 }
1427 }
41637133 1428
5ea8888e 1429 LogMessage(L_DEBUG, "decode_auth() %d username=\"%s\"",
41637133 1430 con->http.fd, con->username);
69f14ebb 1431 DEBUG_printf(("decode_auth() %d username=\"%s\" password=\"%s\"\n",
1432 con->http.fd, con->username, con->password));
a74b005d 1433}
1434
1435
e31bfb6e 1436/*
1437 * 'get_file()' - Get a filename and state info.
1438 */
1439
1440static char * /* O - Real filename */
1441get_file(client_t *con, /* I - Client connection */
1442 struct stat *filestats)/* O - File information */
1443{
1444 int status; /* Status of filesystem calls */
1445 char *params; /* Pointer to parameters in URI */
1446 static char filename[1024]; /* Filename buffer */
1447
1448
1449 /*
1450 * Need to add DocumentRoot global...
1451 */
1452
f3d580b9 1453 if (strncmp(con->uri, "/ppd/", 5) == 0)
970017a4 1454 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri);
f3d580b9 1455 else if (con->language != NULL)
970017a4 1456 snprintf(filename, sizeof(filename), "%s/%s%s", DocumentRoot, con->language->language,
e31bfb6e 1457 con->uri);
1458 else
970017a4 1459 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
e31bfb6e 1460
1461 if ((params = strchr(filename, '?')) != NULL)
1462 *params = '\0';
1463
1464 /*
1465 * Grab the status for this language; if there isn't a language-specific file
1466 * then fallback to the default one...
1467 */
1468
1469 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
1470 {
1471 /*
1472 * Drop the language prefix and try the current directory...
1473 */
1474
f3d580b9 1475 if (strncmp(con->uri, "/ppd/", 5) != 0)
1476 {
970017a4 1477 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
e31bfb6e 1478
f3d580b9 1479 status = stat(filename, filestats);
1480 }
e31bfb6e 1481 }
1482
1483 /*
1484 * If we're found a directory, get the index.html file instead...
1485 */
1486
1487 if (!status && S_ISDIR(filestats->st_mode))
1488 {
1489 if (filename[strlen(filename) - 1] == '/')
1490 strcat(filename, "index.html");
1491 else
1492 strcat(filename, "/index.html");
1493
1494 status = stat(filename, filestats);
1495 }
1496
5ea8888e 1497 LogMessage(L_DEBUG, "get_file() %d filename=%s size=%d",
997edb40 1498 con->http.fd, filename, status ? -1 : filestats->st_size);
e31bfb6e 1499
1500 if (status)
1501 return (NULL);
1502 else
1503 return (filename);
1504}
1505
1506
a74b005d 1507/*
1508 * 'pipe_command()' - Pipe the output of a command to the remote client.
1509 */
1510
1511static int /* O - Process ID */
b14d90ba 1512pipe_command(client_t *con, /* I - Client connection */
1513 int infile, /* I - Standard input for command */
1514 int *outfile, /* O - Standard output for command */
1515 char *command, /* I - Command to run */
1516 char *options) /* I - Options for command */
a74b005d 1517{
1518 int pid; /* Process ID */
1519 char *commptr; /* Command string pointer */
1520 int fds[2]; /* Pipe FDs */
1521 int argc; /* Number of arguments */
f63a2256 1522 char argbuf[10240], /* Argument buffer */
b14d90ba 1523 *argv[100], /* Argument strings */
1524 *envp[100]; /* Environment variables */
96df88bb 1525 char hostname[1024]; /* Hostname string */
b14d90ba 1526 static char lang[1024]; /* LANG env variable */
1527 static char content_length[1024]; /* CONTENT_LENGTH env variable */
1528 static char content_type[1024]; /* CONTENT_TYPE env variable */
57c77867 1529 static char ipp_port[1024]; /* Default listen port */
b14d90ba 1530 static char server_port[1024]; /* Default listen port */
96df88bb 1531 static char server_name[1024]; /* Default listen hostname */
b14d90ba 1532 static char remote_host[1024]; /* REMOTE_HOST env variable */
1533 static char remote_user[1024]; /* REMOTE_HOST env variable */
7f0679f5 1534 static char tmpdir[1024]; /* TMPDIR env variable */
f63a2256 1535 static char query_string[10240]; /* QUERY_STRING env variable */
a74b005d 1536
05e63c18 1537
a74b005d 1538 /*
1539 * Copy the command string...
1540 */
1541
b14d90ba 1542 strncpy(argbuf, options, sizeof(argbuf) - 1);
a74b005d 1543 argbuf[sizeof(argbuf) - 1] = '\0';
1544
1545 /*
f63a2256 1546 * Parse the string; arguments can be separated by + and are terminated
1547 * by ?...
a74b005d 1548 */
1549
1550 argv[0] = argbuf;
1551
bfa1abf0 1552 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
f63a2256 1553 if (*commptr == ' ' || *commptr == '+')
a74b005d 1554 {
1555 *commptr++ = '\0';
1556
1557 while (*commptr == ' ')
1558 commptr ++;
1559
1560 if (*commptr != '\0')
1561 {
1562 argv[argc] = commptr;
1563 argc ++;
1564 }
1565
1566 commptr --;
1567 }
1568 else if (*commptr == '%')
1569 {
1570 if (commptr[1] >= '0' && commptr[1] <= '9')
1571 *commptr = (commptr[1] - '0') << 4;
1572 else
1573 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
1574
1575 if (commptr[2] >= '0' && commptr[2] <= '9')
1576 *commptr |= commptr[2] - '0';
1577 else
1578 *commptr |= tolower(commptr[2]) - 'a' + 10;
1579
1580 strcpy(commptr + 1, commptr + 3);
1581 }
f63a2256 1582 else if (*commptr == '?')
1583 break;
a74b005d 1584
1585 argv[argc] = NULL;
1586
bfa1abf0 1587 if (argv[0][0] == '\0')
1588 argv[0] = strrchr(command, '/') + 1;
b14d90ba 1589
1590 /*
1591 * Setup the environment variables as needed...
1592 */
1593
96df88bb 1594 gethostname(hostname, sizeof(hostname) - 1);
1595
b14d90ba 1596 sprintf(lang, "LANG=%s", con->language ? con->language->language : "C");
57c77867 1597 sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.sin_port));
b14d90ba 1598 sprintf(server_port, "SERVER_PORT=%d", ntohs(con->http.hostaddr.sin_port));
96df88bb 1599 sprintf(server_name, "SERVER_NAME=%s", hostname);
b14d90ba 1600 sprintf(remote_host, "REMOTE_HOST=%s", con->http.hostname);
1601 sprintf(remote_user, "REMOTE_USER=%s", con->username);
7f0679f5 1602 sprintf(tmpdir, "TMPDIR=%s", TempDir);
1603
1604 envp[0] = "PATH=/bin:/usr/bin";
bd84e0d1 1605 envp[1] = "SERVER_SOFTWARE=CUPS/1.1";
7f0679f5 1606 envp[2] = "GATEWAY_INTERFACE=CGI/1.1";
1607 envp[3] = "SERVER_PROTOCOL=HTTP/1.1";
57c77867 1608 envp[4] = ipp_port;
1609 envp[5] = server_name;
1610 envp[6] = server_port;
1611 envp[7] = remote_host;
1612 envp[8] = remote_user;
1613 envp[9] = lang;
05e63c18 1614 envp[10] = TZ;
57c77867 1615 envp[11] = tmpdir;
b14d90ba 1616
1617 if (con->operation == HTTP_GET)
1618 {
57c77867 1619 envp[12] = "REQUEST_METHOD=GET";
f63a2256 1620
1621 if (*commptr)
1622 {
1623 /*
1624 * Add GET form variables after ?...
1625 */
1626
1627 *commptr++ = '\0';
1628
1629 snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
1630 envp[13] = query_string;
1631 envp[14] = NULL;
1632 }
1633 else
1634 envp[13] = NULL;
b14d90ba 1635 }
1636 else
1637 {
f63a2256 1638 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
970017a4 1639 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
1640 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
b14d90ba 1641
57c77867 1642 envp[12] = "REQUEST_METHOD=POST";
1643 envp[13] = content_length;
1644 envp[14] = content_type;
1645 envp[15] = NULL;
b14d90ba 1646 }
1647
a74b005d 1648 /*
1649 * Create a pipe for the output...
1650 */
1651
1652 if (pipe(fds))
1d6c68fb 1653 {
5ea8888e 1654 LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
1d6c68fb 1655 argv[0], strerror(errno));
a74b005d 1656 return (0);
1d6c68fb 1657 }
a74b005d 1658
1659 /*
1d6c68fb 1660 * Then execute the command...
a74b005d 1661 */
1662
1663 if ((pid = fork()) == 0)
1664 {
1665 /*
1666 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
1667 */
1668
b14d90ba 1669 setgid(Group);
d8a6bdd8 1670 setuid(User);
b14d90ba 1671
a74b005d 1672 if (infile)
1673 {
1674 close(0);
1675 dup(infile);
1676 }
1677
1678 close(1);
1679 dup(fds[1]);
1680
1681 close(fds[0]);
1682 close(fds[1]);
1683
1684 /*
1685 * Execute the pipe program; if an error occurs, exit with status 1...
1686 */
1687
b14d90ba 1688 execve(command, argv, envp);
96df88bb 1689 perror("execve failed");
1690 exit(errno);
a74b005d 1691 return (0);
1692 }
1693 else if (pid < 0)
1694 {
1695 /*
1696 * Error - can't fork!
1697 */
1698
5ea8888e 1699 LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
1d6c68fb 1700 strerror(errno));
1701
a74b005d 1702 close(fds[0]);
1703 close(fds[1]);
1704 return (0);
1705 }
1706 else
1707 {
1708 /*
1709 * Fork successful - return the PID...
1710 */
1711
f63a2256 1712 AddCert(pid, con->username);
1713
5ea8888e 1714 LogMessage(L_DEBUG, "CGI %s started - PID = %d", argv[0], pid);
1d6c68fb 1715
a74b005d 1716 *outfile = fds[0];
1717 close(fds[1]);
1718
1719 return (pid);
1720 }
1721}
1722
1723
93894a43 1724/*
83e740a5 1725 * End of "$Id: client.c,v 1.55 2000/05/01 19:50:26 mike Exp $".
a74b005d 1726 */