]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/client.c
Fixed encoding of text-with-language and name-with-language attributes.
[thirdparty/cups.git] / scheduler / client.c
CommitLineData
a74b005d 1/*
e58c04b5 2 * "$Id: client.c,v 1.52 2000/03/09 15:42:30 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;
391 }
392
393 /*
394 * Handle new transfers...
395 */
396
397 if (status == HTTP_OK)
398 {
399 con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
400
d2122fde 401 decode_auth(con);
a74b005d 402
403 if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
404 con->http.version >= HTTP_1_0)
405 {
406 if (!SendError(con, HTTP_BAD_REQUEST))
407 {
408 CloseClient(con);
409 return (0);
410 }
411 }
bd84e0d1 412 else if (strstr(con->uri, "..") != NULL)
a74b005d 413 {
414 /*
415 * Protect against malicious users!
416 */
417
418 if (!SendError(con, HTTP_FORBIDDEN))
419 {
420 CloseClient(con);
421 return (0);
422 }
423 }
424 else if (con->uri[0] != '/')
425 {
426 /*
427 * Don't allow proxying (yet)...
428 */
429
430 if (!SendError(con, HTTP_METHOD_NOT_ALLOWED))
431 {
432 CloseClient(con);
433 return (0);
434 }
435 }
436 else if ((status = IsAuthorized(con)) != HTTP_OK)
437 {
c1918ec5 438 SendError(con, status);
439 CloseClient(con);
440 return (0);
a74b005d 441 }
442 else switch (con->http.state)
443 {
444 case HTTP_GET_SEND :
997edb40 445 if (strncmp(con->uri, "/printers/", 10) == 0 &&
f3d580b9 446 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
447 {
448 /*
2a8fc30c 449 * Send PPD file - get the real printer name since printer
450 * names are not case sensitive but filename can be...
f3d580b9 451 */
452
f969a074 453 con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */
454
2a8fc30c 455 if ((p = FindPrinter(con->uri + 10)) != NULL)
456 sprintf(con->uri, "/ppd/%s.ppd", p->name);
457 else
458 {
459 if (!SendError(con, HTTP_NOT_FOUND))
460 {
461 CloseClient(con);
462 return (0);
463 }
464
465 break;
466 }
f3d580b9 467 }
468
bd84e0d1 469 if (strncmp(con->uri, "/admin", 6) == 0 ||
470 strncmp(con->uri, "/printers", 9) == 0 ||
b14d90ba 471 strncmp(con->uri, "/classes", 8) == 0 ||
472 strncmp(con->uri, "/jobs", 5) == 0)
a74b005d 473 {
474 /*
b14d90ba 475 * Send CGI output...
a74b005d 476 */
477
bd84e0d1 478 if (strncmp(con->uri, "/admin", 6) == 0)
b14d90ba 479 {
f63a2256 480 snprintf(con->command, sizeof(con->command),
481 "%s/cgi-bin/admin.cgi", ServerBin);
482 con->options = con->uri + 6;
bd84e0d1 483 }
484 else if (strncmp(con->uri, "/printers", 9) == 0)
485 {
f63a2256 486 snprintf(con->command, sizeof(con->command),
487 "%s/cgi-bin/printers.cgi", ServerBin);
488 con->options = con->uri + 9;
b14d90ba 489 }
490 else if (strncmp(con->uri, "/classes", 8) == 0)
491 {
f63a2256 492 snprintf(con->command, sizeof(con->command),
493 "%s/cgi-bin/classes.cgi", ServerBin);
494 con->options = con->uri + 8;
b14d90ba 495 }
496 else
497 {
f63a2256 498 snprintf(con->command, sizeof(con->command),
499 "%s/cgi-bin/jobs.cgi", ServerBin);
500 con->options = con->uri + 5;
b14d90ba 501 }
502
f63a2256 503 if (con->options[0] == '/')
504 con->options ++;
b14d90ba 505
f63a2256 506 if (!SendCommand(con, con->command, con->options))
a74b005d 507 {
508 if (!SendError(con, HTTP_NOT_FOUND))
509 {
510 CloseClient(con);
511 return (0);
512 }
513 }
6a0c519d 514 else
515 LogRequest(con, HTTP_OK);
a74b005d 516
b14d90ba 517 if (con->http.version <= HTTP_1_0)
a74b005d 518 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
519 }
520 else
521 {
522 /*
523 * Serve a file...
524 */
525
526 if ((filename = get_file(con, &filestats)) == NULL)
527 {
528 if (!SendError(con, HTTP_NOT_FOUND))
529 {
530 CloseClient(con);
531 return (0);
532 }
533 }
534 else if (!check_if_modified(con, &filestats))
535 {
536 if (!SendError(con, HTTP_NOT_MODIFIED))
537 {
538 CloseClient(con);
539 return (0);
540 }
541 }
542 else
543 {
e31bfb6e 544 type = mimeFileType(MimeDatabase, filename);
545 if (type == NULL)
546 strcpy(line, "text/plain");
547 else
548 sprintf(line, "%s/%s", type->super, type->type);
a74b005d 549
e31bfb6e 550 if (!SendFile(con, HTTP_OK, filename, line, &filestats))
a74b005d 551 {
552 CloseClient(con);
553 return (0);
554 }
555 }
556 }
557 break;
558
559 case HTTP_POST_RECV :
8b43895a 560 /*
561 * See if the POST request includes a Content-Length field, and if
562 * so check the length against any limits that are set...
563 */
564
f63a2256 565 LogMessage(L_DEBUG, "POST %s", con->uri);
566 LogMessage(L_DEBUG, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
567
8b43895a 568 if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
569 atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize &&
570 MaxRequestSize > 0)
571 {
572 /*
573 * Request too large...
574 */
575
576 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
577 {
578 CloseClient(con);
579 return (0);
580 }
581
582 break;
583 }
584
b14d90ba 585 /*
586 * See what kind of POST request this is; for IPP requests the
587 * content-type field will be "application/ipp"...
588 */
589
93894a43 590 if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
b14d90ba 591 con->request = ippNew();
f63a2256 592 else if (strncmp(con->uri, "/admin", 6) == 0 ||
593 strncmp(con->uri, "/printers", 9) == 0 ||
594 strncmp(con->uri, "/classes", 8) == 0 ||
595 strncmp(con->uri, "/jobs", 5) == 0)
a74b005d 596 {
b14d90ba 597 /*
598 * CGI request...
599 */
600
f63a2256 601 if (strncmp(con->uri, "/admin", 6) == 0)
bd84e0d1 602 {
f63a2256 603 snprintf(con->command, sizeof(con->command),
604 "%s/cgi-bin/admin.cgi", ServerBin);
605 con->options = con->uri + 6;
bd84e0d1 606 }
607 else if (strncmp(con->uri, "/printers", 9) == 0)
a74b005d 608 {
f63a2256 609 snprintf(con->command, sizeof(con->command),
610 "%s/cgi-bin/printers.cgi", ServerBin);
611 con->options = con->uri + 9;
b14d90ba 612 }
613 else if (strncmp(con->uri, "/classes", 8) == 0)
614 {
f63a2256 615 snprintf(con->command, sizeof(con->command),
616 "%s/cgi-bin/classes.cgi", ServerBin);
617 con->options = con->uri + 8;
b14d90ba 618 }
619 else
620 {
f63a2256 621 snprintf(con->command, sizeof(con->command),
622 "%s/cgi-bin/jobs.cgi", ServerBin);
623 con->options = con->uri + 5;
a74b005d 624 }
b14d90ba 625
f63a2256 626 if (con->options[0] == '/')
627 con->options ++;
b14d90ba 628
f63a2256 629 LogMessage(L_DEBUG, "ReadClient() %d command=\"%s\", options = \"%s\"",
630 con->http.fd, con->command, con->options);
b14d90ba 631
632 if (con->http.version <= HTTP_1_0)
633 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
634 }
635 else if (!SendError(con, HTTP_UNAUTHORIZED))
636 {
637 CloseClient(con);
638 return (0);
a74b005d 639 }
640 break;
641
642 case HTTP_PUT_RECV :
643 case HTTP_DELETE :
644 case HTTP_TRACE :
645 SendError(con, HTTP_NOT_IMPLEMENTED);
646
647 case HTTP_CLOSE :
648 CloseClient(con);
649 return (0);
650
651 case HTTP_HEAD :
f3d580b9 652 if (strncmp(con->uri, "/printers", 9) == 0 &&
653 strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0)
654 {
655 /*
656 * Send PPD file...
657 */
658
f63a2256 659 snprintf(con->command, sizeof(con->command),
660 "/ppd/%s", con->uri + 10);
661 strcpy(con->uri, con->command);
662 con->command[0] = '\0';
f3d580b9 663 }
664
bd84e0d1 665 if (strncmp(con->uri, "/admin/", 7) == 0 ||
666 strncmp(con->uri, "/printers/", 10) == 0 ||
b14d90ba 667 strncmp(con->uri, "/classes/", 9) == 0 ||
668 strncmp(con->uri, "/jobs/", 6) == 0)
a74b005d 669 {
670 /*
b14d90ba 671 * CGI output...
a74b005d 672 */
673
674 if (!SendHeader(con, HTTP_OK, "text/html"))
675 {
676 CloseClient(con);
677 return (0);
678 }
679
680 if (httpPrintf(HTTP(con), "\r\n") < 0)
681 {
682 CloseClient(con);
683 return (0);
684 }
6a0c519d 685
686 LogRequest(con, HTTP_OK);
a74b005d 687 }
688 else if ((filename = get_file(con, &filestats)) == NULL)
689 {
690 if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
691 {
692 CloseClient(con);
693 return (0);
694 }
6a0c519d 695
696 LogRequest(con, HTTP_NOT_FOUND);
a74b005d 697 }
698 else if (!check_if_modified(con, &filestats))
699 {
700 if (!SendError(con, HTTP_NOT_MODIFIED))
701 {
702 CloseClient(con);
703 return (0);
704 }
6a0c519d 705
706 LogRequest(con, HTTP_NOT_MODIFIED);
a74b005d 707 }
708 else
709 {
710 /*
711 * Serve a file...
712 */
713
e31bfb6e 714 type = mimeFileType(MimeDatabase, filename);
715 if (type == NULL)
716 strcpy(line, "text/plain");
717 else
718 sprintf(line, "%s/%s", type->super, type->type);
a74b005d 719
e31bfb6e 720 if (!SendHeader(con, HTTP_OK, line))
a74b005d 721 {
722 CloseClient(con);
723 return (0);
724 }
725
726 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
727 httpGetDateString(filestats.st_mtime)) < 0)
728 {
729 CloseClient(con);
730 return (0);
731 }
732
733 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
734 filestats.st_size) < 0)
735 {
736 CloseClient(con);
737 return (0);
738 }
6a0c519d 739
740 LogRequest(con, HTTP_OK);
a74b005d 741 }
742
97d73ddb 743 if (httpPrintf(HTTP(con), "\r\n") < 0)
a74b005d 744 {
745 CloseClient(con);
746 return (0);
747 }
748
749 con->http.state = HTTP_WAITING;
750 break;
751 }
752 }
753
754 /*
755 * Handle any incoming data...
756 */
757
758 switch (con->http.state)
759 {
760 case HTTP_PUT_RECV :
761 break;
762
763 case HTTP_POST_RECV :
f63a2256 764 LogMessage(L_DEBUG, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
93894a43 765 con->http.fd,
a74b005d 766 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
f63a2256 767 con->http.data_remaining, con->file);
a74b005d 768
b14d90ba 769 if (con->request != NULL)
770 {
771 /*
772 * Grab any request data from the connection...
773 */
774
bd176c9c 775 if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
776 {
5ea8888e 777 LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!",
bd176c9c 778 con->http.fd);
779 CloseClient(con);
780 return (0);
781 }
782 else if (ipp_state != IPP_DATA)
b14d90ba 783 break;
f63a2256 784 }
b14d90ba 785
f63a2256 786 if (con->file == 0 && con->http.state != HTTP_POST_SEND)
787 {
788 /*
789 * Create a file as needed for the request data...
790 */
1d2c70a6 791
f63a2256 792 snprintf(con->filename, sizeof(con->filename), "%s/%08x",
793 RequestRoot, request_id ++);
794 con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
795 fchmod(con->file, 0640);
796 fchown(con->file, User, Group);
b14d90ba 797
f63a2256 798 LogMessage(L_DEBUG, "ReadClient() %d REQUEST %s", con->http.fd,
799 con->filename);
b14d90ba 800
f63a2256 801 if (con->file < 0)
802 {
803 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
b14d90ba 804 {
f63a2256 805 CloseClient(con);
806 return (0);
b14d90ba 807 }
808 }
f63a2256 809 }
b14d90ba 810
bfa1abf0 811 if (con->http.state != HTTP_POST_SEND)
a74b005d 812 {
bfa1abf0 813 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
a74b005d 814 {
bfa1abf0 815 CloseClient(con);
816 return (0);
817 }
818 else if (bytes > 0)
819 {
820 con->bytes += bytes;
a74b005d 821
5ea8888e 822 LogMessage(L_DEBUG, "ReadClient() %d writing %d bytes",
5d99df62 823 con->http.fd, bytes);
bfa1abf0 824
825 if (write(con->file, line, bytes) < bytes)
a74b005d 826 {
bfa1abf0 827 close(con->file);
828 con->file = 0;
829 unlink(con->filename);
830
831 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
832 {
833 CloseClient(con);
834 return (0);
835 }
a74b005d 836 }
837 }
bd176c9c 838 else if (con->http.state != HTTP_POST_SEND)
839 {
840 CloseClient(con);
841 return (0);
842 }
a74b005d 843 }
93894a43 844
845 if (con->http.state == HTTP_POST_SEND)
846 {
bfa1abf0 847 if (con->file)
848 {
8b43895a 849 fstat(con->file, &filestats);
bfa1abf0 850 close(con->file);
851 con->file = 0;
8b43895a 852
853 if (filestats.st_size > MaxRequestSize &&
854 MaxRequestSize > 0)
855 {
856 /*
857 * Request is too big; remove it and send an error...
858 */
859
860 unlink(con->filename);
861
862 if (con->request)
863 {
864 /*
865 * Delete any IPP request data...
866 */
867
868 ippDelete(con->request);
869 con->request = NULL;
870 }
871
872 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
873 {
874 CloseClient(con);
875 return (0);
876 }
877 }
f63a2256 878
879 if (con->command[0])
880 {
881 if (!SendCommand(con, con->command, con->options))
882 {
883 if (!SendError(con, HTTP_NOT_FOUND))
884 {
885 CloseClient(con);
886 return (0);
887 }
888 }
889 else
890 LogRequest(con, HTTP_OK);
891 }
bfa1abf0 892 }
93894a43 893
894 if (con->request)
e31bfb6e 895 ProcessIPPRequest(con);
93894a43 896 }
a74b005d 897 break;
898 }
899
900 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
901 {
902 CloseClient(con);
903 return (0);
904 }
905 else
906 return (1);
907}
908
909
a74b005d 910/*
911 * 'SendCommand()' - Send output from a command via HTTP.
912 */
913
914int
915SendCommand(client_t *con,
a74b005d 916 char *command,
b14d90ba 917 char *options)
a74b005d 918{
f63a2256 919 int fd;
920
921
922 if (con->filename[0])
923 fd = open(con->filename, O_RDONLY);
924 else
925 fd = open("/dev/null", O_RDONLY);
926
927 con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
928
929 close(fd);
a74b005d 930
5ea8888e 931 LogMessage(L_DEBUG, "SendCommand() %d command=\"%s\" file=%d pipe_pid=%d",
a74b005d 932 con->http.fd, command, con->file, con->pipe_pid);
933
934 if (con->pipe_pid == 0)
935 return (0);
936
937 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
938
bfa1abf0 939 DEBUG_printf(("SendCommand: Adding fd %d to InputSet...\n", con->file));
a74b005d 940 FD_SET(con->file, &InputSet);
941 FD_SET(con->http.fd, &OutputSet);
942
96df88bb 943 if (!SendHeader(con, HTTP_OK, NULL))
a74b005d 944 return (0);
945
946 if (con->http.version == HTTP_1_1)
947 {
948 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
949
950 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
951 return (0);
952 }
953
13486cb7 954 con->got_fields = 0;
955 con->field_col = 0;
956
a74b005d 957 return (1);
958}
959
960
961/*
962 * 'SendError()' - Send an error message via HTTP.
963 */
964
965int /* O - 1 if successful, 0 otherwise */
966SendError(client_t *con, /* I - Connection */
967 http_status_t code) /* I - Error code */
968{
97d73ddb 969 char message[1024]; /* Message for user */
a74b005d 970
971
6a0c519d 972 /*
973 * Put the request in the access_log file...
974 */
975
993e15da 976 if (con->operation > HTTP_WAITING)
977 LogRequest(con, code);
6a0c519d 978
a74b005d 979 /*
980 * To work around bugs in some proxies, don't use Keep-Alive for some
981 * error messages...
982 */
983
984 if (code >= HTTP_BAD_REQUEST)
985 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
986
987 /*
988 * Send an error message back to the client. If the error code is a
989 * 400 or 500 series, make sure the message contains some text, too!
990 */
991
992 if (!SendHeader(con, code, NULL))
993 return (0);
994
995 if (code == HTTP_UNAUTHORIZED)
996 {
997 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
998 return (0);
999 }
1000
1001 if (con->http.version >= HTTP_1_1 && !con->http.keep_alive)
1002 {
1003 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
1004 return (0);
1005 }
1006
1007 if (code >= HTTP_BAD_REQUEST)
1008 {
1009 /*
1010 * Send a human-readable error message.
1011 */
1012
970017a4 1013 snprintf(message, sizeof(message),
1014 "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
1015 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
1016 code, httpStatus(code), httpStatus(code),
1017 con->language ? con->language->messages[code] :
1018 httpStatus(code));
a74b005d 1019
1020 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
1021 return (0);
1022 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", strlen(message)) < 0)
1023 return (0);
1024 if (httpPrintf(HTTP(con), "\r\n") < 0)
1025 return (0);
97d73ddb 1026 if (httpPrintf(HTTP(con), "%s", message) < 0)
a74b005d 1027 return (0);
1028 }
1029 else if (httpPrintf(HTTP(con), "\r\n") < 0)
1030 return (0);
1031
1032 con->http.state = HTTP_WAITING;
1033
1034 return (1);
1035}
1036
1037
1038/*
1039 * 'SendFile()' - Send a file via HTTP.
1040 */
1041
1042int
1043SendFile(client_t *con,
1044 http_status_t code,
1045 char *filename,
1046 char *type,
1047 struct stat *filestats)
1048{
1049 con->file = open(filename, O_RDONLY);
1050
5ea8888e 1051 LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
a74b005d 1052
1053 if (con->file < 0)
1054 return (0);
1055
1056 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
1057
1058 con->pipe_pid = 0;
1059
1060 if (!SendHeader(con, code, type))
1061 return (0);
1062
1063 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
1064 return (0);
1065 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", filestats->st_size) < 0)
1066 return (0);
1067 if (httpPrintf(HTTP(con), "\r\n") < 0)
1068 return (0);
1069
1070 FD_SET(con->http.fd, &OutputSet);
1071
1072 return (1);
1073}
1074
1075
1076/*
93894a43 1077 * 'SendHeader()' - Send an HTTP request.
a74b005d 1078 */
1079
1080int /* O - 1 on success, 0 on failure */
1081SendHeader(client_t *con, /* I - Client to send to */
1082 http_status_t code, /* I - HTTP status code */
1083 char *type) /* I - MIME type of document */
1084{
1085 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
1086 con->http.version % 100, code, httpStatus(code)) < 0)
1087 return (0);
1088 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
1089 return (0);
bd84e0d1 1090 if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
a74b005d 1091 return (0);
1092 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
1093 {
1094 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
1095 return (0);
1096 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
1097 return (0);
1098 }
5ea8888e 1099 /**** MRS - Need to store which kind of authentication to perform in the
1100 client structure! ****/
1101 if (code == HTTP_UNAUTHORIZED)
1102 httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n");
a74b005d 1103 if (con->language != NULL)
1104 {
1105 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
1106 con->language->language) < 0)
1107 return (0);
1108
1109 if (type != NULL)
1110 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
1111 cupsLangEncoding(con->language)) < 0)
1112 return (0);
1113 }
1114 else if (type != NULL)
1115 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
1116 return (0);
1117
1118 return (1);
1119}
1120
1121
a74b005d 1122/*
1123 * 'WriteClient()' - Write data to a client as needed.
1124 */
1125
ff49100f 1126int /* O - 1 if success, 0 if fail */
1127WriteClient(client_t *con) /* I - Client connection */
a74b005d 1128{
ff49100f 1129 int bytes; /* Number of bytes written */
13486cb7 1130 char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */
1131 char *bufptr; /* Pointer into buffer */
ff49100f 1132 ipp_state_t ipp_state; /* IPP state value */
a74b005d 1133
1134
1135 if (con->http.state != HTTP_GET_SEND &&
1136 con->http.state != HTTP_POST_SEND)
1137 return (1);
1138
ff49100f 1139 if (con->response != NULL)
bfa1abf0 1140 {
ff49100f 1141 ipp_state = ippWrite(&(con->http), con->response);
1142 bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
bfa1abf0 1143 }
13486cb7 1144 else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
a74b005d 1145 {
13486cb7 1146 if (con->pipe_pid && !con->got_fields)
1147 {
1148 /*
1149 * Inspect the data for Content-Type and other fields.
1150 */
1151
1152 buf[bytes] = '\0';
1153
1154 for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
1155 if (*bufptr == '\n')
1156 {
1157 /*
1158 * Send line to client...
1159 */
1160
1161 if (bufptr > buf && bufptr[-1] == '\r')
1162 bufptr[-1] = '\0';
1163 *bufptr++ = '\0';
1164
1165 httpPrintf(HTTP(con), "%s\r\n", buf);
f63a2256 1166 LogMessage(L_DEBUG, "WriteClient() %d %s", con->http.fd, buf);
13486cb7 1167
1168 /*
1169 * Update buffer...
1170 */
1171
1172 bytes -= (bufptr - buf);
1173 memcpy(buf, bufptr, bytes + 1);
1174 bufptr = buf - 1;
1175
1176 /*
1177 * See if the line was empty...
1178 */
1179
1180 if (con->field_col == 0)
1181 con->got_fields = 1;
1182 else
1183 con->field_col = 0;
1184 }
1185 else if (*bufptr != '\r')
1186 con->field_col ++;
1187
1188 if (bytes > 0 && !con->got_fields)
1189 {
1190 /*
1191 * Remaining text needs to go out...
1192 */
1193
1194 httpPrintf(HTTP(con), "%s", buf);
1195
1196 con->http.activity = time(NULL);
1197 return (1);
1198 }
1199 else if (bytes == 0)
1200 {
1201 con->http.activity = time(NULL);
1202 return (1);
1203 }
1204 }
1205
a74b005d 1206 if (httpWrite(HTTP(con), buf, bytes) < 0)
1207 {
1208 CloseClient(con);
1209 return (0);
1210 }
6a0c519d 1211
1212 con->bytes += bytes;
a74b005d 1213 }
b14d90ba 1214
1215 if (bytes <= 0)
a74b005d 1216 {
6a0c519d 1217 LogRequest(con, HTTP_OK);
1218
a74b005d 1219 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
1220 {
1221 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
1222 {
1223 CloseClient(con);
1224 return (0);
1225 }
1226 }
1227
96df88bb 1228 con->http.state = HTTP_WAITING;
1229
a74b005d 1230 FD_CLR(con->http.fd, &OutputSet);
a74b005d 1231
bfa1abf0 1232 if (con->file)
a74b005d 1233 {
bfa1abf0 1234 DEBUG_printf(("WriteClient: Removing fd %d from InputSet...\n", con->file));
1235 FD_CLR(con->file, &InputSet);
a74b005d 1236
bfa1abf0 1237 if (con->pipe_pid)
96df88bb 1238 kill(con->pipe_pid, SIGTERM);
bfa1abf0 1239
1240 close(con->file);
96df88bb 1241 con->file = 0;
1242 con->pipe_pid = 0;
bfa1abf0 1243 }
a74b005d 1244
d4c438d4 1245 if (con->filename[0])
1246 unlink(con->filename);
1247
b14d90ba 1248 if (con->request != NULL)
1249 {
1250 ippDelete(con->request);
1251 con->request = NULL;
1252 }
1253
1254 if (con->response != NULL)
1255 {
1256 ippDelete(con->response);
1257 con->response = NULL;
1258 }
96df88bb 1259
1260 if (!con->http.keep_alive)
1261 {
1262 CloseClient(con);
1263 return (0);
1264 }
a74b005d 1265 }
1266
997edb40 1267 if (bytes >= 1024)
5ea8888e 1268 LogMessage(L_DEBUG, "WriteClient() %d %d bytes", con->http.fd, bytes);
a74b005d 1269
1270 con->http.activity = time(NULL);
1271
1272 return (1);
1273}
1274
1275
a74b005d 1276/*
1277 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
1278 */
1279
1280static int /* O - 1 if modified since */
1281check_if_modified(client_t *con, /* I - Client connection */
1282 struct stat *filestats) /* I - File information */
1283{
1284 char *ptr; /* Pointer into field */
1285 time_t date; /* Time/date value */
1286 int size; /* Size/length value */
1287
1288
1289 size = 0;
1290 date = 0;
1291 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
1292
1293 if (*ptr == '\0')
1294 return (1);
1295
5ea8888e 1296 LogMessage(L_DEBUG, "check_if_modified() %d If-Modified-Since=\"%s\"",
4a64fdb7 1297 con->http.fd, ptr);
1298
a74b005d 1299 while (*ptr != '\0')
1300 {
4a64fdb7 1301 while (isspace(*ptr) || *ptr == ';')
a74b005d 1302 ptr ++;
1303
1304 if (strncasecmp(ptr, "length=", 7) == 0)
1305 {
1306 ptr += 7;
1307 size = atoi(ptr);
1308
1309 while (isdigit(*ptr))
1310 ptr ++;
1311 }
4a64fdb7 1312 else if (isalpha(*ptr))
a74b005d 1313 {
1314 date = httpGetDateTime(ptr);
4a64fdb7 1315 while (*ptr != '\0' && *ptr != ';')
a74b005d 1316 ptr ++;
1317 }
1318 }
1319
5ea8888e 1320 LogMessage(L_DEBUG, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
4a64fdb7 1321 con->http.fd, size, filestats->st_size, date, filestats->st_mtime);
1322
a74b005d 1323 return ((size != filestats->st_size && size != 0) ||
4a64fdb7 1324 (date < filestats->st_mtime && date != 0) ||
1325 (size == 0 && date == 0));
a74b005d 1326}
1327
1328
1329/*
d2122fde 1330 * 'decode_auth()' - Decode an authorization string.
a74b005d 1331 */
1332
93894a43 1333static void
d2122fde 1334decode_auth(client_t *con) /* I - Client to decode to */
a74b005d 1335{
d2122fde 1336 char *s, /* Authorization string */
1337 value[1024]; /* Value string */
1338 const char *username; /* Certificate username */
a74b005d 1339
a74b005d 1340
1341 /*
b0d58d07 1342 * Decode the string...
a74b005d 1343 */
1344
f3d580b9 1345 s = con->http.fields[HTTP_FIELD_AUTHORIZATION];
f3d580b9 1346
69f14ebb 1347 DEBUG_printf(("decode_auth(%08x): Authorization string = \"%s\"\n",
1348 con, s));
1349
d2122fde 1350 if (strncmp(s, "Basic", 5) == 0)
1351 {
1352 s += 5;
1353 while (isspace(*s))
1354 s ++;
f3d580b9 1355
d2122fde 1356 httpDecode64(value, s);
a74b005d 1357
d2122fde 1358 /*
1359 * Pull the username and password out...
1360 */
b0d58d07 1361
d2122fde 1362 if ((s = strchr(value, ':')) == NULL)
1363 {
5ea8888e 1364 LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
d2122fde 1365 con->http.fd, value);
1366 return;
1367 }
1368
1369 *s++ = '\0';
b0d58d07 1370
d2122fde 1371 strncpy(con->username, value, sizeof(con->username) - 1);
1372 con->username[sizeof(con->username) - 1] = '\0';
b0d58d07 1373
d2122fde 1374 strncpy(con->password, s, sizeof(con->password) - 1);
1375 con->password[sizeof(con->password) - 1] = '\0';
1376 }
1377 else if (strncmp(s, "Local", 5) == 0)
1378 {
1379 s += 5;
1380 while (isspace(*s))
1381 s ++;
b0d58d07 1382
d2122fde 1383 if ((username = FindCert(s)) != NULL)
1384 strcpy(con->username, username);
1385 }
41637133 1386
5ea8888e 1387 LogMessage(L_DEBUG, "decode_auth() %d username=\"%s\"",
41637133 1388 con->http.fd, con->username);
69f14ebb 1389 DEBUG_printf(("decode_auth() %d username=\"%s\" password=\"%s\"\n",
1390 con->http.fd, con->username, con->password));
a74b005d 1391}
1392
1393
e31bfb6e 1394/*
1395 * 'get_file()' - Get a filename and state info.
1396 */
1397
1398static char * /* O - Real filename */
1399get_file(client_t *con, /* I - Client connection */
1400 struct stat *filestats)/* O - File information */
1401{
1402 int status; /* Status of filesystem calls */
1403 char *params; /* Pointer to parameters in URI */
1404 static char filename[1024]; /* Filename buffer */
1405
1406
1407 /*
1408 * Need to add DocumentRoot global...
1409 */
1410
f3d580b9 1411 if (strncmp(con->uri, "/ppd/", 5) == 0)
970017a4 1412 snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri);
f3d580b9 1413 else if (con->language != NULL)
970017a4 1414 snprintf(filename, sizeof(filename), "%s/%s%s", DocumentRoot, con->language->language,
e31bfb6e 1415 con->uri);
1416 else
970017a4 1417 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
e31bfb6e 1418
1419 if ((params = strchr(filename, '?')) != NULL)
1420 *params = '\0';
1421
1422 /*
1423 * Grab the status for this language; if there isn't a language-specific file
1424 * then fallback to the default one...
1425 */
1426
1427 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
1428 {
1429 /*
1430 * Drop the language prefix and try the current directory...
1431 */
1432
f3d580b9 1433 if (strncmp(con->uri, "/ppd/", 5) != 0)
1434 {
970017a4 1435 snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri);
e31bfb6e 1436
f3d580b9 1437 status = stat(filename, filestats);
1438 }
e31bfb6e 1439 }
1440
1441 /*
1442 * If we're found a directory, get the index.html file instead...
1443 */
1444
1445 if (!status && S_ISDIR(filestats->st_mode))
1446 {
1447 if (filename[strlen(filename) - 1] == '/')
1448 strcat(filename, "index.html");
1449 else
1450 strcat(filename, "/index.html");
1451
1452 status = stat(filename, filestats);
1453 }
1454
5ea8888e 1455 LogMessage(L_DEBUG, "get_file() %d filename=%s size=%d",
997edb40 1456 con->http.fd, filename, status ? -1 : filestats->st_size);
e31bfb6e 1457
1458 if (status)
1459 return (NULL);
1460 else
1461 return (filename);
1462}
1463
1464
a74b005d 1465/*
1466 * 'pipe_command()' - Pipe the output of a command to the remote client.
1467 */
1468
1469static int /* O - Process ID */
b14d90ba 1470pipe_command(client_t *con, /* I - Client connection */
1471 int infile, /* I - Standard input for command */
1472 int *outfile, /* O - Standard output for command */
1473 char *command, /* I - Command to run */
1474 char *options) /* I - Options for command */
a74b005d 1475{
1476 int pid; /* Process ID */
1477 char *commptr; /* Command string pointer */
1478 int fds[2]; /* Pipe FDs */
1479 int argc; /* Number of arguments */
f63a2256 1480 char argbuf[10240], /* Argument buffer */
b14d90ba 1481 *argv[100], /* Argument strings */
1482 *envp[100]; /* Environment variables */
96df88bb 1483 char hostname[1024]; /* Hostname string */
b14d90ba 1484 static char lang[1024]; /* LANG env variable */
1485 static char content_length[1024]; /* CONTENT_LENGTH env variable */
1486 static char content_type[1024]; /* CONTENT_TYPE env variable */
57c77867 1487 static char ipp_port[1024]; /* Default listen port */
b14d90ba 1488 static char server_port[1024]; /* Default listen port */
96df88bb 1489 static char server_name[1024]; /* Default listen hostname */
b14d90ba 1490 static char remote_host[1024]; /* REMOTE_HOST env variable */
1491 static char remote_user[1024]; /* REMOTE_HOST env variable */
7f0679f5 1492 static char tmpdir[1024]; /* TMPDIR env variable */
f63a2256 1493 static char query_string[10240]; /* QUERY_STRING env variable */
a74b005d 1494
05e63c18 1495
a74b005d 1496 /*
1497 * Copy the command string...
1498 */
1499
b14d90ba 1500 strncpy(argbuf, options, sizeof(argbuf) - 1);
a74b005d 1501 argbuf[sizeof(argbuf) - 1] = '\0';
1502
1503 /*
f63a2256 1504 * Parse the string; arguments can be separated by + and are terminated
1505 * by ?...
a74b005d 1506 */
1507
1508 argv[0] = argbuf;
1509
bfa1abf0 1510 for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
f63a2256 1511 if (*commptr == ' ' || *commptr == '+')
a74b005d 1512 {
1513 *commptr++ = '\0';
1514
1515 while (*commptr == ' ')
1516 commptr ++;
1517
1518 if (*commptr != '\0')
1519 {
1520 argv[argc] = commptr;
1521 argc ++;
1522 }
1523
1524 commptr --;
1525 }
1526 else if (*commptr == '%')
1527 {
1528 if (commptr[1] >= '0' && commptr[1] <= '9')
1529 *commptr = (commptr[1] - '0') << 4;
1530 else
1531 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
1532
1533 if (commptr[2] >= '0' && commptr[2] <= '9')
1534 *commptr |= commptr[2] - '0';
1535 else
1536 *commptr |= tolower(commptr[2]) - 'a' + 10;
1537
1538 strcpy(commptr + 1, commptr + 3);
1539 }
f63a2256 1540 else if (*commptr == '?')
1541 break;
a74b005d 1542
1543 argv[argc] = NULL;
1544
bfa1abf0 1545 if (argv[0][0] == '\0')
1546 argv[0] = strrchr(command, '/') + 1;
b14d90ba 1547
1548 /*
1549 * Setup the environment variables as needed...
1550 */
1551
96df88bb 1552 gethostname(hostname, sizeof(hostname) - 1);
1553
b14d90ba 1554 sprintf(lang, "LANG=%s", con->language ? con->language->language : "C");
57c77867 1555 sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.sin_port));
b14d90ba 1556 sprintf(server_port, "SERVER_PORT=%d", ntohs(con->http.hostaddr.sin_port));
96df88bb 1557 sprintf(server_name, "SERVER_NAME=%s", hostname);
b14d90ba 1558 sprintf(remote_host, "REMOTE_HOST=%s", con->http.hostname);
1559 sprintf(remote_user, "REMOTE_USER=%s", con->username);
7f0679f5 1560 sprintf(tmpdir, "TMPDIR=%s", TempDir);
1561
1562 envp[0] = "PATH=/bin:/usr/bin";
bd84e0d1 1563 envp[1] = "SERVER_SOFTWARE=CUPS/1.1";
7f0679f5 1564 envp[2] = "GATEWAY_INTERFACE=CGI/1.1";
1565 envp[3] = "SERVER_PROTOCOL=HTTP/1.1";
57c77867 1566 envp[4] = ipp_port;
1567 envp[5] = server_name;
1568 envp[6] = server_port;
1569 envp[7] = remote_host;
1570 envp[8] = remote_user;
1571 envp[9] = lang;
05e63c18 1572 envp[10] = TZ;
57c77867 1573 envp[11] = tmpdir;
b14d90ba 1574
1575 if (con->operation == HTTP_GET)
1576 {
57c77867 1577 envp[12] = "REQUEST_METHOD=GET";
f63a2256 1578
1579 if (*commptr)
1580 {
1581 /*
1582 * Add GET form variables after ?...
1583 */
1584
1585 *commptr++ = '\0';
1586
1587 snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
1588 envp[13] = query_string;
1589 envp[14] = NULL;
1590 }
1591 else
1592 envp[13] = NULL;
b14d90ba 1593 }
1594 else
1595 {
f63a2256 1596 sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes);
970017a4 1597 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s",
1598 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
b14d90ba 1599
57c77867 1600 envp[12] = "REQUEST_METHOD=POST";
1601 envp[13] = content_length;
1602 envp[14] = content_type;
1603 envp[15] = NULL;
b14d90ba 1604 }
1605
a74b005d 1606 /*
1607 * Create a pipe for the output...
1608 */
1609
1610 if (pipe(fds))
1d6c68fb 1611 {
5ea8888e 1612 LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
1d6c68fb 1613 argv[0], strerror(errno));
a74b005d 1614 return (0);
1d6c68fb 1615 }
a74b005d 1616
1617 /*
1d6c68fb 1618 * Then execute the command...
a74b005d 1619 */
1620
1621 if ((pid = fork()) == 0)
1622 {
1623 /*
1624 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
1625 */
1626
b14d90ba 1627 setgid(Group);
d8a6bdd8 1628 setuid(User);
b14d90ba 1629
a74b005d 1630 if (infile)
1631 {
1632 close(0);
1633 dup(infile);
1634 }
1635
1636 close(1);
1637 dup(fds[1]);
1638
1639 close(fds[0]);
1640 close(fds[1]);
1641
1642 /*
1643 * Execute the pipe program; if an error occurs, exit with status 1...
1644 */
1645
b14d90ba 1646 execve(command, argv, envp);
96df88bb 1647 perror("execve failed");
1648 exit(errno);
a74b005d 1649 return (0);
1650 }
1651 else if (pid < 0)
1652 {
1653 /*
1654 * Error - can't fork!
1655 */
1656
5ea8888e 1657 LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
1d6c68fb 1658 strerror(errno));
1659
a74b005d 1660 close(fds[0]);
1661 close(fds[1]);
1662 return (0);
1663 }
1664 else
1665 {
1666 /*
1667 * Fork successful - return the PID...
1668 */
1669
f63a2256 1670 AddCert(pid, con->username);
1671
5ea8888e 1672 LogMessage(L_DEBUG, "CGI %s started - PID = %d", argv[0], pid);
1d6c68fb 1673
a74b005d 1674 *outfile = fds[0];
1675 close(fds[1]);
1676
1677 return (pid);
1678 }
1679}
1680
1681
93894a43 1682/*
e58c04b5 1683 * End of "$Id: client.c,v 1.52 2000/03/09 15:42:30 mike Exp $".
a74b005d 1684 */