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