]>
Commit | Line | Data |
---|---|---|
a74b005d | 1 | /* |
aa7e125a | 2 | * "$Id: client.c,v 1.91.2.11 2002/05/15 01:57:01 mike Exp $" |
a74b005d | 3 | * |
4 | * Client routines for the Common UNIX Printing System (CUPS) scheduler. | |
5 | * | |
839c43aa | 6 | * Copyright 1997-2002 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. | |
a75c006a | 29 | * EncryptClient() - Enable encryption for the client... |
93894a43 | 30 | * ReadClient() - Read data from a client. |
31 | * SendCommand() - Send output from a command via HTTP. | |
32 | * SendError() - Send an error message via HTTP. | |
33 | * SendFile() - Send a file via HTTP. | |
34 | * SendHeader() - Send an HTTP request. | |
93894a43 | 35 | * WriteClient() - Write data to a client as needed. |
36 | * check_if_modified() - Decode an "If-Modified-Since" line. | |
d2122fde | 37 | * decode_auth() - Decode an authorization string. |
93894a43 | 38 | * get_file() - Get a filename and state info. |
753453e4 | 39 | * install_conf_file() - Install a configuration file. |
93894a43 | 40 | * pipe_command() - Pipe the output of a command to the remote client. |
a74b005d | 41 | */ |
42 | ||
43 | /* | |
44 | * Include necessary headers... | |
45 | */ | |
46 | ||
47 | #include "cupsd.h" | |
48 | ||
bd917997 | 49 | #include <grp.h> |
50 | ||
a75c006a | 51 | #ifdef HAVE_LIBSSL |
1c6682dd | 52 | # include <openssl/err.h> |
a75c006a | 53 | # include <openssl/ssl.h> |
54 | # include <openssl/rand.h> | |
55 | #endif /* HAVE_LIBSSL */ | |
56 | ||
a74b005d | 57 | |
58 | /* | |
59 | * Local functions... | |
60 | */ | |
61 | ||
753453e4 | 62 | static int check_if_modified(client_t *con, |
63 | struct stat *filestats); | |
64 | static void decode_auth(client_t *con); | |
65 | static char *get_file(client_t *con, struct stat *filestats); | |
66 | static http_status_t install_conf_file(client_t *con); | |
67 | static int pipe_command(client_t *con, int infile, int *outfile, | |
68 | char *command, char *options); | |
a74b005d | 69 | |
70 | ||
71 | /* | |
72 | * 'AcceptClient()' - Accept a new client. | |
73 | */ | |
74 | ||
75 | void | |
76 | AcceptClient(listener_t *lis) /* I - Listener socket */ | |
77 | { | |
27eba2dd | 78 | int i; /* Looping var */ |
a74b005d | 79 | int val; /* Parameter value */ |
80 | client_t *con; /* New client pointer */ | |
99de6da0 | 81 | const struct hostent *host; /* Host entry for address */ |
18fe941f | 82 | char *hostname;/* Hostname for address */ |
99de6da0 | 83 | http_addr_t temp; /* Temporary address variable */ |
a74b005d | 84 | |
85 | ||
aa7e125a | 86 | LogMessage(L_DEBUG2, "AcceptClient(%p) %d NumClients = %d", |
27eba2dd | 87 | lis, lis->fd, NumClients); |
bfa1abf0 | 88 | |
12aa0c1f | 89 | /* |
90 | * Make sure we don't have a full set of clients already... | |
91 | */ | |
92 | ||
93 | if (NumClients == MaxClients) | |
94 | return; | |
95 | ||
a74b005d | 96 | /* |
97 | * Get a pointer to the next available client... | |
98 | */ | |
99 | ||
100 | con = Clients + NumClients; | |
101 | ||
102 | memset(con, 0, sizeof(client_t)); | |
103 | con->http.activity = time(NULL); | |
104 | ||
105 | /* | |
106 | * Accept the client and get the remote address... | |
107 | */ | |
108 | ||
109 | val = sizeof(struct sockaddr_in); | |
110 | ||
2f6d083c | 111 | if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr), |
112 | &val)) < 0) | |
a74b005d | 113 | { |
27eba2dd | 114 | LogMessage(L_ERROR, "Unable to accept client connection - %s.", |
115 | strerror(errno)); | |
a74b005d | 116 | return; |
117 | } | |
118 | ||
99de6da0 | 119 | #ifdef AF_INET6 |
120 | if (lis->address.addr.sa_family == AF_INET6) | |
121 | con->http.hostaddr.ipv6.sin6_port = lis->address.ipv6.sin6_port; | |
122 | else | |
123 | #endif /* AF_INET6 */ | |
124 | con->http.hostaddr.ipv4.sin_port = lis->address.ipv4.sin_port; | |
a74b005d | 125 | |
126 | if (HostNameLookups) | |
18fe941f | 127 | hostname = httpAddrLookup(&(con->http.hostaddr), con->http.hostname, |
128 | sizeof(con->http.hostname)); | |
a74b005d | 129 | else |
99de6da0 | 130 | { |
18fe941f | 131 | hostname = NULL; |
99de6da0 | 132 | httpAddrString(&(con->http.hostaddr), con->http.hostname, |
133 | sizeof(con->http.hostname)); | |
134 | } | |
a74b005d | 135 | |
99de6da0 | 136 | if (httpAddrEqual(&(con->http.hostaddr), &ServerAddr)) |
2bdd1992 | 137 | { |
138 | /* | |
139 | * Map accesses from the same host to the server name. | |
140 | */ | |
141 | ||
142 | strncpy(con->http.hostname, ServerName, sizeof(con->http.hostname) - 1); | |
143 | } | |
27eba2dd | 144 | |
18fe941f | 145 | if (hostname == NULL && HostNameLookups == 2) |
99de6da0 | 146 | { |
147 | /* | |
148 | * Can't have an unresolved IP address with double-lookups enabled... | |
149 | */ | |
27eba2dd | 150 | |
6cd03542 | 151 | LogMessage(L_DEBUG2, "AcceptClient: Closing connection %d...", |
152 | con->http.fd); | |
153 | ||
27eba2dd | 154 | #ifdef WIN32 |
99de6da0 | 155 | closesocket(con->http.fd); |
27eba2dd | 156 | #else |
99de6da0 | 157 | close(con->http.fd); |
27eba2dd | 158 | #endif /* WIN32 */ |
159 | ||
99de6da0 | 160 | LogMessage(L_WARN, "Name lookup failed - connection from %s closed!", |
161 | con->http.hostname); | |
162 | return; | |
27eba2dd | 163 | } |
a74b005d | 164 | |
27eba2dd | 165 | if (HostNameLookups == 2) |
166 | { | |
167 | /* | |
168 | * Do double lookups as needed... | |
169 | */ | |
170 | ||
753453e4 | 171 | if ((host = httpGetHostByName(con->http.hostname)) != NULL) |
27eba2dd | 172 | { |
173 | /* | |
99de6da0 | 174 | * See if the hostname maps to the same IP address... |
27eba2dd | 175 | */ |
176 | ||
99de6da0 | 177 | if (host->h_addrtype != con->http.hostaddr.addr.sa_family) |
27eba2dd | 178 | { |
179 | /* | |
99de6da0 | 180 | * Not the right type of address... |
27eba2dd | 181 | */ |
182 | ||
46490d9d | 183 | host = NULL; |
27eba2dd | 184 | } |
185 | else | |
186 | { | |
187 | /* | |
188 | * Compare all of the addresses against this one... | |
189 | */ | |
190 | ||
46490d9d | 191 | for (i = 0; host->h_addr_list[i]; i ++) |
99de6da0 | 192 | { |
193 | httpAddrLoad(host, 0, i, &temp); | |
194 | ||
195 | if (httpAddrEqual(&(con->http.hostaddr), &temp)) | |
27eba2dd | 196 | break; |
99de6da0 | 197 | } |
27eba2dd | 198 | |
46490d9d | 199 | if (!host->h_addr_list[i]) |
200 | host = NULL; | |
27eba2dd | 201 | } |
202 | } | |
203 | ||
46490d9d | 204 | if (host == NULL) |
27eba2dd | 205 | { |
206 | /* | |
207 | * Can't have a hostname that doesn't resolve to the same IP address | |
208 | * with double-lookups enabled... | |
209 | */ | |
210 | ||
6cd03542 | 211 | LogMessage(L_DEBUG2, "AcceptClient: Closing connection %d...", |
212 | con->http.fd); | |
213 | ||
27eba2dd | 214 | #ifdef WIN32 |
215 | closesocket(con->http.fd); | |
216 | #else | |
217 | close(con->http.fd); | |
218 | #endif /* WIN32 */ | |
219 | ||
220 | LogMessage(L_WARN, "IP lookup failed - connection from %s closed!", | |
221 | con->http.hostname); | |
222 | return; | |
223 | } | |
224 | } | |
225 | ||
99de6da0 | 226 | #ifdef AF_INET6 |
227 | if (con->http.hostaddr.addr.sa_family == AF_INET6) | |
18fe941f | 228 | LogMessage(L_DEBUG, "AcceptClient: %d from %s:%d.", con->http.fd, |
99de6da0 | 229 | con->http.hostname, ntohs(con->http.hostaddr.ipv6.sin6_port)); |
230 | else | |
231 | #endif /* AF_INET6 */ | |
18fe941f | 232 | LogMessage(L_DEBUG, "AcceptClient: %d from %s:%d.", con->http.fd, |
99de6da0 | 233 | con->http.hostname, ntohs(con->http.hostaddr.ipv4.sin_port)); |
a74b005d | 234 | |
9ae34eb7 | 235 | /* |
236 | * Using TCP_NODELAY improves responsiveness, especially on systems | |
237 | * with a slow loopback interface... Since we write large buffers | |
238 | * when sending print files and requests, there shouldn't be any | |
239 | * performance penalty for this... | |
240 | */ | |
241 | ||
242 | val = 1; | |
243 | setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); | |
244 | ||
a74b005d | 245 | /* |
246 | * Add the socket to the select() input mask. | |
247 | */ | |
248 | ||
249 | fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC); | |
250 | ||
18fe941f | 251 | LogMessage(L_DEBUG2, "AcceptClient: Adding fd %d to InputSet...", |
27eba2dd | 252 | con->http.fd); |
a74b005d | 253 | FD_SET(con->http.fd, &InputSet); |
254 | ||
255 | NumClients ++; | |
256 | ||
257 | /* | |
258 | * Temporarily suspend accept()'s until we lose a client... | |
259 | */ | |
260 | ||
997edb40 | 261 | if (NumClients == MaxClients) |
d236cb49 | 262 | PauseListening(); |
2a0ef17a | 263 | |
264 | #ifdef HAVE_LIBSSL | |
265 | /* | |
0b50ddbc | 266 | * See if we are connecting on a secure port... |
2a0ef17a | 267 | */ |
268 | ||
0b50ddbc | 269 | if (lis->encryption == HTTP_ENCRYPT_ALWAYS) |
2a0ef17a | 270 | { |
271 | /* | |
272 | * https connection; go secure... | |
273 | */ | |
274 | ||
275 | EncryptClient(con); | |
276 | } | |
277 | #endif /* HAVE_LIBSSL */ | |
a74b005d | 278 | } |
279 | ||
280 | ||
281 | /* | |
282 | * 'CloseAllClients()' - Close all remote clients immediately. | |
283 | */ | |
284 | ||
285 | void | |
286 | CloseAllClients(void) | |
287 | { | |
288 | while (NumClients > 0) | |
289 | CloseClient(Clients); | |
290 | } | |
291 | ||
292 | ||
293 | /* | |
294 | * 'CloseClient()' - Close a remote client. | |
295 | */ | |
296 | ||
297 | void | |
298 | CloseClient(client_t *con) /* I - Client to close */ | |
299 | { | |
a74b005d | 300 | int status; /* Exit status of pipe command */ |
2a0ef17a | 301 | #ifdef HAVE_LIBSSL |
302 | SSL_CTX *context; /* Context for encryption */ | |
303 | SSL *conn; /* Connection for encryption */ | |
304 | #endif /* HAVE_LIBSSL */ | |
a74b005d | 305 | |
306 | ||
5ea8888e | 307 | LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd); |
a74b005d | 308 | |
2a0ef17a | 309 | #ifdef HAVE_LIBSSL |
310 | /* | |
311 | * Shutdown encryption as needed... | |
312 | */ | |
313 | ||
314 | if (con->http.tls) | |
315 | { | |
316 | conn = (SSL *)(con->http.tls); | |
317 | context = SSL_get_SSL_CTX(conn); | |
318 | ||
319 | SSL_shutdown(conn); | |
320 | SSL_CTX_free(context); | |
321 | SSL_free(conn); | |
322 | ||
323 | con->http.tls = NULL; | |
324 | } | |
325 | #endif /* HAVE_LIBSSL */ | |
326 | ||
a74b005d | 327 | /* |
328 | * Close the socket and clear the file from the input set for select()... | |
329 | */ | |
330 | ||
6cd03542 | 331 | if (con->http.fd >= 0) |
882031b3 | 332 | { |
e668e627 | 333 | LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...", |
27eba2dd | 334 | con->http.fd); |
882031b3 | 335 | close(con->http.fd); |
336 | FD_CLR(con->http.fd, &InputSet); | |
337 | FD_CLR(con->http.fd, &OutputSet); | |
338 | con->http.fd = 0; | |
339 | } | |
a74b005d | 340 | |
a74b005d | 341 | if (con->pipe_pid != 0) |
bfa1abf0 | 342 | { |
27eba2dd | 343 | LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet...", |
344 | con->file); | |
a74b005d | 345 | FD_CLR(con->file, &InputSet); |
bfa1abf0 | 346 | } |
347 | ||
bfa1abf0 | 348 | if (con->file) |
a74b005d | 349 | { |
26bb6ac6 | 350 | /* |
351 | * Close the open data file... | |
352 | */ | |
353 | ||
a74b005d | 354 | if (con->pipe_pid) |
355 | { | |
356 | kill(con->pipe_pid, SIGKILL); | |
357 | waitpid(con->pipe_pid, &status, WNOHANG); | |
358 | } | |
359 | ||
bd917997 | 360 | LogMessage(L_DEBUG2, "CloseClient() %d Closing data file %d.", |
361 | con->http.fd, con->file); | |
e668e627 | 362 | LogMessage(L_DEBUG2, "CloseClient() %d Removing fd %d from InputSet.", |
363 | con->http.fd, con->file); | |
bd917997 | 364 | |
882031b3 | 365 | FD_CLR(con->file, &InputSet); |
a74b005d | 366 | close(con->file); |
bfa1abf0 | 367 | con->file = 0; |
a74b005d | 368 | } |
369 | ||
25135af7 | 370 | if (con->request) |
371 | { | |
372 | ippDelete(con->request); | |
373 | con->request = NULL; | |
374 | } | |
375 | ||
376 | if (con->response) | |
377 | { | |
378 | ippDelete(con->response); | |
379 | con->response = NULL; | |
380 | } | |
381 | ||
382 | if (con->language) | |
383 | { | |
384 | cupsLangFree(con->language); | |
385 | con->language = NULL; | |
386 | } | |
387 | ||
26bb6ac6 | 388 | /* |
389 | * Re-enable new client connections if we are going back under the | |
390 | * limit... | |
391 | */ | |
392 | ||
393 | if (NumClients == MaxClients) | |
d236cb49 | 394 | ResumeListening(); |
26bb6ac6 | 395 | |
a74b005d | 396 | /* |
397 | * Compact the list of clients as necessary... | |
398 | */ | |
399 | ||
400 | NumClients --; | |
401 | ||
402 | if (con < (Clients + NumClients)) | |
403 | memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t)); | |
404 | } | |
405 | ||
406 | ||
a75c006a | 407 | /* |
408 | * 'EncryptClient()' - Enable encryption for the client... | |
409 | */ | |
410 | ||
411 | int /* O - 1 on success, 0 on error */ | |
412 | EncryptClient(client_t *con) /* I - Client to encrypt */ | |
413 | { | |
414 | #ifdef HAVE_LIBSSL | |
415 | SSL_CTX *context; /* Context for encryption */ | |
416 | SSL *conn; /* Connection for encryption */ | |
753453e4 | 417 | unsigned long error; /* Error code */ |
1c6682dd | 418 | |
419 | ||
420 | /* | |
421 | * Create the SSL context and accept the connection... | |
422 | */ | |
423 | ||
52cf768e | 424 | context = SSL_CTX_new(SSLv23_method()); |
a75c006a | 425 | conn = SSL_new(context); |
426 | ||
427 | SSL_use_PrivateKey_file(conn, ServerKey, SSL_FILETYPE_PEM); | |
428 | SSL_use_certificate_file(conn, ServerCertificate, SSL_FILETYPE_PEM); | |
429 | ||
430 | SSL_set_fd(conn, con->http.fd); | |
431 | if (SSL_accept(conn) != 1) | |
432 | { | |
1c6682dd | 433 | while ((error = ERR_get_error()) != 0) |
434 | LogMessage(L_ERROR, "EncryptClient: %s", ERR_error_string(error, NULL)); | |
435 | ||
a75c006a | 436 | SSL_CTX_free(context); |
437 | SSL_free(conn); | |
438 | return (0); | |
439 | } | |
440 | ||
441 | con->http.tls = conn; | |
442 | return (1); | |
443 | #else | |
444 | return (0); | |
445 | #endif /* HAVE_LIBSSL */ | |
446 | } | |
447 | ||
448 | ||
a74b005d | 449 | /* |
450 | * 'ReadClient()' - Read data from a client. | |
451 | */ | |
452 | ||
453 | int /* O - 1 on success, 0 on error */ | |
454 | ReadClient(client_t *con) /* I - Client to read from */ | |
455 | { | |
753453e4 | 456 | char line[32768], /* Line from client... */ |
a74b005d | 457 | operation[64], /* Operation code from socket */ |
458 | version[64]; /* HTTP version number string */ | |
459 | int major, minor; /* HTTP version numbers */ | |
460 | http_status_t status; /* Transfer status */ | |
bd176c9c | 461 | ipp_state_t ipp_state; /* State of IPP transfer */ |
a74b005d | 462 | int bytes; /* Number of bytes to POST */ |
e31bfb6e | 463 | char *filename; /* Name of file for GET/HEAD */ |
a74b005d | 464 | struct stat filestats; /* File information */ |
e31bfb6e | 465 | mime_type_t *type; /* MIME type of file */ |
2a8fc30c | 466 | printer_t *p; /* Printer */ |
8f85ff77 | 467 | location_t *best; /* Best match for authentication */ |
5d99df62 | 468 | static unsigned request_id = 0;/* Request ID for temp files */ |
a74b005d | 469 | |
bfa1abf0 | 470 | |
a74b005d | 471 | status = HTTP_CONTINUE; |
472 | ||
473 | switch (con->http.state) | |
474 | { | |
475 | case HTTP_WAITING : | |
476 | /* | |
477 | * See if we've received a request line... | |
478 | */ | |
479 | ||
480 | if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL) | |
93894a43 | 481 | { |
482 | CloseClient(con); | |
483 | return (0); | |
484 | } | |
a74b005d | 485 | |
486 | /* | |
487 | * Ignore blank request lines... | |
488 | */ | |
489 | ||
490 | if (line[0] == '\0') | |
491 | break; | |
492 | ||
493 | /* | |
494 | * Clear other state variables... | |
495 | */ | |
496 | ||
497 | httpClearFields(HTTP(con)); | |
498 | ||
499 | con->http.activity = time(NULL); | |
500 | con->http.version = HTTP_1_0; | |
501 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
502 | con->http.data_encoding = HTTP_ENCODE_LENGTH; | |
503 | con->http.data_remaining = 0; | |
6a0c519d | 504 | con->operation = HTTP_WAITING; |
505 | con->bytes = 0; | |
a74b005d | 506 | con->file = 0; |
507 | con->pipe_pid = 0; | |
f63a2256 | 508 | con->command[0] = '\0'; |
a74b005d | 509 | con->username[0] = '\0'; |
510 | con->password[0] = '\0'; | |
511 | con->uri[0] = '\0'; | |
512 | ||
513 | if (con->language != NULL) | |
514 | { | |
515 | cupsLangFree(con->language); | |
516 | con->language = NULL; | |
517 | } | |
518 | ||
519 | /* | |
520 | * Grab the request line... | |
521 | */ | |
522 | ||
970017a4 | 523 | switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version)) |
a74b005d | 524 | { |
525 | case 1 : | |
526 | SendError(con, HTTP_BAD_REQUEST); | |
527 | CloseClient(con); | |
528 | return (0); | |
529 | case 2 : | |
530 | con->http.version = HTTP_0_9; | |
531 | break; | |
532 | case 3 : | |
533 | if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2) | |
534 | { | |
535 | SendError(con, HTTP_BAD_REQUEST); | |
536 | CloseClient(con); | |
537 | return (0); | |
538 | } | |
539 | ||
540 | if (major < 2) | |
541 | { | |
542 | con->http.version = (http_version_t)(major * 100 + minor); | |
753453e4 | 543 | if (con->http.version == HTTP_1_1 && KeepAlive) |
a74b005d | 544 | con->http.keep_alive = HTTP_KEEPALIVE_ON; |
545 | else | |
546 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
547 | } | |
548 | else | |
549 | { | |
550 | SendError(con, HTTP_NOT_SUPPORTED); | |
551 | CloseClient(con); | |
552 | return (0); | |
553 | } | |
554 | break; | |
555 | } | |
556 | ||
557 | /* | |
558 | * Process the request... | |
559 | */ | |
560 | ||
561 | if (strcmp(operation, "GET") == 0) | |
562 | con->http.state = HTTP_GET; | |
563 | else if (strcmp(operation, "PUT") == 0) | |
564 | con->http.state = HTTP_PUT; | |
565 | else if (strcmp(operation, "POST") == 0) | |
566 | con->http.state = HTTP_POST; | |
567 | else if (strcmp(operation, "DELETE") == 0) | |
568 | con->http.state = HTTP_DELETE; | |
569 | else if (strcmp(operation, "TRACE") == 0) | |
570 | con->http.state = HTTP_TRACE; | |
a74b005d | 571 | else if (strcmp(operation, "OPTIONS") == 0) |
572 | con->http.state = HTTP_OPTIONS; | |
573 | else if (strcmp(operation, "HEAD") == 0) | |
574 | con->http.state = HTTP_HEAD; | |
575 | else | |
576 | { | |
577 | SendError(con, HTTP_BAD_REQUEST); | |
578 | CloseClient(con); | |
579 | return (0); | |
580 | } | |
581 | ||
6a0c519d | 582 | con->start = time(NULL); |
583 | con->operation = con->http.state; | |
584 | ||
5ea8888e | 585 | LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd, |
4a64fdb7 | 586 | operation, con->uri, |
587 | con->http.version / 100, con->http.version % 100); | |
a74b005d | 588 | |
589 | con->http.status = HTTP_OK; | |
a74b005d | 590 | |
8f85ff77 | 591 | case HTTP_OPTIONS : |
a74b005d | 592 | case HTTP_DELETE : |
593 | case HTTP_GET : | |
594 | case HTTP_HEAD : | |
595 | case HTTP_POST : | |
596 | case HTTP_PUT : | |
597 | case HTTP_TRACE : | |
598 | /* | |
599 | * Parse incoming parameters until the status changes... | |
600 | */ | |
601 | ||
602 | status = httpUpdate(HTTP(con)); | |
603 | ||
604 | if (status != HTTP_OK && status != HTTP_CONTINUE) | |
605 | { | |
606 | SendError(con, HTTP_BAD_REQUEST); | |
607 | CloseClient(con); | |
608 | return (0); | |
609 | } | |
610 | break; | |
d21a7597 | 611 | |
612 | default : | |
613 | break; /* Anti-compiler-warning-code */ | |
a74b005d | 614 | } |
615 | ||
616 | /* | |
617 | * Handle new transfers... | |
618 | */ | |
619 | ||
620 | if (status == HTTP_OK) | |
621 | { | |
622 | con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]); | |
623 | ||
d2122fde | 624 | decode_auth(con); |
a74b005d | 625 | |
753453e4 | 626 | if (strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) == 0 && |
627 | KeepAlive) | |
b1ac1113 | 628 | con->http.keep_alive = HTTP_KEEPALIVE_ON; |
629 | ||
a74b005d | 630 | if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' && |
d558743f | 631 | con->http.version >= HTTP_1_1) |
a74b005d | 632 | { |
d558743f | 633 | /* |
634 | * HTTP/1.1 and higher require the "Host:" field... | |
635 | */ | |
636 | ||
a74b005d | 637 | if (!SendError(con, HTTP_BAD_REQUEST)) |
638 | { | |
639 | CloseClient(con); | |
640 | return (0); | |
641 | } | |
642 | } | |
8f85ff77 | 643 | else if (con->operation == HTTP_OPTIONS) |
644 | { | |
645 | /* | |
646 | * Do OPTIONS command... | |
647 | */ | |
648 | ||
753453e4 | 649 | if ((best = FindBest(con->uri, con->http.state)) != NULL && |
8f85ff77 | 650 | best->type != AUTH_NONE) |
651 | { | |
652 | if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL)) | |
653 | { | |
654 | CloseClient(con); | |
655 | return (0); | |
656 | } | |
657 | } | |
a75c006a | 658 | |
b5cb0608 | 659 | if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 && |
a75c006a | 660 | con->http.tls == NULL) |
8f85ff77 | 661 | { |
a75c006a | 662 | #ifdef HAVE_LIBSSL |
663 | /* | |
664 | * Do encryption stuff... | |
665 | */ | |
666 | ||
b38fb7fc | 667 | if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL)) |
8f85ff77 | 668 | { |
669 | CloseClient(con); | |
670 | return (0); | |
671 | } | |
a75c006a | 672 | |
673 | httpPrintf(HTTP(con), "Connection: Upgrade\r\n"); | |
b5cb0608 | 674 | httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n"); |
1c6682dd | 675 | httpPrintf(HTTP(con), "Content-Length: 0\r\n"); |
a75c006a | 676 | httpPrintf(HTTP(con), "\r\n"); |
677 | ||
678 | EncryptClient(con); | |
679 | #else | |
680 | if (!SendError(con, HTTP_NOT_IMPLEMENTED)) | |
681 | { | |
682 | CloseClient(con); | |
683 | return (0); | |
684 | } | |
685 | #endif /* HAVE_LIBSSL */ | |
686 | } | |
687 | ||
688 | if (!SendHeader(con, HTTP_OK, NULL)) | |
689 | { | |
690 | CloseClient(con); | |
691 | return (0); | |
8f85ff77 | 692 | } |
693 | ||
753453e4 | 694 | httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n"); |
1c6682dd | 695 | httpPrintf(HTTP(con), "Content-Length: 0\r\n"); |
8f85ff77 | 696 | httpPrintf(HTTP(con), "\r\n"); |
697 | } | |
bd84e0d1 | 698 | else if (strstr(con->uri, "..") != NULL) |
a74b005d | 699 | { |
700 | /* | |
701 | * Protect against malicious users! | |
702 | */ | |
703 | ||
704 | if (!SendError(con, HTTP_FORBIDDEN)) | |
705 | { | |
706 | CloseClient(con); | |
707 | return (0); | |
708 | } | |
709 | } | |
710 | else if (con->uri[0] != '/') | |
711 | { | |
712 | /* | |
713 | * Don't allow proxying (yet)... | |
714 | */ | |
715 | ||
716 | if (!SendError(con, HTTP_METHOD_NOT_ALLOWED)) | |
717 | { | |
718 | CloseClient(con); | |
719 | return (0); | |
720 | } | |
721 | } | |
b38fb7fc | 722 | else |
a74b005d | 723 | { |
b5cb0608 | 724 | if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 && |
725 | con->http.tls == NULL) | |
b38fb7fc | 726 | { |
727 | #ifdef HAVE_LIBSSL | |
728 | /* | |
729 | * Do encryption stuff... | |
730 | */ | |
f3d580b9 | 731 | |
b38fb7fc | 732 | if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL)) |
733 | { | |
734 | CloseClient(con); | |
735 | return (0); | |
736 | } | |
f969a074 | 737 | |
b38fb7fc | 738 | httpPrintf(HTTP(con), "Connection: Upgrade\r\n"); |
b5cb0608 | 739 | httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n"); |
1c6682dd | 740 | httpPrintf(HTTP(con), "Content-Length: 0\r\n"); |
b38fb7fc | 741 | httpPrintf(HTTP(con), "\r\n"); |
742 | ||
743 | EncryptClient(con); | |
77425121 | 744 | |
745 | status = IsAuthorized(con); | |
b38fb7fc | 746 | #else |
747 | if (!SendError(con, HTTP_NOT_IMPLEMENTED)) | |
748 | { | |
749 | CloseClient(con); | |
750 | return (0); | |
751 | } | |
752 | #endif /* HAVE_LIBSSL */ | |
753 | } | |
b5cb0608 | 754 | |
77425121 | 755 | if (status != HTTP_OK) |
b38fb7fc | 756 | { |
757 | SendError(con, status); | |
758 | CloseClient(con); | |
759 | return (0); | |
760 | } | |
761 | ||
762 | switch (con->http.state) | |
763 | { | |
764 | case HTTP_GET_SEND : | |
765 | if (strncmp(con->uri, "/printers/", 10) == 0 && | |
766 | strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0) | |
2a8fc30c | 767 | { |
b38fb7fc | 768 | /* |
769 | * Send PPD file - get the real printer name since printer | |
770 | * names are not case sensitive but filenames can be... | |
771 | */ | |
2a8fc30c | 772 | |
b38fb7fc | 773 | con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ |
f3d580b9 | 774 | |
b38fb7fc | 775 | if ((p = FindPrinter(con->uri + 10)) != NULL) |
776 | snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); | |
777 | else | |
778 | { | |
779 | if (!SendError(con, HTTP_NOT_FOUND)) | |
780 | { | |
781 | CloseClient(con); | |
782 | return (0); | |
783 | } | |
a74b005d | 784 | |
b38fb7fc | 785 | break; |
786 | } | |
b14d90ba | 787 | } |
b38fb7fc | 788 | |
753453e4 | 789 | if ((strncmp(con->uri, "/admin", 6) == 0 && |
790 | strncmp(con->uri, "/admin/conf/", 12) != 0) || | |
b38fb7fc | 791 | strncmp(con->uri, "/printers", 9) == 0 || |
792 | strncmp(con->uri, "/classes", 8) == 0 || | |
793 | strncmp(con->uri, "/jobs", 5) == 0) | |
b14d90ba | 794 | { |
b38fb7fc | 795 | /* |
796 | * Send CGI output... | |
797 | */ | |
798 | ||
799 | if (strncmp(con->uri, "/admin", 6) == 0) | |
800 | { | |
801 | snprintf(con->command, sizeof(con->command), | |
802 | "%s/cgi-bin/admin.cgi", ServerBin); | |
803 | con->options = con->uri + 6; | |
804 | } | |
805 | else if (strncmp(con->uri, "/printers", 9) == 0) | |
806 | { | |
807 | snprintf(con->command, sizeof(con->command), | |
808 | "%s/cgi-bin/printers.cgi", ServerBin); | |
809 | con->options = con->uri + 9; | |
810 | } | |
811 | else if (strncmp(con->uri, "/classes", 8) == 0) | |
812 | { | |
813 | snprintf(con->command, sizeof(con->command), | |
814 | "%s/cgi-bin/classes.cgi", ServerBin); | |
815 | con->options = con->uri + 8; | |
816 | } | |
817 | else | |
818 | { | |
819 | snprintf(con->command, sizeof(con->command), | |
820 | "%s/cgi-bin/jobs.cgi", ServerBin); | |
821 | con->options = con->uri + 5; | |
822 | } | |
823 | ||
824 | if (con->options[0] == '/') | |
825 | con->options ++; | |
826 | ||
827 | if (!SendCommand(con, con->command, con->options)) | |
828 | { | |
829 | if (!SendError(con, HTTP_NOT_FOUND)) | |
830 | { | |
831 | CloseClient(con); | |
832 | return (0); | |
833 | } | |
834 | } | |
835 | else | |
836 | LogRequest(con, HTTP_OK); | |
837 | ||
838 | if (con->http.version <= HTTP_1_0) | |
839 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
b14d90ba | 840 | } |
753453e4 | 841 | else if (strncmp(con->uri, "/admin/conf/", 12) == 0 && |
842 | (strchr(con->uri + 12, '/') != NULL || | |
843 | strlen(con->uri) == 12)) | |
844 | { | |
845 | /* | |
846 | * GET can only be done to configuration files under | |
847 | * /admin/conf... | |
848 | */ | |
849 | ||
850 | if (!SendError(con, HTTP_FORBIDDEN)) | |
851 | { | |
852 | CloseClient(con); | |
853 | return (0); | |
854 | } | |
855 | ||
856 | break; | |
857 | } | |
b14d90ba | 858 | else |
859 | { | |
b38fb7fc | 860 | /* |
861 | * Serve a file... | |
862 | */ | |
863 | ||
864 | if ((filename = get_file(con, &filestats)) == NULL) | |
865 | { | |
866 | if (!SendError(con, HTTP_NOT_FOUND)) | |
867 | { | |
868 | CloseClient(con); | |
869 | return (0); | |
870 | } | |
871 | } | |
872 | else if (!check_if_modified(con, &filestats)) | |
873 | { | |
874 | if (!SendError(con, HTTP_NOT_MODIFIED)) | |
875 | { | |
876 | CloseClient(con); | |
877 | return (0); | |
878 | } | |
879 | } | |
880 | else | |
881 | { | |
882 | type = mimeFileType(MimeDatabase, filename); | |
883 | if (type == NULL) | |
884 | strcpy(line, "text/plain"); | |
885 | else | |
886 | snprintf(line, sizeof(line), "%s/%s", type->super, type->type); | |
887 | ||
888 | if (!SendFile(con, HTTP_OK, filename, line, &filestats)) | |
889 | { | |
890 | CloseClient(con); | |
891 | return (0); | |
892 | } | |
893 | } | |
b14d90ba | 894 | } |
b38fb7fc | 895 | break; |
896 | ||
897 | case HTTP_POST_RECV : | |
898 | /* | |
899 | * See if the POST request includes a Content-Length field, and if | |
900 | * so check the length against any limits that are set... | |
901 | */ | |
b14d90ba | 902 | |
b38fb7fc | 903 | LogMessage(L_DEBUG2, "POST %s", con->uri); |
904 | LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]); | |
b14d90ba | 905 | |
b38fb7fc | 906 | if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] && |
907 | atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize && | |
908 | MaxRequestSize > 0) | |
a74b005d | 909 | { |
b38fb7fc | 910 | /* |
911 | * Request too large... | |
912 | */ | |
913 | ||
914 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
a74b005d | 915 | { |
b38fb7fc | 916 | CloseClient(con); |
a74b005d | 917 | return (0); |
918 | } | |
b38fb7fc | 919 | |
920 | break; | |
a74b005d | 921 | } |
922 | ||
b38fb7fc | 923 | /* |
924 | * See what kind of POST request this is; for IPP requests the | |
925 | * content-type field will be "application/ipp"... | |
a74b005d | 926 | */ |
927 | ||
b38fb7fc | 928 | if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0) |
929 | con->request = ippNew(); | |
753453e4 | 930 | else if ((strncmp(con->uri, "/admin", 6) == 0 && |
931 | strncmp(con->uri, "/admin/conf/", 12) != 0) || | |
b38fb7fc | 932 | strncmp(con->uri, "/printers", 9) == 0 || |
933 | strncmp(con->uri, "/classes", 8) == 0 || | |
934 | strncmp(con->uri, "/jobs", 5) == 0) | |
a74b005d | 935 | { |
b38fb7fc | 936 | /* |
937 | * CGI request... | |
938 | */ | |
939 | ||
940 | if (strncmp(con->uri, "/admin", 6) == 0) | |
a74b005d | 941 | { |
b38fb7fc | 942 | snprintf(con->command, sizeof(con->command), |
943 | "%s/cgi-bin/admin.cgi", ServerBin); | |
944 | con->options = con->uri + 6; | |
a74b005d | 945 | } |
b38fb7fc | 946 | else if (strncmp(con->uri, "/printers", 9) == 0) |
a74b005d | 947 | { |
b38fb7fc | 948 | snprintf(con->command, sizeof(con->command), |
949 | "%s/cgi-bin/printers.cgi", ServerBin); | |
950 | con->options = con->uri + 9; | |
951 | } | |
952 | else if (strncmp(con->uri, "/classes", 8) == 0) | |
953 | { | |
954 | snprintf(con->command, sizeof(con->command), | |
955 | "%s/cgi-bin/classes.cgi", ServerBin); | |
956 | con->options = con->uri + 8; | |
a74b005d | 957 | } |
e31bfb6e | 958 | else |
a74b005d | 959 | { |
b38fb7fc | 960 | snprintf(con->command, sizeof(con->command), |
961 | "%s/cgi-bin/jobs.cgi", ServerBin); | |
962 | con->options = con->uri + 5; | |
a74b005d | 963 | } |
8b43895a | 964 | |
b38fb7fc | 965 | if (con->options[0] == '/') |
966 | con->options ++; | |
f63a2256 | 967 | |
b38fb7fc | 968 | LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"", |
969 | con->http.fd, con->command, con->options); | |
8b43895a | 970 | |
b38fb7fc | 971 | if (con->http.version <= HTTP_1_0) |
972 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
973 | } | |
974 | else if (!SendError(con, HTTP_UNAUTHORIZED)) | |
8b43895a | 975 | { |
976 | CloseClient(con); | |
977 | return (0); | |
978 | } | |
8b43895a | 979 | break; |
b14d90ba | 980 | |
b38fb7fc | 981 | case HTTP_PUT_RECV : |
753453e4 | 982 | /* |
983 | * Validate the resource name... | |
984 | */ | |
985 | ||
986 | if (strncmp(con->uri, "/admin/conf/", 12) != 0 || | |
987 | strchr(con->uri + 12, '/') != NULL || | |
988 | strlen(con->uri) == 12) | |
989 | { | |
990 | /* | |
991 | * PUT can only be done to configuration files under | |
992 | * /admin/conf... | |
993 | */ | |
994 | ||
995 | if (!SendError(con, HTTP_FORBIDDEN)) | |
996 | { | |
997 | CloseClient(con); | |
998 | return (0); | |
999 | } | |
1000 | ||
1001 | break; | |
1002 | } | |
1003 | ||
1004 | /* | |
1005 | * See if the PUT request includes a Content-Length field, and if | |
1006 | * so check the length against any limits that are set... | |
1007 | */ | |
1008 | ||
1009 | LogMessage(L_DEBUG2, "PUT %s", con->uri); | |
1010 | LogMessage(L_DEBUG2, "CONTENT_TYPE = %s", con->http.fields[HTTP_FIELD_CONTENT_TYPE]); | |
1011 | ||
1012 | if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] && | |
1013 | atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) > MaxRequestSize && | |
1014 | MaxRequestSize > 0) | |
1015 | { | |
1016 | /* | |
1017 | * Request too large... | |
1018 | */ | |
1019 | ||
1020 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
1021 | { | |
1022 | CloseClient(con); | |
1023 | return (0); | |
1024 | } | |
1025 | ||
1026 | break; | |
1027 | } | |
1028 | ||
1029 | /* | |
1030 | * Open a temporary file to hold the request... | |
1031 | */ | |
1032 | ||
1033 | snprintf(con->filename, sizeof(con->filename), "%s/%08x", | |
1034 | RequestRoot, request_id ++); | |
1035 | con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); | |
1036 | fchmod(con->file, 0640); | |
1037 | fchown(con->file, User, Group); | |
1038 | ||
1039 | LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd, | |
1040 | con->filename, con->file); | |
1041 | ||
1042 | if (con->file < 0) | |
1043 | { | |
1044 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
1045 | { | |
1046 | CloseClient(con); | |
1047 | return (0); | |
1048 | } | |
1049 | } | |
1050 | break; | |
1051 | ||
b38fb7fc | 1052 | case HTTP_DELETE : |
1053 | case HTTP_TRACE : | |
1054 | SendError(con, HTTP_NOT_IMPLEMENTED); | |
1055 | CloseClient(con); | |
1056 | return (0); | |
b14d90ba | 1057 | |
b38fb7fc | 1058 | case HTTP_HEAD : |
d588a92e | 1059 | if (strncmp(con->uri, "/printers/", 10) == 0 && |
b38fb7fc | 1060 | strcmp(con->uri + strlen(con->uri) - 4, ".ppd") == 0) |
bd84e0d1 | 1061 | { |
b38fb7fc | 1062 | /* |
d588a92e | 1063 | * Send PPD file - get the real printer name since printer |
1064 | * names are not case sensitive but filenames can be... | |
b38fb7fc | 1065 | */ |
a74b005d | 1066 | |
d588a92e | 1067 | con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ |
1068 | ||
1069 | if ((p = FindPrinter(con->uri + 10)) != NULL) | |
1070 | snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); | |
1071 | else | |
1072 | { | |
1073 | if (!SendError(con, HTTP_NOT_FOUND)) | |
1074 | { | |
1075 | CloseClient(con); | |
1076 | return (0); | |
1077 | } | |
1078 | ||
1079 | break; | |
1080 | } | |
b38fb7fc | 1081 | } |
a74b005d | 1082 | |
753453e4 | 1083 | if ((strncmp(con->uri, "/admin/", 7) == 0 && |
1084 | strncmp(con->uri, "/admin/conf/", 12) != 0) || | |
b38fb7fc | 1085 | strncmp(con->uri, "/printers/", 10) == 0 || |
1086 | strncmp(con->uri, "/classes/", 9) == 0 || | |
1087 | strncmp(con->uri, "/jobs/", 6) == 0) | |
1088 | { | |
1089 | /* | |
1090 | * CGI output... | |
1091 | */ | |
f3d580b9 | 1092 | |
b38fb7fc | 1093 | if (!SendHeader(con, HTTP_OK, "text/html")) |
1094 | { | |
1095 | CloseClient(con); | |
1096 | return (0); | |
1097 | } | |
f3d580b9 | 1098 | |
b38fb7fc | 1099 | if (httpPrintf(HTTP(con), "\r\n") < 0) |
1100 | { | |
1101 | CloseClient(con); | |
1102 | return (0); | |
1103 | } | |
a74b005d | 1104 | |
b38fb7fc | 1105 | LogRequest(con, HTTP_OK); |
a74b005d | 1106 | } |
753453e4 | 1107 | else if (strncmp(con->uri, "/admin/conf/", 12) == 0 && |
1108 | (strchr(con->uri + 12, '/') != NULL || | |
1109 | strlen(con->uri) == 12)) | |
1110 | { | |
1111 | /* | |
1112 | * HEAD can only be done to configuration files under | |
1113 | * /admin/conf... | |
1114 | */ | |
1115 | ||
1116 | if (!SendError(con, HTTP_FORBIDDEN)) | |
1117 | { | |
1118 | CloseClient(con); | |
1119 | return (0); | |
1120 | } | |
1121 | ||
1122 | break; | |
1123 | } | |
b38fb7fc | 1124 | else if ((filename = get_file(con, &filestats)) == NULL) |
a74b005d | 1125 | { |
b38fb7fc | 1126 | if (!SendHeader(con, HTTP_NOT_FOUND, "text/html")) |
1127 | { | |
1128 | CloseClient(con); | |
1129 | return (0); | |
1130 | } | |
6a0c519d | 1131 | |
b38fb7fc | 1132 | LogRequest(con, HTTP_NOT_FOUND); |
a74b005d | 1133 | } |
b38fb7fc | 1134 | else if (!check_if_modified(con, &filestats)) |
1135 | { | |
1136 | if (!SendError(con, HTTP_NOT_MODIFIED)) | |
1137 | { | |
1138 | CloseClient(con); | |
1139 | return (0); | |
1140 | } | |
6a0c519d | 1141 | |
b38fb7fc | 1142 | LogRequest(con, HTTP_NOT_MODIFIED); |
a74b005d | 1143 | } |
b38fb7fc | 1144 | else |
1145 | { | |
1146 | /* | |
1147 | * Serve a file... | |
1148 | */ | |
6a0c519d | 1149 | |
b38fb7fc | 1150 | type = mimeFileType(MimeDatabase, filename); |
1151 | if (type == NULL) | |
1152 | strcpy(line, "text/plain"); | |
1153 | else | |
1154 | snprintf(line, sizeof(line), "%s/%s", type->super, type->type); | |
a74b005d | 1155 | |
b38fb7fc | 1156 | if (!SendHeader(con, HTTP_OK, line)) |
1157 | { | |
1158 | CloseClient(con); | |
1159 | return (0); | |
1160 | } | |
a74b005d | 1161 | |
b38fb7fc | 1162 | if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", |
1163 | httpGetDateString(filestats.st_mtime)) < 0) | |
1164 | { | |
1165 | CloseClient(con); | |
1166 | return (0); | |
1167 | } | |
a74b005d | 1168 | |
753453e4 | 1169 | if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n", |
1170 | (unsigned long)filestats.st_size) < 0) | |
b38fb7fc | 1171 | { |
1172 | CloseClient(con); | |
1173 | return (0); | |
1174 | } | |
1175 | ||
1176 | LogRequest(con, HTTP_OK); | |
a74b005d | 1177 | } |
1178 | ||
b38fb7fc | 1179 | if (httpPrintf(HTTP(con), "\r\n") < 0) |
a74b005d | 1180 | { |
1181 | CloseClient(con); | |
1182 | return (0); | |
1183 | } | |
6a0c519d | 1184 | |
b38fb7fc | 1185 | con->http.state = HTTP_WAITING; |
1186 | break; | |
a74b005d | 1187 | |
b38fb7fc | 1188 | default : |
1189 | break; /* Anti-compiler-warning-code */ | |
1190 | } | |
a74b005d | 1191 | } |
1192 | } | |
1193 | ||
1194 | /* | |
1195 | * Handle any incoming data... | |
1196 | */ | |
1197 | ||
1198 | switch (con->http.state) | |
1199 | { | |
1200 | case HTTP_PUT_RECV : | |
753453e4 | 1201 | LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d", |
1202 | con->http.fd, | |
1203 | con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length", | |
1204 | con->http.data_remaining, con->file); | |
1205 | ||
1206 | if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0) | |
1207 | { | |
1208 | CloseClient(con); | |
1209 | return (0); | |
1210 | } | |
1211 | else if (bytes > 0) | |
1212 | { | |
1213 | con->bytes += bytes; | |
1214 | ||
1215 | LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d", | |
1216 | con->http.fd, bytes, con->file); | |
1217 | ||
1218 | if (write(con->file, line, bytes) < bytes) | |
1219 | { | |
1220 | LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s", | |
1221 | bytes, con->filename, strerror(errno)); | |
1222 | ||
6cd03542 | 1223 | LogMessage(L_DEBUG2, "ReadClient: Closing data file %d...", |
1224 | con->file); | |
1225 | ||
753453e4 | 1226 | close(con->file); |
1227 | con->file = 0; | |
1228 | unlink(con->filename); | |
1229 | con->filename[0] = '\0'; | |
1230 | ||
1231 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
1232 | { | |
1233 | CloseClient(con); | |
1234 | return (0); | |
1235 | } | |
1236 | } | |
1237 | } | |
1238 | ||
1239 | if (con->http.state == HTTP_WAITING) | |
1240 | { | |
1241 | /* | |
1242 | * End of file, see how big it is... | |
1243 | */ | |
1244 | ||
1245 | fstat(con->file, &filestats); | |
1246 | ||
1247 | LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.", | |
aa7e125a | 1248 | con->http.fd, con->file, (int)filestats.st_size); |
753453e4 | 1249 | |
1250 | close(con->file); | |
1251 | con->file = 0; | |
1252 | ||
1253 | if (filestats.st_size > MaxRequestSize && | |
1254 | MaxRequestSize > 0) | |
1255 | { | |
1256 | /* | |
1257 | * Request is too big; remove it and send an error... | |
1258 | */ | |
1259 | ||
1260 | LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s", | |
1261 | con->http.fd, con->filename); | |
1262 | unlink(con->filename); | |
1263 | con->filename[0] = '\0'; | |
1264 | ||
1265 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
1266 | { | |
1267 | CloseClient(con); | |
1268 | return (0); | |
1269 | } | |
1270 | } | |
1271 | ||
1272 | /* | |
1273 | * Install the configuration file... | |
1274 | */ | |
1275 | ||
1276 | status = install_conf_file(con); | |
1277 | ||
1278 | /* | |
1279 | * Return the status to the client... | |
1280 | */ | |
1281 | ||
1282 | if (!SendError(con, status)) | |
1283 | { | |
1284 | CloseClient(con); | |
1285 | return (0); | |
1286 | } | |
1287 | } | |
a74b005d | 1288 | break; |
1289 | ||
1290 | case HTTP_POST_RECV : | |
58e8cf34 | 1291 | LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d", |
93894a43 | 1292 | con->http.fd, |
a74b005d | 1293 | con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length", |
f63a2256 | 1294 | con->http.data_remaining, con->file); |
a74b005d | 1295 | |
b14d90ba | 1296 | if (con->request != NULL) |
1297 | { | |
1298 | /* | |
1299 | * Grab any request data from the connection... | |
1300 | */ | |
1301 | ||
bd176c9c | 1302 | if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR) |
1303 | { | |
5ea8888e | 1304 | LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!", |
bd176c9c | 1305 | con->http.fd); |
1306 | CloseClient(con); | |
1307 | return (0); | |
1308 | } | |
1309 | else if (ipp_state != IPP_DATA) | |
b14d90ba | 1310 | break; |
68ff4f01 | 1311 | else |
1312 | con->bytes += ippLength(con->request); | |
f63a2256 | 1313 | } |
b14d90ba | 1314 | |
f63a2256 | 1315 | if (con->file == 0 && con->http.state != HTTP_POST_SEND) |
1316 | { | |
1317 | /* | |
1318 | * Create a file as needed for the request data... | |
1319 | */ | |
1d2c70a6 | 1320 | |
04de52f8 | 1321 | snprintf(con->filename, sizeof(con->filename), "%s/%08x", |
f63a2256 | 1322 | RequestRoot, request_id ++); |
1323 | con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); | |
1324 | fchmod(con->file, 0640); | |
1325 | fchown(con->file, User, Group); | |
b14d90ba | 1326 | |
a61a2be6 | 1327 | LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd, |
1328 | con->filename, con->file); | |
b14d90ba | 1329 | |
f63a2256 | 1330 | if (con->file < 0) |
1331 | { | |
1332 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
b14d90ba | 1333 | { |
f63a2256 | 1334 | CloseClient(con); |
1335 | return (0); | |
b14d90ba | 1336 | } |
1337 | } | |
f63a2256 | 1338 | } |
b14d90ba | 1339 | |
bfa1abf0 | 1340 | if (con->http.state != HTTP_POST_SEND) |
a74b005d | 1341 | { |
bfa1abf0 | 1342 | if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0) |
a74b005d | 1343 | { |
bfa1abf0 | 1344 | CloseClient(con); |
1345 | return (0); | |
1346 | } | |
1347 | else if (bytes > 0) | |
1348 | { | |
1349 | con->bytes += bytes; | |
a74b005d | 1350 | |
a61a2be6 | 1351 | LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d", |
1352 | con->http.fd, bytes, con->file); | |
bfa1abf0 | 1353 | |
1354 | if (write(con->file, line, bytes) < bytes) | |
a74b005d | 1355 | { |
a61a2be6 | 1356 | LogMessage(L_ERROR, "ReadClient: Unable to write %d bytes to %s: %s", |
1357 | bytes, con->filename, strerror(errno)); | |
1358 | ||
6cd03542 | 1359 | LogMessage(L_DEBUG2, "ReadClient: Closing file %d...", |
1360 | con->file); | |
1361 | ||
bfa1abf0 | 1362 | close(con->file); |
1363 | con->file = 0; | |
1364 | unlink(con->filename); | |
753453e4 | 1365 | con->filename[0] = '\0'; |
bfa1abf0 | 1366 | |
1367 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
1368 | { | |
1369 | CloseClient(con); | |
1370 | return (0); | |
1371 | } | |
a74b005d | 1372 | } |
1373 | } | |
bd176c9c | 1374 | else if (con->http.state != HTTP_POST_SEND) |
1375 | { | |
1376 | CloseClient(con); | |
1377 | return (0); | |
1378 | } | |
a74b005d | 1379 | } |
93894a43 | 1380 | |
1381 | if (con->http.state == HTTP_POST_SEND) | |
1382 | { | |
bfa1abf0 | 1383 | if (con->file) |
1384 | { | |
8b43895a | 1385 | fstat(con->file, &filestats); |
bd917997 | 1386 | |
1387 | LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.", | |
aa7e125a | 1388 | con->http.fd, con->file, (int)filestats.st_size); |
bd917997 | 1389 | |
bfa1abf0 | 1390 | close(con->file); |
1391 | con->file = 0; | |
8b43895a | 1392 | |
1393 | if (filestats.st_size > MaxRequestSize && | |
1394 | MaxRequestSize > 0) | |
1395 | { | |
1396 | /* | |
1397 | * Request is too big; remove it and send an error... | |
1398 | */ | |
1399 | ||
a61a2be6 | 1400 | LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s", |
1401 | con->http.fd, con->filename); | |
8b43895a | 1402 | unlink(con->filename); |
753453e4 | 1403 | con->filename[0] = '\0'; |
8b43895a | 1404 | |
1405 | if (con->request) | |
1406 | { | |
1407 | /* | |
1408 | * Delete any IPP request data... | |
1409 | */ | |
1410 | ||
1411 | ippDelete(con->request); | |
1412 | con->request = NULL; | |
1413 | } | |
1414 | ||
1415 | if (!SendError(con, HTTP_REQUEST_TOO_LARGE)) | |
1416 | { | |
1417 | CloseClient(con); | |
1418 | return (0); | |
1419 | } | |
1420 | } | |
f63a2256 | 1421 | |
1422 | if (con->command[0]) | |
1423 | { | |
1424 | if (!SendCommand(con, con->command, con->options)) | |
1425 | { | |
1426 | if (!SendError(con, HTTP_NOT_FOUND)) | |
1427 | { | |
1428 | CloseClient(con); | |
1429 | return (0); | |
1430 | } | |
1431 | } | |
1432 | else | |
1433 | LogRequest(con, HTTP_OK); | |
1434 | } | |
bfa1abf0 | 1435 | } |
93894a43 | 1436 | |
1437 | if (con->request) | |
e31bfb6e | 1438 | ProcessIPPRequest(con); |
93894a43 | 1439 | } |
a74b005d | 1440 | break; |
d21a7597 | 1441 | |
1442 | default : | |
1443 | break; /* Anti-compiler-warning-code */ | |
a74b005d | 1444 | } |
1445 | ||
1446 | if (!con->http.keep_alive && con->http.state == HTTP_WAITING) | |
1447 | { | |
1448 | CloseClient(con); | |
1449 | return (0); | |
1450 | } | |
1451 | else | |
1452 | return (1); | |
1453 | } | |
1454 | ||
1455 | ||
a74b005d | 1456 | /* |
1457 | * 'SendCommand()' - Send output from a command via HTTP. | |
1458 | */ | |
1459 | ||
1460 | int | |
1461 | SendCommand(client_t *con, | |
a74b005d | 1462 | char *command, |
b14d90ba | 1463 | char *options) |
a74b005d | 1464 | { |
f63a2256 | 1465 | int fd; |
1466 | ||
1467 | ||
1468 | if (con->filename[0]) | |
1469 | fd = open(con->filename, O_RDONLY); | |
1470 | else | |
1471 | fd = open("/dev/null", O_RDONLY); | |
1472 | ||
1473 | con->pipe_pid = pipe_command(con, fd, &(con->file), command, options); | |
1474 | ||
1475 | close(fd); | |
a74b005d | 1476 | |
b5cb0608 | 1477 | LogMessage(L_INFO, "Started \"%s\" (pid=%d)", command, con->pipe_pid); |
1478 | ||
1479 | LogMessage(L_DEBUG, "SendCommand() %d file=%d", con->http.fd, con->file); | |
a74b005d | 1480 | |
1481 | if (con->pipe_pid == 0) | |
1482 | return (0); | |
1483 | ||
1484 | fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); | |
1485 | ||
27eba2dd | 1486 | LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to InputSet...", con->file); |
e668e627 | 1487 | LogMessage(L_DEBUG2, "SendCommand: Adding fd %d to OutputSet...", |
1488 | con->http.fd); | |
1489 | ||
a74b005d | 1490 | FD_SET(con->file, &InputSet); |
1491 | FD_SET(con->http.fd, &OutputSet); | |
1492 | ||
96df88bb | 1493 | if (!SendHeader(con, HTTP_OK, NULL)) |
a74b005d | 1494 | return (0); |
1495 | ||
1496 | if (con->http.version == HTTP_1_1) | |
1497 | { | |
1498 | con->http.data_encoding = HTTP_ENCODE_CHUNKED; | |
1499 | ||
1500 | if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0) | |
1501 | return (0); | |
1502 | } | |
1503 | ||
13486cb7 | 1504 | con->got_fields = 0; |
1505 | con->field_col = 0; | |
1506 | ||
a74b005d | 1507 | return (1); |
1508 | } | |
1509 | ||
1510 | ||
1511 | /* | |
1512 | * 'SendError()' - Send an error message via HTTP. | |
1513 | */ | |
1514 | ||
1515 | int /* O - 1 if successful, 0 otherwise */ | |
1516 | SendError(client_t *con, /* I - Connection */ | |
1517 | http_status_t code) /* I - Error code */ | |
1518 | { | |
83e740a5 | 1519 | char message[1024]; /* Message for user */ |
a74b005d | 1520 | |
1521 | ||
6a0c519d | 1522 | /* |
1523 | * Put the request in the access_log file... | |
1524 | */ | |
1525 | ||
993e15da | 1526 | if (con->operation > HTTP_WAITING) |
1527 | LogRequest(con, code); | |
6a0c519d | 1528 | |
753453e4 | 1529 | LogMessage(L_DEBUG, "SendError() %d code=%d", con->http.fd, code); |
a61a2be6 | 1530 | |
a74b005d | 1531 | /* |
1532 | * To work around bugs in some proxies, don't use Keep-Alive for some | |
1533 | * error messages... | |
1534 | */ | |
1535 | ||
1536 | if (code >= HTTP_BAD_REQUEST) | |
1537 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
1538 | ||
1539 | /* | |
1540 | * Send an error message back to the client. If the error code is a | |
1541 | * 400 or 500 series, make sure the message contains some text, too! | |
1542 | */ | |
1543 | ||
1544 | if (!SendHeader(con, code, NULL)) | |
1545 | return (0); | |
1546 | ||
a75c006a | 1547 | #ifdef HAVE_LIBSSL |
1548 | if (code == HTTP_UPGRADE_REQUIRED) | |
a75c006a | 1549 | if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0) |
1550 | return (0); | |
1551 | ||
b5cb0608 | 1552 | if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0) |
1553 | return (0); | |
a75c006a | 1554 | #endif /* HAVE_LIBSSL */ |
1555 | ||
a74b005d | 1556 | if (con->http.version >= HTTP_1_1 && !con->http.keep_alive) |
1557 | { | |
1558 | if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0) | |
1559 | return (0); | |
1560 | } | |
1561 | ||
1562 | if (code >= HTTP_BAD_REQUEST) | |
1563 | { | |
1564 | /* | |
1565 | * Send a human-readable error message. | |
1566 | */ | |
1567 | ||
04de52f8 | 1568 | snprintf(message, sizeof(message), |
970017a4 | 1569 | "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>" |
1570 | "<BODY><H1>%s</H1>%s</BODY></HTML>\n", | |
1571 | code, httpStatus(code), httpStatus(code), | |
1572 | con->language ? con->language->messages[code] : | |
1573 | httpStatus(code)); | |
a74b005d | 1574 | |
1575 | if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0) | |
1576 | return (0); | |
1577 | if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", strlen(message)) < 0) | |
1578 | return (0); | |
1579 | if (httpPrintf(HTTP(con), "\r\n") < 0) | |
1580 | return (0); | |
97d73ddb | 1581 | if (httpPrintf(HTTP(con), "%s", message) < 0) |
a74b005d | 1582 | return (0); |
1583 | } | |
1584 | else if (httpPrintf(HTTP(con), "\r\n") < 0) | |
1585 | return (0); | |
1586 | ||
1587 | con->http.state = HTTP_WAITING; | |
1588 | ||
1589 | return (1); | |
1590 | } | |
1591 | ||
1592 | ||
1593 | /* | |
1594 | * 'SendFile()' - Send a file via HTTP. | |
1595 | */ | |
1596 | ||
1597 | int | |
1598 | SendFile(client_t *con, | |
1599 | http_status_t code, | |
1600 | char *filename, | |
1601 | char *type, | |
1602 | struct stat *filestats) | |
1603 | { | |
1604 | con->file = open(filename, O_RDONLY); | |
1605 | ||
5ea8888e | 1606 | LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file); |
a74b005d | 1607 | |
1608 | if (con->file < 0) | |
1609 | return (0); | |
1610 | ||
1611 | fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); | |
1612 | ||
1613 | con->pipe_pid = 0; | |
1614 | ||
1615 | if (!SendHeader(con, code, type)) | |
1616 | return (0); | |
1617 | ||
1618 | if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0) | |
1619 | return (0); | |
753453e4 | 1620 | if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n", |
1621 | (unsigned long)filestats->st_size) < 0) | |
a74b005d | 1622 | return (0); |
1623 | if (httpPrintf(HTTP(con), "\r\n") < 0) | |
1624 | return (0); | |
1625 | ||
e668e627 | 1626 | LogMessage(L_DEBUG2, "SendFile: Adding fd %d to OutputSet...", con->http.fd); |
1627 | ||
a74b005d | 1628 | FD_SET(con->http.fd, &OutputSet); |
1629 | ||
1630 | return (1); | |
1631 | } | |
1632 | ||
1633 | ||
1634 | /* | |
93894a43 | 1635 | * 'SendHeader()' - Send an HTTP request. |
a74b005d | 1636 | */ |
1637 | ||
1638 | int /* O - 1 on success, 0 on failure */ | |
1639 | SendHeader(client_t *con, /* I - Client to send to */ | |
1640 | http_status_t code, /* I - HTTP status code */ | |
1641 | char *type) /* I - MIME type of document */ | |
1642 | { | |
83e740a5 | 1643 | location_t *loc; /* Authentication location */ |
1644 | ||
1645 | ||
a74b005d | 1646 | if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100, |
ffe90a99 | 1647 | con->http.version % 100, code, httpStatus(code)) < 0) |
a74b005d | 1648 | return (0); |
1649 | if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0) | |
1650 | return (0); | |
bd84e0d1 | 1651 | if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0) |
a74b005d | 1652 | return (0); |
1653 | if (con->http.keep_alive && con->http.version >= HTTP_1_0) | |
1654 | { | |
1655 | if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0) | |
1656 | return (0); | |
1657 | if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0) | |
1658 | return (0); | |
1659 | } | |
8f85ff77 | 1660 | if (code == HTTP_METHOD_NOT_ALLOWED) |
9bfcbffc | 1661 | if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0) |
8f85ff77 | 1662 | return (0); |
1663 | ||
5ea8888e | 1664 | if (code == HTTP_UNAUTHORIZED) |
83e740a5 | 1665 | { |
753453e4 | 1666 | /* |
1667 | * This already succeeded in IsAuthorized... | |
1668 | */ | |
1669 | ||
1670 | loc = FindBest(con->uri, con->http.state); | |
83e740a5 | 1671 | |
753453e4 | 1672 | if (loc->type != AUTH_DIGEST) |
83e740a5 | 1673 | { |
1674 | if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0) | |
1675 | return (0); | |
1676 | } | |
1677 | else | |
1678 | { | |
1679 | if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" " | |
1680 | "nonce=\"%s\"\r\n", con->http.hostname) < 0) | |
1681 | return (0); | |
1682 | } | |
1683 | } | |
a74b005d | 1684 | if (con->language != NULL) |
1685 | { | |
1686 | if (httpPrintf(HTTP(con), "Content-Language: %s\r\n", | |
1687 | con->language->language) < 0) | |
1688 | return (0); | |
1689 | ||
1690 | if (type != NULL) | |
1691 | if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type, | |
1692 | cupsLangEncoding(con->language)) < 0) | |
1693 | return (0); | |
1694 | } | |
1695 | else if (type != NULL) | |
1696 | if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0) | |
1697 | return (0); | |
1698 | ||
1699 | return (1); | |
1700 | } | |
1701 | ||
1702 | ||
a74b005d | 1703 | /* |
1704 | * 'WriteClient()' - Write data to a client as needed. | |
1705 | */ | |
1706 | ||
ff49100f | 1707 | int /* O - 1 if success, 0 if fail */ |
1708 | WriteClient(client_t *con) /* I - Client connection */ | |
a74b005d | 1709 | { |
ff49100f | 1710 | int bytes; /* Number of bytes written */ |
13486cb7 | 1711 | char buf[HTTP_MAX_BUFFER + 1];/* Data buffer */ |
1712 | char *bufptr; /* Pointer into buffer */ | |
ff49100f | 1713 | ipp_state_t ipp_state; /* IPP state value */ |
a74b005d | 1714 | |
1715 | ||
1716 | if (con->http.state != HTTP_GET_SEND && | |
1717 | con->http.state != HTTP_POST_SEND) | |
1718 | return (1); | |
1719 | ||
ff49100f | 1720 | if (con->response != NULL) |
bfa1abf0 | 1721 | { |
ff49100f | 1722 | ipp_state = ippWrite(&(con->http), con->response); |
1723 | bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA; | |
bfa1abf0 | 1724 | } |
13486cb7 | 1725 | else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0) |
a74b005d | 1726 | { |
13486cb7 | 1727 | if (con->pipe_pid && !con->got_fields) |
1728 | { | |
1729 | /* | |
1730 | * Inspect the data for Content-Type and other fields. | |
1731 | */ | |
1732 | ||
1733 | buf[bytes] = '\0'; | |
1734 | ||
1735 | for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++) | |
1736 | if (*bufptr == '\n') | |
1737 | { | |
1738 | /* | |
1739 | * Send line to client... | |
1740 | */ | |
1741 | ||
1742 | if (bufptr > buf && bufptr[-1] == '\r') | |
1743 | bufptr[-1] = '\0'; | |
1744 | *bufptr++ = '\0'; | |
1745 | ||
1746 | httpPrintf(HTTP(con), "%s\r\n", buf); | |
58e8cf34 | 1747 | LogMessage(L_DEBUG2, "WriteClient() %d %s", con->http.fd, buf); |
13486cb7 | 1748 | |
1749 | /* | |
1750 | * Update buffer... | |
1751 | */ | |
1752 | ||
1753 | bytes -= (bufptr - buf); | |
1754 | memcpy(buf, bufptr, bytes + 1); | |
1755 | bufptr = buf - 1; | |
1756 | ||
1757 | /* | |
1758 | * See if the line was empty... | |
1759 | */ | |
1760 | ||
1761 | if (con->field_col == 0) | |
1762 | con->got_fields = 1; | |
1763 | else | |
1764 | con->field_col = 0; | |
1765 | } | |
1766 | else if (*bufptr != '\r') | |
1767 | con->field_col ++; | |
1768 | ||
1769 | if (bytes > 0 && !con->got_fields) | |
1770 | { | |
1771 | /* | |
1772 | * Remaining text needs to go out... | |
1773 | */ | |
1774 | ||
1775 | httpPrintf(HTTP(con), "%s", buf); | |
1776 | ||
1777 | con->http.activity = time(NULL); | |
1778 | return (1); | |
1779 | } | |
1780 | else if (bytes == 0) | |
1781 | { | |
1782 | con->http.activity = time(NULL); | |
1783 | return (1); | |
1784 | } | |
1785 | } | |
1786 | ||
a74b005d | 1787 | if (httpWrite(HTTP(con), buf, bytes) < 0) |
1788 | { | |
1789 | CloseClient(con); | |
1790 | return (0); | |
1791 | } | |
6a0c519d | 1792 | |
1793 | con->bytes += bytes; | |
a74b005d | 1794 | } |
b14d90ba | 1795 | |
1796 | if (bytes <= 0) | |
a74b005d | 1797 | { |
6a0c519d | 1798 | LogRequest(con, HTTP_OK); |
1799 | ||
a74b005d | 1800 | if (con->http.data_encoding == HTTP_ENCODE_CHUNKED) |
1801 | { | |
1802 | if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0) | |
1803 | { | |
1804 | CloseClient(con); | |
1805 | return (0); | |
1806 | } | |
1807 | } | |
1808 | ||
96df88bb | 1809 | con->http.state = HTTP_WAITING; |
1810 | ||
e668e627 | 1811 | LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from OutputSet...", |
1812 | con->http.fd); | |
1813 | ||
a74b005d | 1814 | FD_CLR(con->http.fd, &OutputSet); |
a74b005d | 1815 | |
bfa1abf0 | 1816 | if (con->file) |
a74b005d | 1817 | { |
27eba2dd | 1818 | LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from InputSet...", |
1819 | con->file); | |
bfa1abf0 | 1820 | FD_CLR(con->file, &InputSet); |
a74b005d | 1821 | |
bfa1abf0 | 1822 | if (con->pipe_pid) |
96df88bb | 1823 | kill(con->pipe_pid, SIGTERM); |
bfa1abf0 | 1824 | |
bd917997 | 1825 | LogMessage(L_DEBUG2, "WriteClient() %d Closing data file %d.", |
1826 | con->http.fd, con->file); | |
1827 | ||
bfa1abf0 | 1828 | close(con->file); |
96df88bb | 1829 | con->file = 0; |
1830 | con->pipe_pid = 0; | |
bfa1abf0 | 1831 | } |
a74b005d | 1832 | |
d4c438d4 | 1833 | if (con->filename[0]) |
a61a2be6 | 1834 | { |
1835 | LogMessage(L_DEBUG2, "WriteClient() %d Removing temp file %s", | |
bd917997 | 1836 | con->http.fd, con->filename); |
d4c438d4 | 1837 | unlink(con->filename); |
753453e4 | 1838 | con->filename[0] = '\0'; |
a61a2be6 | 1839 | } |
d4c438d4 | 1840 | |
b14d90ba | 1841 | if (con->request != NULL) |
1842 | { | |
1843 | ippDelete(con->request); | |
1844 | con->request = NULL; | |
1845 | } | |
1846 | ||
1847 | if (con->response != NULL) | |
1848 | { | |
1849 | ippDelete(con->response); | |
1850 | con->response = NULL; | |
1851 | } | |
96df88bb | 1852 | |
1853 | if (!con->http.keep_alive) | |
1854 | { | |
1855 | CloseClient(con); | |
1856 | return (0); | |
1857 | } | |
a74b005d | 1858 | } |
1859 | ||
997edb40 | 1860 | if (bytes >= 1024) |
58e8cf34 | 1861 | LogMessage(L_DEBUG2, "WriteClient() %d %d bytes", con->http.fd, bytes); |
a74b005d | 1862 | |
1863 | con->http.activity = time(NULL); | |
1864 | ||
1865 | return (1); | |
1866 | } | |
1867 | ||
1868 | ||
a74b005d | 1869 | /* |
1870 | * 'check_if_modified()' - Decode an "If-Modified-Since" line. | |
1871 | */ | |
1872 | ||
1873 | static int /* O - 1 if modified since */ | |
1874 | check_if_modified(client_t *con, /* I - Client connection */ | |
1875 | struct stat *filestats) /* I - File information */ | |
1876 | { | |
1877 | char *ptr; /* Pointer into field */ | |
1878 | time_t date; /* Time/date value */ | |
1879 | int size; /* Size/length value */ | |
1880 | ||
1881 | ||
1882 | size = 0; | |
1883 | date = 0; | |
1884 | ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE]; | |
1885 | ||
1886 | if (*ptr == '\0') | |
1887 | return (1); | |
1888 | ||
58e8cf34 | 1889 | LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"", |
4a64fdb7 | 1890 | con->http.fd, ptr); |
1891 | ||
a74b005d | 1892 | while (*ptr != '\0') |
1893 | { | |
4a64fdb7 | 1894 | while (isspace(*ptr) || *ptr == ';') |
a74b005d | 1895 | ptr ++; |
1896 | ||
1897 | if (strncasecmp(ptr, "length=", 7) == 0) | |
1898 | { | |
1899 | ptr += 7; | |
1900 | size = atoi(ptr); | |
1901 | ||
1902 | while (isdigit(*ptr)) | |
1903 | ptr ++; | |
1904 | } | |
4a64fdb7 | 1905 | else if (isalpha(*ptr)) |
a74b005d | 1906 | { |
1907 | date = httpGetDateTime(ptr); | |
4a64fdb7 | 1908 | while (*ptr != '\0' && *ptr != ';') |
a74b005d | 1909 | ptr ++; |
1910 | } | |
1911 | } | |
1912 | ||
58e8cf34 | 1913 | LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d", |
aa7e125a | 1914 | con->http.fd, size, (int)filestats->st_size, (int)date, |
1915 | (int)filestats->st_mtime); | |
4a64fdb7 | 1916 | |
a74b005d | 1917 | return ((size != filestats->st_size && size != 0) || |
4a64fdb7 | 1918 | (date < filestats->st_mtime && date != 0) || |
1919 | (size == 0 && date == 0)); | |
a74b005d | 1920 | } |
1921 | ||
1922 | ||
1923 | /* | |
d2122fde | 1924 | * 'decode_auth()' - Decode an authorization string. |
a74b005d | 1925 | */ |
1926 | ||
93894a43 | 1927 | static void |
d2122fde | 1928 | decode_auth(client_t *con) /* I - Client to decode to */ |
a74b005d | 1929 | { |
d2122fde | 1930 | char *s, /* Authorization string */ |
1931 | value[1024]; /* Value string */ | |
1932 | const char *username; /* Certificate username */ | |
a74b005d | 1933 | |
a74b005d | 1934 | |
1935 | /* | |
b0d58d07 | 1936 | * Decode the string... |
a74b005d | 1937 | */ |
1938 | ||
f3d580b9 | 1939 | s = con->http.fields[HTTP_FIELD_AUTHORIZATION]; |
f3d580b9 | 1940 | |
aa7e125a | 1941 | LogMessage(L_DEBUG2, "decode_auth(%p): Authorization string = \"%s\"", |
27eba2dd | 1942 | con, s); |
69f14ebb | 1943 | |
d2122fde | 1944 | if (strncmp(s, "Basic", 5) == 0) |
1945 | { | |
1946 | s += 5; | |
1947 | while (isspace(*s)) | |
1948 | s ++; | |
f3d580b9 | 1949 | |
d2122fde | 1950 | httpDecode64(value, s); |
a74b005d | 1951 | |
d2122fde | 1952 | /* |
1953 | * Pull the username and password out... | |
1954 | */ | |
b0d58d07 | 1955 | |
d2122fde | 1956 | if ((s = strchr(value, ':')) == NULL) |
1957 | { | |
5ea8888e | 1958 | LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"", |
d2122fde | 1959 | con->http.fd, value); |
1960 | return; | |
1961 | } | |
1962 | ||
1963 | *s++ = '\0'; | |
b0d58d07 | 1964 | |
d2122fde | 1965 | strncpy(con->username, value, sizeof(con->username) - 1); |
1966 | con->username[sizeof(con->username) - 1] = '\0'; | |
b0d58d07 | 1967 | |
d2122fde | 1968 | strncpy(con->password, s, sizeof(con->password) - 1); |
1969 | con->password[sizeof(con->password) - 1] = '\0'; | |
1970 | } | |
1971 | else if (strncmp(s, "Local", 5) == 0) | |
1972 | { | |
1973 | s += 5; | |
1974 | while (isspace(*s)) | |
1975 | s ++; | |
b0d58d07 | 1976 | |
d2122fde | 1977 | if ((username = FindCert(s)) != NULL) |
a6988fb1 | 1978 | { |
1979 | strncpy(con->username, username, sizeof(con->username) - 1); | |
1980 | con->username[sizeof(con->username) - 1] = '\0'; | |
1981 | } | |
d2122fde | 1982 | } |
83e740a5 | 1983 | else if (strncmp(s, "Digest", 5) == 0) |
1984 | { | |
1985 | /* | |
1986 | * Get the username and password from the Digest attributes... | |
1987 | */ | |
1988 | ||
1989 | if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username", | |
1990 | value)) | |
1991 | { | |
1992 | strncpy(con->username, value, sizeof(con->username) - 1); | |
1993 | con->username[sizeof(con->username) - 1] = '\0'; | |
1994 | } | |
1995 | ||
1996 | if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response", | |
1997 | value)) | |
1998 | { | |
1999 | strncpy(con->password, value, sizeof(con->password) - 1); | |
2000 | con->password[sizeof(con->password) - 1] = '\0'; | |
2001 | } | |
2002 | } | |
41637133 | 2003 | |
27eba2dd | 2004 | LogMessage(L_DEBUG2, "decode_auth() %d username=\"%s\"", |
41637133 | 2005 | con->http.fd, con->username); |
a74b005d | 2006 | } |
2007 | ||
2008 | ||
e31bfb6e | 2009 | /* |
2010 | * 'get_file()' - Get a filename and state info. | |
2011 | */ | |
2012 | ||
2013 | static char * /* O - Real filename */ | |
2014 | get_file(client_t *con, /* I - Client connection */ | |
2015 | struct stat *filestats)/* O - File information */ | |
2016 | { | |
2017 | int status; /* Status of filesystem calls */ | |
2018 | char *params; /* Pointer to parameters in URI */ | |
2019 | static char filename[1024]; /* Filename buffer */ | |
2020 | ||
2021 | ||
2022 | /* | |
2023 | * Need to add DocumentRoot global... | |
2024 | */ | |
2025 | ||
f3d580b9 | 2026 | if (strncmp(con->uri, "/ppd/", 5) == 0) |
04de52f8 | 2027 | snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri); |
753453e4 | 2028 | else if (strncmp(con->uri, "/admin/conf/", 12) == 0) |
2029 | snprintf(filename, sizeof(filename), "%s%s", ServerRoot, con->uri + 11); | |
f3d580b9 | 2030 | else if (con->language != NULL) |
04de52f8 | 2031 | snprintf(filename, sizeof(filename), "%s/%s%s", DocumentRoot, con->language->language, |
e31bfb6e | 2032 | con->uri); |
2033 | else | |
04de52f8 | 2034 | snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri); |
e31bfb6e | 2035 | |
2036 | if ((params = strchr(filename, '?')) != NULL) | |
2037 | *params = '\0'; | |
2038 | ||
2039 | /* | |
2040 | * Grab the status for this language; if there isn't a language-specific file | |
2041 | * then fallback to the default one... | |
2042 | */ | |
2043 | ||
2044 | if ((status = stat(filename, filestats)) != 0 && con->language != NULL) | |
2045 | { | |
2046 | /* | |
2047 | * Drop the language prefix and try the current directory... | |
2048 | */ | |
2049 | ||
753453e4 | 2050 | if (strncmp(con->uri, "/ppd/", 5) != 0 && |
2051 | strncmp(con->uri, "/admin/conf/", 12) != 0) | |
f3d580b9 | 2052 | { |
04de52f8 | 2053 | snprintf(filename, sizeof(filename), "%s%s", DocumentRoot, con->uri); |
e31bfb6e | 2054 | |
f3d580b9 | 2055 | status = stat(filename, filestats); |
2056 | } | |
e31bfb6e | 2057 | } |
2058 | ||
2059 | /* | |
2060 | * If we're found a directory, get the index.html file instead... | |
2061 | */ | |
2062 | ||
2063 | if (!status && S_ISDIR(filestats->st_mode)) | |
2064 | { | |
2065 | if (filename[strlen(filename) - 1] == '/') | |
d588a92e | 2066 | strncat(filename, "index.html", sizeof(filename)); |
e31bfb6e | 2067 | else |
d588a92e | 2068 | strncat(filename, "/index.html", sizeof(filename)); |
2069 | ||
2070 | filename[sizeof(filename) - 1] = '\0'; | |
e31bfb6e | 2071 | |
2072 | status = stat(filename, filestats); | |
2073 | } | |
2074 | ||
58e8cf34 | 2075 | LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d", |
aa7e125a | 2076 | con->http.fd, filename, status ? -1 : (int)filestats->st_size); |
e31bfb6e | 2077 | |
2078 | if (status) | |
2079 | return (NULL); | |
2080 | else | |
2081 | return (filename); | |
2082 | } | |
2083 | ||
2084 | ||
753453e4 | 2085 | /* |
2086 | * 'install_conf_file()' - Install a configuration file. | |
2087 | */ | |
2088 | ||
2089 | static http_status_t /* O - Status */ | |
2090 | install_conf_file(client_t *con) /* I - Connection */ | |
2091 | { | |
2092 | FILE *in, /* Input file */ | |
2093 | *out; /* Output file */ | |
2094 | char buffer[1024]; /* Copy buffer */ | |
2095 | int bytes; /* Number of bytes */ | |
2096 | char conffile[1024], /* Configuration filename */ | |
2097 | newfile[1024], /* New config filename */ | |
2098 | oldfile[1024]; /* Old config filename */ | |
2099 | struct stat confinfo; /* Config file info */ | |
2100 | ||
2101 | ||
2102 | /* | |
2103 | * First construct the filenames... | |
2104 | */ | |
2105 | ||
2106 | snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11); | |
2107 | snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11); | |
2108 | snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11); | |
2109 | ||
2110 | LogMessage(L_INFO, "Installing config file \"%s\"...", conffile); | |
2111 | ||
2112 | /* | |
2113 | * Get the owner, group, and permissions of the configuration file. | |
2114 | * If it doesn't exist, assign it to the User and Group in the | |
2115 | * cupsd.conf file with mode 0640 permissions. | |
2116 | */ | |
2117 | ||
2118 | if (stat(conffile, &confinfo)) | |
2119 | { | |
2120 | confinfo.st_uid = User; | |
2121 | confinfo.st_gid = Group; | |
2122 | confinfo.st_mode = 0640; | |
2123 | } | |
2124 | ||
2125 | /* | |
2126 | * Open the request file and new config file... | |
2127 | */ | |
2128 | ||
2129 | if ((in = fopen(con->filename, "rb")) == NULL) | |
2130 | { | |
2131 | LogMessage(L_ERROR, "Unable to open request file \"%s\" - %s", | |
2132 | con->filename, strerror(errno)); | |
2133 | return (HTTP_SERVER_ERROR); | |
2134 | } | |
2135 | ||
2136 | if ((out = fopen(newfile, "wb")) == NULL) | |
2137 | { | |
2138 | fclose(in); | |
2139 | LogMessage(L_ERROR, "Unable to open config file \"%s\" - %s", | |
2140 | newfile, strerror(errno)); | |
2141 | return (HTTP_SERVER_ERROR); | |
2142 | } | |
2143 | ||
2144 | fchmod(fileno(out), confinfo.st_mode); | |
2145 | fchown(fileno(out), confinfo.st_uid, confinfo.st_gid); | |
2146 | ||
2147 | /* | |
2148 | * Copy from the request to the new config file... | |
2149 | */ | |
2150 | ||
2151 | while ((bytes = fread(buffer, 1, sizeof(buffer), in)) > 0) | |
2152 | if (fwrite(buffer, 1, bytes, out) < bytes) | |
2153 | { | |
2154 | LogMessage(L_ERROR, "Unable to copy to config file \"%s\" - %s", | |
2155 | newfile, strerror(errno)); | |
2156 | ||
2157 | fclose(in); | |
2158 | fclose(out); | |
2159 | unlink(newfile); | |
2160 | ||
2161 | return (HTTP_SERVER_ERROR); | |
2162 | } | |
2163 | ||
2164 | /* | |
2165 | * Close the files... | |
2166 | */ | |
2167 | ||
2168 | fclose(in); | |
2169 | if (fclose(out)) | |
2170 | { | |
2171 | LogMessage(L_ERROR, "Error file closing config file \"%s\" - %s", | |
2172 | newfile, strerror(errno)); | |
2173 | ||
2174 | unlink(newfile); | |
2175 | ||
2176 | return (HTTP_SERVER_ERROR); | |
2177 | } | |
2178 | ||
2179 | /* | |
2180 | * Remove the request file... | |
2181 | */ | |
2182 | ||
2183 | unlink(con->filename); | |
2184 | con->filename[0] = '\0'; | |
2185 | ||
2186 | /* | |
2187 | * Unlink the old backup, rename the current config file to the backup | |
2188 | * filename, and rename the new config file to the config file name... | |
2189 | */ | |
2190 | ||
2191 | if (unlink(oldfile)) | |
2192 | if (errno != ENOENT) | |
2193 | { | |
2194 | LogMessage(L_ERROR, "Unable to remove backup config file \"%s\" - %s", | |
2195 | oldfile, strerror(errno)); | |
2196 | ||
2197 | unlink(newfile); | |
2198 | ||
2199 | return (HTTP_SERVER_ERROR); | |
2200 | } | |
2201 | ||
2202 | if (rename(conffile, oldfile)) | |
2203 | if (errno != ENOENT) | |
2204 | { | |
2205 | LogMessage(L_ERROR, "Unable to rename old config file \"%s\" - %s", | |
2206 | conffile, strerror(errno)); | |
2207 | ||
2208 | unlink(newfile); | |
2209 | ||
2210 | return (HTTP_SERVER_ERROR); | |
2211 | } | |
2212 | ||
2213 | if (rename(newfile, conffile)) | |
2214 | { | |
2215 | LogMessage(L_ERROR, "Unable to rename new config file \"%s\" - %s", | |
2216 | newfile, strerror(errno)); | |
2217 | ||
2218 | rename(oldfile, conffile); | |
2219 | unlink(newfile); | |
2220 | ||
2221 | return (HTTP_SERVER_ERROR); | |
2222 | } | |
2223 | ||
2224 | /* | |
2225 | * If the cupsd.conf file was updated, set the NeedReload flag... | |
2226 | */ | |
2227 | ||
2228 | if (strcmp(con->uri, "/admin/conf/cupsd.conf") == 0) | |
2229 | NeedReload = TRUE; | |
2230 | ||
2231 | /* | |
2232 | * Return that the file was created successfully... | |
2233 | */ | |
2234 | ||
2235 | return (HTTP_CREATED); | |
2236 | } | |
2237 | ||
2238 | ||
a74b005d | 2239 | /* |
2240 | * 'pipe_command()' - Pipe the output of a command to the remote client. | |
2241 | */ | |
2242 | ||
2243 | static int /* O - Process ID */ | |
b14d90ba | 2244 | pipe_command(client_t *con, /* I - Client connection */ |
2245 | int infile, /* I - Standard input for command */ | |
2246 | int *outfile, /* O - Standard output for command */ | |
2247 | char *command, /* I - Command to run */ | |
2248 | char *options) /* I - Options for command */ | |
a74b005d | 2249 | { |
2250 | int pid; /* Process ID */ | |
2251 | char *commptr; /* Command string pointer */ | |
082b40d2 | 2252 | int fd; /* Looping var */ |
a74b005d | 2253 | int fds[2]; /* Pipe FDs */ |
2254 | int argc; /* Number of arguments */ | |
df3f73ff | 2255 | int envc; /* Number of environment variables */ |
f63a2256 | 2256 | char argbuf[10240], /* Argument buffer */ |
b14d90ba | 2257 | *argv[100], /* Argument strings */ |
2258 | *envp[100]; /* Environment variables */ | |
20b85438 | 2259 | char lang[1024], /* LANG env variable */ |
df3f73ff | 2260 | content_length[1024], /* CONTENT_LENGTH env variable */ |
2261 | content_type[1024], /* CONTENT_TYPE env variable */ | |
2262 | ipp_port[1024], /* Default listen port */ | |
167f2f52 | 2263 | server_port[1024], /* Default server port */ |
df3f73ff | 2264 | server_name[1024], /* Default listen hostname */ |
2265 | remote_host[1024], /* REMOTE_HOST env variable */ | |
167f2f52 | 2266 | remote_user[1024], /* REMOTE_USER env variable */ |
df3f73ff | 2267 | tmpdir[1024], /* TMPDIR environment variable */ |
2268 | ldpath[1024], /* LD_LIBRARY_PATH environment variable */ | |
1cf29e95 | 2269 | nlspath[1024], /* NLSPATH environment variable */ |
cbb56a9b | 2270 | datadir[1024], /* CUPS_DATADIR environment variable */ |
245bf65d | 2271 | root[1024], /* CUPS_SERVERROOT environment variable */ |
df3f73ff | 2272 | query_string[10240]; /* QUERY_STRING env variable */ |
a74b005d | 2273 | |
05e63c18 | 2274 | |
a74b005d | 2275 | /* |
2276 | * Copy the command string... | |
2277 | */ | |
2278 | ||
b14d90ba | 2279 | strncpy(argbuf, options, sizeof(argbuf) - 1); |
a74b005d | 2280 | argbuf[sizeof(argbuf) - 1] = '\0'; |
2281 | ||
2282 | /* | |
f63a2256 | 2283 | * Parse the string; arguments can be separated by + and are terminated |
2284 | * by ?... | |
a74b005d | 2285 | */ |
2286 | ||
2287 | argv[0] = argbuf; | |
2288 | ||
bfa1abf0 | 2289 | for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++) |
f63a2256 | 2290 | if (*commptr == ' ' || *commptr == '+') |
a74b005d | 2291 | { |
2292 | *commptr++ = '\0'; | |
2293 | ||
2294 | while (*commptr == ' ') | |
2295 | commptr ++; | |
2296 | ||
2297 | if (*commptr != '\0') | |
2298 | { | |
2299 | argv[argc] = commptr; | |
2300 | argc ++; | |
2301 | } | |
2302 | ||
2303 | commptr --; | |
2304 | } | |
2305 | else if (*commptr == '%') | |
2306 | { | |
2307 | if (commptr[1] >= '0' && commptr[1] <= '9') | |
2308 | *commptr = (commptr[1] - '0') << 4; | |
2309 | else | |
2310 | *commptr = (tolower(commptr[1]) - 'a' + 10) << 4; | |
2311 | ||
2312 | if (commptr[2] >= '0' && commptr[2] <= '9') | |
2313 | *commptr |= commptr[2] - '0'; | |
2314 | else | |
2315 | *commptr |= tolower(commptr[2]) - 'a' + 10; | |
2316 | ||
2317 | strcpy(commptr + 1, commptr + 3); | |
2318 | } | |
f63a2256 | 2319 | else if (*commptr == '?') |
2320 | break; | |
a74b005d | 2321 | |
2322 | argv[argc] = NULL; | |
2323 | ||
bfa1abf0 | 2324 | if (argv[0][0] == '\0') |
2325 | argv[0] = strrchr(command, '/') + 1; | |
b14d90ba | 2326 | |
2327 | /* | |
2328 | * Setup the environment variables as needed... | |
2329 | */ | |
2330 | ||
a6988fb1 | 2331 | snprintf(lang, sizeof(lang), "LANG=%s", |
2332 | con->language ? con->language->language : "C"); | |
99de6da0 | 2333 | #ifdef AF_INET6 |
2334 | if (con->http.hostaddr.addr.sa_family == AF_INET6) | |
2335 | { | |
2336 | sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.ipv6.sin6_port)); | |
2337 | sprintf(server_port, "SERVER_PORT=%d", | |
2338 | ntohs(con->http.hostaddr.ipv6.sin6_port)); | |
2339 | } | |
2340 | else | |
2341 | #endif /* AF_INET6 */ | |
2342 | { | |
2343 | sprintf(ipp_port, "IPP_PORT=%d", ntohs(con->http.hostaddr.ipv4.sin_port)); | |
2344 | sprintf(server_port, "SERVER_PORT=%d", | |
2345 | ntohs(con->http.hostaddr.ipv4.sin_port)); | |
2346 | } | |
2347 | ||
04de52f8 | 2348 | snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", ServerName); |
2349 | snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", con->http.hostname); | |
2350 | snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username); | |
2351 | snprintf(tmpdir, sizeof(tmpdir), "TMPDIR=%s", TempDir); | |
2352 | snprintf(datadir, sizeof(datadir), "CUPS_DATADIR=%s", DataDir); | |
2353 | snprintf(root, sizeof(root), "CUPS_SERVERROOT=%s", ServerRoot); | |
df3f73ff | 2354 | |
2355 | if (getenv("LD_LIBRARY_PATH") != NULL) | |
1cf29e95 | 2356 | snprintf(ldpath, sizeof(ldpath), "LD_LIBRARY_PATH=%s", |
2357 | getenv("LD_LIBRARY_PATH")); | |
2358 | else if (getenv("DYLD_LIBRARY_PATH") != NULL) | |
2359 | snprintf(ldpath, sizeof(ldpath), "DYLD_LIBRARY_PATH=%s", | |
2360 | getenv("DYLD_LIBRARY_PATH")); | |
df3f73ff | 2361 | else |
2362 | ldpath[0] = '\0'; | |
7f0679f5 | 2363 | |
1cf29e95 | 2364 | if (getenv("NLSPATH") != NULL) |
2365 | snprintf(nlspath, sizeof(nlspath), "NLSPATH=%s", getenv("NLSPATH")); | |
2366 | else | |
2367 | nlspath[0] = '\0'; | |
2368 | ||
7f0679f5 | 2369 | envp[0] = "PATH=/bin:/usr/bin"; |
99de6da0 | 2370 | envp[1] = "SERVER_SOFTWARE=CUPS/1.2"; |
7f0679f5 | 2371 | envp[2] = "GATEWAY_INTERFACE=CGI/1.1"; |
2372 | envp[3] = "SERVER_PROTOCOL=HTTP/1.1"; | |
57c77867 | 2373 | envp[4] = ipp_port; |
2374 | envp[5] = server_name; | |
2375 | envp[6] = server_port; | |
2376 | envp[7] = remote_host; | |
2377 | envp[8] = remote_user; | |
2378 | envp[9] = lang; | |
05e63c18 | 2379 | envp[10] = TZ; |
57c77867 | 2380 | envp[11] = tmpdir; |
df3f73ff | 2381 | envp[12] = datadir; |
245bf65d | 2382 | envp[13] = root; |
df3f73ff | 2383 | |
245bf65d | 2384 | envc = 14; |
df3f73ff | 2385 | |
2386 | if (ldpath[0]) | |
2387 | envp[envc ++] = ldpath; | |
b14d90ba | 2388 | |
1cf29e95 | 2389 | if (nlspath[0]) |
2390 | envp[envc ++] = nlspath; | |
2391 | ||
b14d90ba | 2392 | if (con->operation == HTTP_GET) |
2393 | { | |
df3f73ff | 2394 | envp[envc ++] = "REQUEST_METHOD=GET"; |
f63a2256 | 2395 | |
2396 | if (*commptr) | |
2397 | { | |
2398 | /* | |
2399 | * Add GET form variables after ?... | |
2400 | */ | |
2401 | ||
2402 | *commptr++ = '\0'; | |
2403 | ||
04de52f8 | 2404 | snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr); |
df3f73ff | 2405 | envp[envc ++] = query_string; |
f63a2256 | 2406 | } |
b14d90ba | 2407 | } |
2408 | else | |
2409 | { | |
f63a2256 | 2410 | sprintf(content_length, "CONTENT_LENGTH=%d", con->bytes); |
04de52f8 | 2411 | snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s", |
970017a4 | 2412 | con->http.fields[HTTP_FIELD_CONTENT_TYPE]); |
b14d90ba | 2413 | |
df3f73ff | 2414 | envp[envc ++] = "REQUEST_METHOD=POST"; |
2415 | envp[envc ++] = content_length; | |
2416 | envp[envc ++] = content_type; | |
b14d90ba | 2417 | } |
2418 | ||
30d60919 | 2419 | /* |
2420 | * Tell the CGI if we are using encryption... | |
2421 | */ | |
2422 | ||
2423 | if (con->http.encryption >= HTTP_ENCRYPT_REQUIRED) | |
2424 | envp[envc ++] = "HTTPS=ON"; | |
2425 | ||
df3f73ff | 2426 | envp[envc] = NULL; |
2427 | ||
a74b005d | 2428 | /* |
2429 | * Create a pipe for the output... | |
2430 | */ | |
2431 | ||
2432 | if (pipe(fds)) | |
1d6c68fb | 2433 | { |
5ea8888e | 2434 | LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s", |
1d6c68fb | 2435 | argv[0], strerror(errno)); |
a74b005d | 2436 | return (0); |
1d6c68fb | 2437 | } |
a74b005d | 2438 | |
2439 | /* | |
1d6c68fb | 2440 | * Then execute the command... |
a74b005d | 2441 | */ |
2442 | ||
2443 | if ((pid = fork()) == 0) | |
2444 | { | |
2445 | /* | |
2446 | * Child comes here... Close stdin if necessary and dup the pipe to stdout. | |
2447 | */ | |
2448 | ||
5aeb433c | 2449 | if (getuid() == 0) |
2450 | { | |
2451 | /* | |
753453e4 | 2452 | * Running as root, so change to a non-priviledged user... |
5aeb433c | 2453 | */ |
2454 | ||
2455 | if (setgid(Group)) | |
2456 | exit(errno); | |
2457 | ||
2458 | if (setuid(User)) | |
2459 | exit(errno); | |
2460 | } | |
2461 | ||
2462 | /* | |
2463 | * Reset group membership to just the main one we belong to. | |
2464 | */ | |
2465 | ||
2466 | setgroups(0, NULL); | |
2467 | ||
2468 | /* | |
a9775e85 | 2469 | * Update stdin/stdout/stderr... |
5aeb433c | 2470 | */ |
b14d90ba | 2471 | |
a74b005d | 2472 | if (infile) |
2473 | { | |
2474 | close(0); | |
753453e4 | 2475 | if (dup(infile) < 0) |
2476 | exit(errno); | |
a74b005d | 2477 | } |
2478 | ||
2479 | close(1); | |
753453e4 | 2480 | if (dup(fds[1]) < 0) |
2481 | exit(errno); | |
a74b005d | 2482 | |
a9775e85 | 2483 | close(2); |
2484 | open("/dev/null", O_WRONLY); | |
2485 | ||
082b40d2 | 2486 | /* |
2487 | * Close extra file descriptors... | |
2488 | */ | |
2489 | ||
93336dbc | 2490 | for (fd = 3; fd < MaxFDs; fd ++) |
082b40d2 | 2491 | close(fd); |
2492 | ||
2493 | /* | |
2494 | * Change umask to restrict permissions on created files... | |
2495 | */ | |
2496 | ||
2497 | umask(077); | |
a74b005d | 2498 | |
2499 | /* | |
2500 | * Execute the pipe program; if an error occurs, exit with status 1... | |
2501 | */ | |
2502 | ||
b14d90ba | 2503 | execve(command, argv, envp); |
96df88bb | 2504 | exit(errno); |
a74b005d | 2505 | return (0); |
2506 | } | |
2507 | else if (pid < 0) | |
2508 | { | |
2509 | /* | |
2510 | * Error - can't fork! | |
2511 | */ | |
2512 | ||
5ea8888e | 2513 | LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0], |
1d6c68fb | 2514 | strerror(errno)); |
2515 | ||
a74b005d | 2516 | close(fds[0]); |
2517 | close(fds[1]); | |
2518 | return (0); | |
2519 | } | |
2520 | else | |
2521 | { | |
2522 | /* | |
2523 | * Fork successful - return the PID... | |
2524 | */ | |
2525 | ||
f63a2256 | 2526 | AddCert(pid, con->username); |
2527 | ||
753453e4 | 2528 | LogMessage(L_DEBUG, "CGI %s started - PID = %d", command, pid); |
1d6c68fb | 2529 | |
a74b005d | 2530 | *outfile = fds[0]; |
2531 | close(fds[1]); | |
2532 | ||
2533 | return (pid); | |
2534 | } | |
2535 | } | |
2536 | ||
2537 | ||
93894a43 | 2538 | /* |
aa7e125a | 2539 | * End of "$Id: client.c,v 1.91.2.11 2002/05/15 01:57:01 mike Exp $". |
a74b005d | 2540 | */ |