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