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