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