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