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