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