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