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