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