]>
Commit | Line | Data |
---|---|---|
a74b005d | 1 | /* |
b2e10895 | 2 | * "$Id$" |
a74b005d | 3 | * |
4 | * Client routines for the Common UNIX Printing System (CUPS) scheduler. | |
5 | * | |
c9d3f842 | 6 | * Copyright 1997-2005 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 |
8650fcf2 | 18 | * Hollywood, Maryland 20636 USA |
a74b005d | 19 | * |
6bb5a528 | 20 | * Voice: (301) 373-9600 |
a74b005d | 21 | * EMail: cups-info@cups.org |
22 | * WWW: http://www.cups.org | |
23 | * | |
24 | * Contents: | |
25 | * | |
f3e786fc | 26 | * cupsdAcceptClient() - Accept a new client. |
27 | * cupsdCloseAllClients() - Close all remote clients immediately. | |
28 | * cupsdCloseClient() - Close a remote client. | |
29 | * cupsdEncryptClient() - Enable encryption for the client... | |
30 | * cupsdIsCGI() - Is the resource a CGI script/program? | |
31 | * cupsdReadClient() - Read data from a client. | |
32 | * cupsdSendCommand() - Send output from a command via HTTP. | |
33 | * cupsdSendError() - Send an error message via HTTP. | |
34 | * cupsdSendFile() - Send a file via HTTP. | |
35 | * cupsdSendHeader() - Send an HTTP request. | |
36 | * cupsdUpdateCGI() - Read status messages from CGI scripts and programs. | |
37 | * cupsdWriteClient() - Write data to a client as needed. | |
38 | * check_if_modified() - Decode an "If-Modified-Since" line. | |
39 | * decode_auth() - Decode an authorization string. | |
40 | * get_file() - Get a filename and state info. | |
41 | * install_conf_file() - Install a configuration file. | |
42 | * is_path_absolute() - Is a path absolute and free of relative elements. | |
43 | * pipe_command() - Pipe the output of a command to the remote client. | |
44 | * CDSAReadFunc() - Read function for CDSA decryption code. | |
45 | * CDSAWriteFunc() - Write function for CDSA encryption code. | |
a74b005d | 46 | */ |
47 | ||
48 | /* | |
49 | * Include necessary headers... | |
50 | */ | |
51 | ||
3b602493 | 52 | #include <cups/http-private.h> |
a74b005d | 53 | #include "cupsd.h" |
bd917997 | 54 | |
a74b005d | 55 | |
56 | /* | |
57 | * Local functions... | |
58 | */ | |
59 | ||
589eb420 | 60 | static int check_if_modified(cupsd_client_t *con, |
753453e4 | 61 | struct stat *filestats); |
589eb420 | 62 | static void decode_auth(cupsd_client_t *con); |
63 | static char *get_file(cupsd_client_t *con, struct stat *filestats, | |
6db7190f | 64 | char *filename, int len); |
589eb420 | 65 | static http_status_t install_conf_file(cupsd_client_t *con); |
3780dd72 | 66 | static int is_path_absolute(const char *path); |
589eb420 | 67 | static int pipe_command(cupsd_client_t *con, int infile, int *outfile, |
b38d93df | 68 | char *command, char *options, int root); |
a74b005d | 69 | |
dcfcaeac | 70 | #ifdef HAVE_CDSASSL |
df28ec49 | 71 | static OSStatus CDSAReadFunc(SSLConnectionRef connection, void *data, |
dcfcaeac | 72 | size_t *dataLength); |
df28ec49 | 73 | static OSStatus CDSAWriteFunc(SSLConnectionRef connection, |
dcfcaeac | 74 | const void *data, size_t *dataLength); |
75 | #endif /* HAVE_CDSASSL */ | |
76 | ||
a74b005d | 77 | |
78 | /* | |
589eb420 | 79 | * 'cupsdAcceptClient()' - Accept a new client. |
a74b005d | 80 | */ |
81 | ||
82 | void | |
f3e786fc | 83 | cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */ |
a74b005d | 84 | { |
f3e786fc | 85 | int i; /* Looping var */ |
86 | int count; /* Count of connections on a host */ | |
87 | int val; /* Parameter value */ | |
88 | cupsd_client_t *con; /* New client pointer */ | |
89 | const struct hostent *host; /* Host entry for address */ | |
90 | char *hostname; /* Hostname for address */ | |
91 | http_addr_t temp; /* Temporary address variable */ | |
92 | static time_t last_dos = 0; /* Time of last DoS attack */ | |
a74b005d | 93 | |
94 | ||
f3e786fc | 95 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
96 | "cupsdAcceptClient(lis=%p) %d NumClients = %d", | |
97 | lis, lis->fd, NumClients); | |
bfa1abf0 | 98 | |
12aa0c1f | 99 | /* |
100 | * Make sure we don't have a full set of clients already... | |
101 | */ | |
102 | ||
103 | if (NumClients == MaxClients) | |
104 | return; | |
105 | ||
a74b005d | 106 | /* |
107 | * Get a pointer to the next available client... | |
108 | */ | |
109 | ||
110 | con = Clients + NumClients; | |
111 | ||
589eb420 | 112 | memset(con, 0, sizeof(cupsd_client_t)); |
a74b005d | 113 | con->http.activity = time(NULL); |
819347a7 | 114 | con->file = -1; |
a74b005d | 115 | |
116 | /* | |
117 | * Accept the client and get the remote address... | |
118 | */ | |
119 | ||
120 | val = sizeof(struct sockaddr_in); | |
121 | ||
2f6d083c | 122 | if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr), |
123 | &val)) < 0) | |
a74b005d | 124 | { |
f3e786fc | 125 | cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to accept client connection - %s.", |
126 | strerror(errno)); | |
a74b005d | 127 | return; |
128 | } | |
129 | ||
99de6da0 | 130 | #ifdef AF_INET6 |
131 | if (lis->address.addr.sa_family == AF_INET6) | |
132 | con->http.hostaddr.ipv6.sin6_port = lis->address.ipv6.sin6_port; | |
133 | else | |
134 | #endif /* AF_INET6 */ | |
f3e786fc | 135 | if (lis->address.addr.sa_family == AF_INET) |
136 | con->http.hostaddr.ipv4.sin_port = lis->address.ipv4.sin_port; | |
a74b005d | 137 | |
1060c9b6 | 138 | /* |
139 | * Check the number of clients on the same address... | |
140 | */ | |
141 | ||
142 | for (i = 0, count = 0; i < NumClients; i ++) | |
928c0377 | 143 | if (httpAddrEqual(&(Clients[i].http.hostaddr), &(con->http.hostaddr))) |
1060c9b6 | 144 | { |
145 | count ++; | |
146 | if (count >= MaxClientsPerHost) | |
147 | break; | |
148 | } | |
149 | ||
150 | if (count >= MaxClientsPerHost) | |
151 | { | |
f0ad8cdc | 152 | if ((time(NULL) - last_dos) >= 60) |
153 | { | |
154 | last_dos = time(NULL); | |
f3e786fc | 155 | cupsdLogMessage(CUPSD_LOG_WARN, |
156 | "Possible DoS attack - more than %d clients connecting from %s!", | |
157 | MaxClientsPerHost, Clients[i].http.hostname); | |
f0ad8cdc | 158 | } |
1060c9b6 | 159 | |
160 | #ifdef WIN32 | |
161 | closesocket(con->http.fd); | |
162 | #else | |
163 | close(con->http.fd); | |
164 | #endif /* WIN32 */ | |
165 | ||
166 | return; | |
167 | } | |
b2e10895 | 168 | |
1060c9b6 | 169 | /* |
170 | * Get the hostname or format the IP address as needed... | |
171 | */ | |
172 | ||
06415a96 | 173 | if (httpAddrLocalhost(&(con->http.hostaddr))) |
174 | { | |
175 | /* | |
176 | * Map accesses from the loopback interface to "localhost"... | |
177 | */ | |
178 | ||
179 | strlcpy(con->http.hostname, "localhost", sizeof(con->http.hostname)); | |
90aec4f7 | 180 | hostname = con->http.hostname; |
06415a96 | 181 | } |
182 | else if (httpAddrEqual(&(con->http.hostaddr), &ServerAddr)) | |
2bdd1992 | 183 | { |
184 | /* | |
185 | * Map accesses from the same host to the server name. | |
186 | */ | |
187 | ||
def978d5 | 188 | strlcpy(con->http.hostname, ServerName, sizeof(con->http.hostname)); |
90aec4f7 | 189 | hostname = con->http.hostname; |
2bdd1992 | 190 | } |
7c298ddc | 191 | else if (HostNameLookups) |
192 | hostname = httpAddrLookup(&(con->http.hostaddr), con->http.hostname, | |
193 | sizeof(con->http.hostname)); | |
194 | else | |
195 | { | |
196 | hostname = NULL; | |
197 | httpAddrString(&(con->http.hostaddr), con->http.hostname, | |
198 | sizeof(con->http.hostname)); | |
199 | } | |
27eba2dd | 200 | |
18fe941f | 201 | if (hostname == NULL && HostNameLookups == 2) |
99de6da0 | 202 | { |
203 | /* | |
204 | * Can't have an unresolved IP address with double-lookups enabled... | |
205 | */ | |
27eba2dd | 206 | |
f3e786fc | 207 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
208 | "cupsdAcceptClient: Closing connection %d...", | |
209 | con->http.fd); | |
6cd03542 | 210 | |
27eba2dd | 211 | #ifdef WIN32 |
99de6da0 | 212 | closesocket(con->http.fd); |
27eba2dd | 213 | #else |
99de6da0 | 214 | close(con->http.fd); |
27eba2dd | 215 | #endif /* WIN32 */ |
216 | ||
f3e786fc | 217 | cupsdLogMessage(CUPSD_LOG_WARN, |
218 | "Name lookup failed - connection from %s closed!", | |
219 | con->http.hostname); | |
99de6da0 | 220 | return; |
27eba2dd | 221 | } |
a74b005d | 222 | |
27eba2dd | 223 | if (HostNameLookups == 2) |
224 | { | |
225 | /* | |
226 | * Do double lookups as needed... | |
227 | */ | |
228 | ||
753453e4 | 229 | if ((host = httpGetHostByName(con->http.hostname)) != NULL) |
27eba2dd | 230 | { |
231 | /* | |
99de6da0 | 232 | * See if the hostname maps to the same IP address... |
27eba2dd | 233 | */ |
234 | ||
99de6da0 | 235 | if (host->h_addrtype != con->http.hostaddr.addr.sa_family) |
27eba2dd | 236 | { |
237 | /* | |
99de6da0 | 238 | * Not the right type of address... |
27eba2dd | 239 | */ |
240 | ||
46490d9d | 241 | host = NULL; |
27eba2dd | 242 | } |
243 | else | |
244 | { | |
245 | /* | |
246 | * Compare all of the addresses against this one... | |
247 | */ | |
248 | ||
46490d9d | 249 | for (i = 0; host->h_addr_list[i]; i ++) |
99de6da0 | 250 | { |
251 | httpAddrLoad(host, 0, i, &temp); | |
252 | ||
253 | if (httpAddrEqual(&(con->http.hostaddr), &temp)) | |
27eba2dd | 254 | break; |
99de6da0 | 255 | } |
27eba2dd | 256 | |
46490d9d | 257 | if (!host->h_addr_list[i]) |
258 | host = NULL; | |
27eba2dd | 259 | } |
260 | } | |
261 | ||
46490d9d | 262 | if (host == NULL) |
27eba2dd | 263 | { |
264 | /* | |
265 | * Can't have a hostname that doesn't resolve to the same IP address | |
266 | * with double-lookups enabled... | |
267 | */ | |
268 | ||
f3e786fc | 269 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
270 | "cupsdAcceptClient: Closing connection %d...", | |
271 | con->http.fd); | |
6cd03542 | 272 | |
27eba2dd | 273 | #ifdef WIN32 |
274 | closesocket(con->http.fd); | |
275 | #else | |
276 | close(con->http.fd); | |
277 | #endif /* WIN32 */ | |
278 | ||
f3e786fc | 279 | cupsdLogMessage(CUPSD_LOG_WARN, |
280 | "IP lookup failed - connection from %s closed!", | |
281 | con->http.hostname); | |
27eba2dd | 282 | return; |
283 | } | |
284 | } | |
285 | ||
99de6da0 | 286 | #ifdef AF_INET6 |
287 | if (con->http.hostaddr.addr.sa_family == AF_INET6) | |
f3e786fc | 288 | cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv6)", |
289 | con->http.fd, con->http.hostname, | |
290 | ntohs(con->http.hostaddr.ipv6.sin6_port)); | |
99de6da0 | 291 | else |
292 | #endif /* AF_INET6 */ | |
f3e786fc | 293 | #ifdef AF_LOCAL |
294 | if (con->http.hostaddr.addr.sa_family == AF_LOCAL) | |
295 | cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s (Domain)", | |
296 | con->http.fd, con->http.hostname); | |
297 | else | |
298 | #endif /* AF_LOCAL */ | |
299 | cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv4)", | |
300 | con->http.fd, con->http.hostname, | |
301 | ntohs(con->http.hostaddr.ipv4.sin_port)); | |
a74b005d | 302 | |
e5c4f5a1 | 303 | /* |
304 | * Get the local address the client connected to... | |
305 | */ | |
306 | ||
307 | i = sizeof(temp); | |
3fd04f12 | 308 | if (getsockname(con->http.fd, (struct sockaddr *)&temp, &i)) |
e5c4f5a1 | 309 | { |
f3e786fc | 310 | cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to get local address - %s", |
311 | strerror(errno)); | |
e5c4f5a1 | 312 | |
313 | strcpy(con->servername, "localhost"); | |
314 | con->serverport = LocalPort; | |
315 | } | |
316 | else | |
317 | { | |
318 | #ifdef AF_INET6 | |
319 | if (temp.addr.sa_family == AF_INET6) | |
320 | { | |
321 | httpAddrLookup(&temp, con->servername, sizeof(con->servername)); | |
322 | con->serverport = ntohs(lis->address.ipv6.sin6_port); | |
323 | } | |
324 | else | |
325 | #endif /* AF_INET6 */ | |
326 | if (temp.addr.sa_family == AF_INET) | |
327 | { | |
328 | httpAddrLookup(&temp, con->servername, sizeof(con->servername)); | |
329 | con->serverport = ntohs(lis->address.ipv4.sin_port); | |
330 | } | |
331 | else | |
332 | { | |
333 | strcpy(con->servername, "localhost"); | |
334 | con->serverport = LocalPort; | |
335 | } | |
336 | } | |
337 | ||
f3e786fc | 338 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
339 | "cupsdAcceptClient: %d connected to server on %s:%d", | |
340 | con->http.fd, con->servername, con->serverport); | |
341 | ||
9ae34eb7 | 342 | /* |
343 | * Using TCP_NODELAY improves responsiveness, especially on systems | |
344 | * with a slow loopback interface... Since we write large buffers | |
345 | * when sending print files and requests, there shouldn't be any | |
346 | * performance penalty for this... | |
347 | */ | |
348 | ||
349 | val = 1; | |
350 | setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); | |
351 | ||
a74b005d | 352 | /* |
2e32fcdb | 353 | * Close this file on all execs... |
a74b005d | 354 | */ |
355 | ||
b2e10895 | 356 | fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC); |
a74b005d | 357 | |
2e32fcdb | 358 | /* |
359 | * Add the socket to the select() input mask. | |
360 | */ | |
361 | ||
f3e786fc | 362 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
363 | "cupsdAcceptClient: Adding fd %d to InputSet...", | |
364 | con->http.fd); | |
f3bc1068 | 365 | FD_SET(con->http.fd, InputSet); |
a74b005d | 366 | |
367 | NumClients ++; | |
368 | ||
369 | /* | |
370 | * Temporarily suspend accept()'s until we lose a client... | |
371 | */ | |
372 | ||
997edb40 | 373 | if (NumClients == MaxClients) |
589eb420 | 374 | cupsdPauseListening(); |
2a0ef17a | 375 | |
bcf61448 | 376 | #ifdef HAVE_SSL |
2a0ef17a | 377 | /* |
0b50ddbc | 378 | * See if we are connecting on a secure port... |
2a0ef17a | 379 | */ |
380 | ||
0b50ddbc | 381 | if (lis->encryption == HTTP_ENCRYPT_ALWAYS) |
2a0ef17a | 382 | { |
383 | /* | |
384 | * https connection; go secure... | |
385 | */ | |
386 | ||
b92c5a4f | 387 | con->http.encryption = HTTP_ENCRYPT_ALWAYS; |
388 | ||
589eb420 | 389 | cupsdEncryptClient(con); |
2a0ef17a | 390 | } |
84f8a991 | 391 | else |
392 | con->auto_ssl = 1; | |
bcf61448 | 393 | #endif /* HAVE_SSL */ |
a74b005d | 394 | } |
395 | ||
396 | ||
397 | /* | |
589eb420 | 398 | * 'cupsdCloseAllClients()' - Close all remote clients immediately. |
a74b005d | 399 | */ |
400 | ||
401 | void | |
589eb420 | 402 | cupsdCloseAllClients(void) |
a74b005d | 403 | { |
404 | while (NumClients > 0) | |
589eb420 | 405 | cupsdCloseClient(Clients); |
a74b005d | 406 | } |
407 | ||
408 | ||
409 | /* | |
589eb420 | 410 | * 'cupsdCloseClient()' - Close a remote client. |
a74b005d | 411 | */ |
412 | ||
f3e786fc | 413 | int /* O - 1 if partial close, 0 if fully closed */ |
589eb420 | 414 | cupsdCloseClient(cupsd_client_t *con) /* I - Client to close */ |
a74b005d | 415 | { |
f3e786fc | 416 | int partial; /* Do partial close for SSL? */ |
dcfcaeac | 417 | #if defined(HAVE_LIBSSL) |
f3e786fc | 418 | SSL_CTX *context; /* Context for encryption */ |
419 | SSL *conn; /* Connection for encryption */ | |
420 | unsigned long error; /* Error code */ | |
dcfcaeac | 421 | #elif defined(HAVE_GNUTLS) |
f3e786fc | 422 | http_tls_t *conn; /* TLS connection information */ |
423 | int error; /* Error code */ | |
dcfcaeac | 424 | gnutls_certificate_server_credentials *credentials; |
f3e786fc | 425 | /* TLS credentials */ |
99baf768 | 426 | #elif defined(HAVE_CDSASSL) |
f3e786fc | 427 | int status; /* Error status */ |
717a318c | 428 | #endif /* HAVE_LIBSSL */ |
a74b005d | 429 | |
430 | ||
f3e786fc | 431 | cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCloseClient: %d", con->http.fd); |
a74b005d | 432 | |
e7bede57 | 433 | /* |
434 | * Flush pending writes before closing... | |
435 | */ | |
436 | ||
ad4be4c3 | 437 | httpFlushWrite(HTTP(con)); |
e7bede57 | 438 | |
4cdd993e | 439 | partial = 0; |
fc757c63 | 440 | |
bcf61448 | 441 | #ifdef HAVE_SSL |
2a0ef17a | 442 | /* |
443 | * Shutdown encryption as needed... | |
444 | */ | |
445 | ||
446 | if (con->http.tls) | |
447 | { | |
4cdd993e | 448 | partial = 1; |
449 | ||
dcfcaeac | 450 | # ifdef HAVE_LIBSSL |
2a0ef17a | 451 | conn = (SSL *)(con->http.tls); |
452 | context = SSL_get_SSL_CTX(conn); | |
453 | ||
c3c5af5e | 454 | switch (SSL_shutdown(conn)) |
455 | { | |
456 | case 1 : | |
f3e786fc | 457 | cupsdLogMessage(CUPSD_LOG_INFO, |
458 | "cupsdCloseClient: SSL shutdown successful!"); | |
c3c5af5e | 459 | break; |
460 | case -1 : | |
f3e786fc | 461 | cupsdLogMessage(CUPSD_LOG_ERROR, |
462 | "cupsdCloseClient: Fatal error during SSL shutdown!"); | |
c3c5af5e | 463 | default : |
464 | while ((error = ERR_get_error()) != 0) | |
f3e786fc | 465 | cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCloseClient: %s", |
466 | ERR_error_string(error, NULL)); | |
c3c5af5e | 467 | break; |
468 | } | |
469 | ||
2a0ef17a | 470 | SSL_CTX_free(context); |
471 | SSL_free(conn); | |
dcfcaeac | 472 | |
473 | # elif defined(HAVE_GNUTLS) | |
bcf61448 | 474 | conn = (http_tls_t *)(con->http.tls); |
475 | credentials = (gnutls_certificate_server_credentials *)(conn->credentials); | |
476 | ||
477 | error = gnutls_bye(conn->session, GNUTLS_SHUT_WR); | |
478 | switch (error) | |
479 | { | |
480 | case GNUTLS_E_SUCCESS: | |
f3e786fc | 481 | cupsdLogMessage(CUPSD_LOG_INFO, |
482 | "cupsdCloseClient: SSL shutdown successful!"); | |
bcf61448 | 483 | break; |
484 | default: | |
f3e786fc | 485 | cupsdLogMessage(CUPSD_LOG_ERROR, |
486 | "cupsdCloseClient: %s", gnutls_strerror(error)); | |
bcf61448 | 487 | break; |
488 | } | |
489 | ||
490 | gnutls_deinit(conn->session); | |
491 | gnutls_certificate_free_credentials(*credentials); | |
492 | free(credentials); | |
493 | free(conn); | |
dcfcaeac | 494 | |
495 | # elif defined(HAVE_CDSASSL) | |
496 | status = SSLClose((SSLContextRef)con->http.tls); | |
497 | SSLDisposeContext((SSLContextRef)con->http.tls); | |
498 | # endif /* HAVE_LIBSSL */ | |
2a0ef17a | 499 | |
500 | con->http.tls = NULL; | |
501 | } | |
dcfcaeac | 502 | #endif /* HAVE_SSL */ |
2a0ef17a | 503 | |
ed5ceafb | 504 | if (con->pipe_pid != 0) |
505 | { | |
506 | /* | |
507 | * Stop any CGI process... | |
508 | */ | |
509 | ||
f3e786fc | 510 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
511 | "cupsdCloseClient: %d Killing process ID %d...", | |
512 | con->http.fd, con->pipe_pid); | |
ed5ceafb | 513 | cupsdEndProcess(con->pipe_pid, 1); |
514 | } | |
515 | ||
516 | if (con->file >= 0) | |
517 | { | |
518 | if (FD_ISSET(con->file, InputSet)) | |
519 | { | |
f3e786fc | 520 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
521 | "cupsdCloseClient: %d Removing fd %d from InputSet...", | |
522 | con->http.fd, con->file); | |
ed5ceafb | 523 | FD_CLR(con->file, InputSet); |
524 | } | |
525 | ||
f3e786fc | 526 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
527 | "cupsdCloseClient: %d Closing data file %d.", | |
528 | con->http.fd, con->file); | |
ed5ceafb | 529 | |
530 | close(con->file); | |
531 | con->file = -1; | |
532 | } | |
533 | ||
a74b005d | 534 | /* |
535 | * Close the socket and clear the file from the input set for select()... | |
536 | */ | |
537 | ||
4cdd993e | 538 | if (con->http.fd > 0) |
882031b3 | 539 | { |
4cdd993e | 540 | if (partial) |
541 | { | |
542 | /* | |
543 | * Only do a partial close so that the encrypted client gets everything. | |
544 | */ | |
545 | ||
f3e786fc | 546 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
547 | "cupsdCloseClient: Removing fd %d from OutputSet...", | |
548 | con->http.fd); | |
4cdd993e | 549 | shutdown(con->http.fd, 0); |
550 | FD_CLR(con->http.fd, OutputSet); | |
551 | } | |
552 | else | |
553 | { | |
554 | /* | |
555 | * Shut the socket down fully... | |
556 | */ | |
557 | ||
f3e786fc | 558 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
559 | "cupsdCloseClient: Removing fd %d from InputSet and OutputSet...", | |
560 | con->http.fd); | |
4cdd993e | 561 | close(con->http.fd); |
562 | FD_CLR(con->http.fd, InputSet); | |
563 | FD_CLR(con->http.fd, OutputSet); | |
10d8ebcc | 564 | con->http.fd = -1; |
4cdd993e | 565 | } |
882031b3 | 566 | } |
a74b005d | 567 | |
4cdd993e | 568 | if (!partial) |
25135af7 | 569 | { |
4cdd993e | 570 | /* |
571 | * Free memory... | |
572 | */ | |
25135af7 | 573 | |
4cdd993e | 574 | if (con->http.input_set) |
575 | free(con->http.input_set); | |
25135af7 | 576 | |
4cdd993e | 577 | httpClearCookie(HTTP(con)); |
25135af7 | 578 | |
589eb420 | 579 | cupsdClearString(&con->filename); |
580 | cupsdClearString(&con->command); | |
581 | cupsdClearString(&con->options); | |
26bb6ac6 | 582 | |
4cdd993e | 583 | if (con->request) |
584 | { | |
585 | ippDelete(con->request); | |
586 | con->request = NULL; | |
587 | } | |
26bb6ac6 | 588 | |
4cdd993e | 589 | if (con->response) |
590 | { | |
591 | ippDelete(con->response); | |
592 | con->response = NULL; | |
593 | } | |
a74b005d | 594 | |
4cdd993e | 595 | if (con->language) |
596 | { | |
597 | cupsLangFree(con->language); | |
598 | con->language = NULL; | |
599 | } | |
a74b005d | 600 | |
4cdd993e | 601 | /* |
602 | * Re-enable new client connections if we are going back under the | |
603 | * limit... | |
604 | */ | |
605 | ||
606 | if (NumClients == MaxClients) | |
589eb420 | 607 | cupsdResumeListening(); |
4cdd993e | 608 | |
609 | /* | |
610 | * Compact the list of clients as necessary... | |
611 | */ | |
612 | ||
613 | NumClients --; | |
614 | ||
615 | if (con < (Clients + NumClients)) | |
589eb420 | 616 | memmove(con, con + 1, (Clients + NumClients - con) * sizeof(cupsd_client_t)); |
4cdd993e | 617 | } |
819347a7 | 618 | |
619 | return (partial); | |
a74b005d | 620 | } |
621 | ||
622 | ||
a75c006a | 623 | /* |
589eb420 | 624 | * 'cupsdEncryptClient()' - Enable encryption for the client... |
a75c006a | 625 | */ |
626 | ||
f3e786fc | 627 | int /* O - 1 on success, 0 on error */ |
589eb420 | 628 | cupsdEncryptClient(cupsd_client_t *con) /* I - Client to encrypt */ |
a75c006a | 629 | { |
bcf61448 | 630 | #if defined HAVE_LIBSSL |
f3e786fc | 631 | SSL_CTX *context; /* Context for encryption */ |
632 | SSL *conn; /* Connection for encryption */ | |
633 | unsigned long error; /* Error code */ | |
1c6682dd | 634 | |
635 | ||
636 | /* | |
637 | * Create the SSL context and accept the connection... | |
638 | */ | |
639 | ||
bcf61448 | 640 | context = SSL_CTX_new(SSLv23_server_method()); |
641 | ||
836c8127 | 642 | SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */ |
bcf61448 | 643 | SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM); |
644 | SSL_CTX_use_certificate_file(context, ServerCertificate, SSL_FILETYPE_PEM); | |
a75c006a | 645 | |
bcf61448 | 646 | conn = SSL_new(context); |
a75c006a | 647 | |
648 | SSL_set_fd(conn, con->http.fd); | |
649 | if (SSL_accept(conn) != 1) | |
650 | { | |
f3e786fc | 651 | cupsdLogMessage(CUPSD_LOG_ERROR, |
652 | "cupsdEncryptClient: Unable to encrypt connection from %s!", | |
653 | con->http.hostname); | |
11009aea | 654 | |
1c6682dd | 655 | while ((error = ERR_get_error()) != 0) |
f3e786fc | 656 | cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdEncryptClient: %s", |
657 | ERR_error_string(error, NULL)); | |
1c6682dd | 658 | |
a75c006a | 659 | SSL_CTX_free(context); |
660 | SSL_free(conn); | |
661 | return (0); | |
662 | } | |
663 | ||
f3e786fc | 664 | cupsdLogMessage(CUPSD_LOG_DEBUG, |
665 | "cupsdEncryptClient: %d Connection from %s now encrypted.", | |
666 | con->http.fd, con->http.hostname); | |
11009aea | 667 | |
bcf61448 | 668 | con->http.tls = conn; |
669 | return (1); | |
dcfcaeac | 670 | |
671 | #elif defined(HAVE_GNUTLS) | |
f3e786fc | 672 | http_tls_t *conn; /* TLS session object */ |
673 | int error; /* Error code */ | |
dcfcaeac | 674 | gnutls_certificate_server_credentials *credentials; |
f3e786fc | 675 | /* TLS credentials */ |
bcf61448 | 676 | |
677 | /* | |
678 | * Create the SSL object and perform the SSL handshake... | |
679 | */ | |
680 | ||
681 | conn = (http_tls_t *)malloc(sizeof(gnutls_session)); | |
dcfcaeac | 682 | |
bcf61448 | 683 | if (conn == NULL) |
684 | return (0); | |
dcfcaeac | 685 | |
bcf61448 | 686 | credentials = (gnutls_certificate_server_credentials *) |
dcfcaeac | 687 | malloc(sizeof(gnutls_certificate_server_credentials)); |
bcf61448 | 688 | if (credentials == NULL) |
689 | { | |
f3e786fc | 690 | cupsdLogMessage(CUPSD_LOG_ERROR, |
691 | "cupsdEncryptClient: Unable to encrypt connection from %s!", | |
692 | con->http.hostname); | |
693 | cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdEncryptClient: %s", strerror(errno)); | |
11009aea | 694 | |
bcf61448 | 695 | free(conn); |
696 | return (0); | |
697 | } | |
698 | ||
699 | gnutls_certificate_allocate_credentials(credentials); | |
700 | gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate, | |
701 | ServerKey, GNUTLS_X509_FMT_PEM); | |
702 | ||
703 | gnutls_init(&(conn->session), GNUTLS_SERVER); | |
704 | gnutls_set_default_priority(conn->session); | |
705 | gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials); | |
706 | gnutls_transport_set_ptr(conn->session, con->http.fd); | |
707 | ||
708 | error = gnutls_handshake(conn->session); | |
709 | ||
710 | if (error != GNUTLS_E_SUCCESS) | |
711 | { | |
f3e786fc | 712 | cupsdLogMessage(CUPSD_LOG_ERROR, |
713 | "cupsdEncryptClient: Unable to encrypt connection from %s!", | |
714 | con->http.hostname); | |
715 | cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdEncryptClient: %s", | |
716 | gnutls_strerror(error)); | |
11009aea | 717 | |
bcf61448 | 718 | gnutls_deinit(conn->session); |
719 | gnutls_certificate_free_credentials(*credentials); | |
720 | free(conn); | |
721 | free(credentials); | |
722 | return (0); | |
723 | } | |
724 | ||
f3e786fc | 725 | cupsdLogMessage(CUPSD_LOG_DEBUG, |
726 | "cupsdEncryptClient: %d Connection from %s now encrypted.", | |
727 | con->http.fd, con->http.hostname); | |
bcf61448 | 728 | |
729 | conn->credentials = credentials; | |
a75c006a | 730 | con->http.tls = conn; |
731 | return (1); | |
dcfcaeac | 732 | |
733 | #elif defined(HAVE_CDSASSL) | |
734 | OSStatus error; /* Error info */ | |
735 | SSLContextRef conn; /* New connection */ | |
736 | SSLProtocol tryVersion; /* Protocol version */ | |
737 | const char *hostName; /* Local hostname */ | |
738 | int allowExpired; /* Allow expired certificates? */ | |
739 | int allowAnyRoot; /* Allow any root certificate? */ | |
740 | SSLProtocol *negVersion; /* Negotiated protocol version */ | |
741 | SSLCipherSuite *negCipher; /* Negotiated cypher */ | |
742 | CFArrayRef *peerCerts; /* Certificates */ | |
743 | ||
744 | ||
745 | conn = NULL; | |
746 | error = SSLNewContext(true, &conn); | |
747 | allowExpired = 1; | |
748 | allowAnyRoot = 1; | |
749 | ||
750 | if (!error) | |
751 | error = SSLSetIOFuncs(conn, CDSAReadFunc, CDSAWriteFunc); | |
752 | ||
753 | if (!error) | |
754 | error = SSLSetProtocolVersion(conn, kSSLProtocol3); | |
755 | ||
756 | if (!error) | |
757 | error = SSLSetConnection(conn, (SSLConnectionRef)con->http.fd); | |
758 | ||
759 | if (!error) | |
760 | { | |
761 | hostName = ServerName; /* MRS: ??? */ | |
762 | error = SSLSetPeerDomainName(conn, hostName, strlen(hostName) + 1); | |
763 | } | |
764 | ||
765 | /* have to do these options befor setting server certs */ | |
766 | if (!error && allowExpired) | |
767 | error = SSLSetAllowsExpiredCerts(conn, true); | |
768 | ||
769 | if (!error && allowAnyRoot) | |
770 | error = SSLSetAllowsAnyRoot(conn, true); | |
771 | ||
772 | if (!error && ServerCertificatesArray != NULL) | |
773 | error = SSLSetCertificate(conn, ServerCertificatesArray); | |
774 | ||
775 | /* | |
776 | * Perform SSL/TLS handshake | |
777 | */ | |
778 | ||
779 | do | |
780 | { | |
781 | error = SSLHandshake(conn); | |
782 | } | |
783 | while (error == errSSLWouldBlock); | |
784 | ||
785 | if (error) | |
786 | { | |
f3e786fc | 787 | cupsdLogMessage(CUPSD_LOG_ERROR, |
788 | "cupsdEncryptClient: Unable to encrypt connection from %s!", | |
789 | con->http.hostname); | |
11009aea | 790 | |
f3e786fc | 791 | cupsdLogMessage(CUPSD_LOG_ERROR, |
792 | "cupsdEncryptClient: CDSA error code is %d", error); | |
dcfcaeac | 793 | |
794 | con->http.error = error; | |
795 | con->http.status = HTTP_ERROR; | |
796 | ||
797 | if (conn != NULL) | |
798 | SSLDisposeContext(conn); | |
799 | ||
800 | return (0); | |
801 | } | |
802 | ||
f3e786fc | 803 | cupsdLogMessage(CUPSD_LOG_DEBUG, |
804 | "cupsdEncryptClient: %d Connection from %s now encrypted.", | |
805 | con->http.fd, con->http.hostname); | |
11009aea | 806 | |
dcfcaeac | 807 | con->http.tls = conn; |
808 | return (1); | |
809 | ||
a75c006a | 810 | #else |
811 | return (0); | |
bcf61448 | 812 | #endif /* HAVE_GNUTLS */ |
a75c006a | 813 | } |
814 | ||
815 | ||
3721af07 | 816 | /* |
589eb420 | 817 | * 'cupsdIsCGI()' - Is the resource a CGI script/program? |
3721af07 | 818 | */ |
819 | ||
f3e786fc | 820 | int /* O - 1 = CGI, 0 = file */ |
821 | cupsdIsCGI(cupsd_client_t *con, /* I - Client connection */ | |
822 | const char *filename, /* I - Real filename */ | |
823 | struct stat *filestats, /* I - File information */ | |
824 | mime_type_t *type) /* I - MIME type */ | |
3721af07 | 825 | { |
f3e786fc | 826 | const char *options; /* Options on URL */ |
3721af07 | 827 | |
828 | ||
f3e786fc | 829 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
830 | "cupsdIsCGI(con=%p, filename=\"%s\", filestats=%p, type=%s/%s)\n", | |
831 | con, filename, filestats, type ? type->super : "unknown", | |
832 | type ? type->type : "unknown"); | |
3721af07 | 833 | |
834 | /* | |
835 | * Get the options, if any... | |
836 | */ | |
837 | ||
838 | if ((options = strchr(con->uri, '?')) != NULL) | |
839 | options ++; | |
840 | ||
841 | /* | |
842 | * Check for known types... | |
843 | */ | |
844 | ||
a89836ef | 845 | if (!type || strcasecmp(type->super, "application")) |
3721af07 | 846 | { |
f3e786fc | 847 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsCGI: Returning 0..."); |
3721af07 | 848 | return (0); |
849 | } | |
850 | ||
851 | if (!strcasecmp(type->type, "x-httpd-cgi") && | |
852 | (filestats->st_mode & 0111)) | |
853 | { | |
854 | /* | |
855 | * "application/x-httpd-cgi" is a CGI script. | |
856 | */ | |
857 | ||
589eb420 | 858 | cupsdSetString(&con->command, filename); |
3721af07 | 859 | |
860 | filename = strrchr(filename, '/') + 1; /* Filename always absolute */ | |
861 | ||
862 | if (options) | |
589eb420 | 863 | cupsdSetStringf(&con->options, "%s %s", filename, options); |
3721af07 | 864 | else |
589eb420 | 865 | cupsdSetStringf(&con->options, "%s", filename); |
3721af07 | 866 | |
f3e786fc | 867 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
868 | "cupsdIsCGI: Returning 1 with command=\"%s\" and options=\"%s\"", | |
869 | con->command, con->options); | |
3721af07 | 870 | |
871 | return (1); | |
872 | } | |
873 | #ifdef HAVE_JAVA | |
874 | else if (!strcasecmp(type->type, "x-httpd-java")) | |
875 | { | |
876 | /* | |
877 | * "application/x-httpd-java" is a Java servlet. | |
878 | */ | |
879 | ||
589eb420 | 880 | cupsdSetString(&con->command, CUPS_JAVA); |
3721af07 | 881 | |
882 | if (options) | |
589eb420 | 883 | cupsdSetStringf(&con->options, "java %s %s", filename, options); |
3721af07 | 884 | else |
589eb420 | 885 | cupsdSetStringf(&con->options, "java %s", filename); |
3721af07 | 886 | |
f3e786fc | 887 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
888 | "cupsdIsCGI: Returning 1 with command=\"%s\" and options=\"%s\"", | |
889 | con->command, con->options); | |
3721af07 | 890 | |
891 | return (1); | |
892 | } | |
893 | #endif /* HAVE_JAVA */ | |
894 | #ifdef HAVE_PERL | |
895 | else if (!strcasecmp(type->type, "x-httpd-perl")) | |
896 | { | |
897 | /* | |
898 | * "application/x-httpd-perl" is a Perl page. | |
899 | */ | |
900 | ||
589eb420 | 901 | cupsdSetString(&con->command, CUPS_PERL); |
3721af07 | 902 | |
903 | if (options) | |
589eb420 | 904 | cupsdSetStringf(&con->options, "perl %s %s", filename, options); |
3721af07 | 905 | else |
589eb420 | 906 | cupsdSetStringf(&con->options, "perl %s", filename); |
3721af07 | 907 | |
f3e786fc | 908 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
909 | "cupsdIsCGI: Returning 1 with command=\"%s\" and options=\"%s\"", | |
910 | con->command, con->options); | |
3721af07 | 911 | |
912 | return (1); | |
913 | } | |
914 | #endif /* HAVE_PERL */ | |
915 | #ifdef HAVE_PHP | |
916 | else if (!strcasecmp(type->type, "x-httpd-php")) | |
917 | { | |
918 | /* | |
919 | * "application/x-httpd-php" is a PHP page. | |
920 | */ | |
921 | ||
589eb420 | 922 | cupsdSetString(&con->command, CUPS_PHP); |
3721af07 | 923 | |
924 | if (options) | |
589eb420 | 925 | cupsdSetStringf(&con->options, "php %s %s", filename, options); |
3721af07 | 926 | else |
589eb420 | 927 | cupsdSetStringf(&con->options, "php %s", filename); |
3721af07 | 928 | |
f3e786fc | 929 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
930 | "cupsdIsCGI: Returning 1 with command=\"%s\" and options=\"%s\"", | |
931 | con->command, con->options); | |
3721af07 | 932 | |
933 | return (1); | |
934 | } | |
935 | #endif /* HAVE_PHP */ | |
936 | #ifdef HAVE_PYTHON | |
937 | else if (!strcasecmp(type->type, "x-httpd-python")) | |
938 | { | |
939 | /* | |
940 | * "application/x-httpd-python" is a Python page. | |
941 | */ | |
942 | ||
589eb420 | 943 | cupsdSetString(&con->command, CUPS_PYTHON); |
3721af07 | 944 | |
945 | if (options) | |
589eb420 | 946 | cupsdSetStringf(&con->options, "python %s %s", filename, options); |
3721af07 | 947 | else |
589eb420 | 948 | cupsdSetStringf(&con->options, "python %s", filename); |
3721af07 | 949 | |
f3e786fc | 950 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
951 | "cupsdIsCGI: Returning 1 with command=\"%s\" and options=\"%s\"", | |
952 | con->command, con->options); | |
3721af07 | 953 | |
954 | return (1); | |
955 | } | |
956 | #endif /* HAVE_PYTHON */ | |
957 | ||
f3e786fc | 958 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsCGI: Returning 0..."); |
3721af07 | 959 | |
960 | return (0); | |
961 | } | |
962 | ||
963 | ||
a74b005d | 964 | /* |
589eb420 | 965 | * 'cupsdReadClient()' - Read data from a client. |
a74b005d | 966 | */ |
967 | ||
08c0ff15 | 968 | int /* O - 1 on success, 0 on error */ |
f3e786fc | 969 | cupsdReadClient(cupsd_client_t *con) /* I - Client to read from */ |
a74b005d | 970 | { |
f3e786fc | 971 | char line[32768], /* Line from client... */ |
972 | operation[64], /* Operation code from socket */ | |
973 | version[64], /* HTTP version number string */ | |
974 | locale[64], /* Locale */ | |
975 | *ptr; /* Pointer into strings */ | |
976 | int major, minor; /* HTTP version numbers */ | |
977 | http_status_t status; /* Transfer status */ | |
978 | ipp_state_t ipp_state; /* State of IPP transfer */ | |
979 | int bytes; /* Number of bytes to POST */ | |
980 | char *filename; /* Name of file for GET/HEAD */ | |
981 | char buf[1024]; /* Buffer for real filename */ | |
982 | struct stat filestats; /* File information */ | |
983 | mime_type_t *type; /* MIME type of file */ | |
984 | cupsd_printer_t *p; /* Printer */ | |
985 | static unsigned request_id = 0; /* Request ID for temp files */ | |
a74b005d | 986 | |
bfa1abf0 | 987 | |
a74b005d | 988 | status = HTTP_CONTINUE; |
989 | ||
f3e786fc | 990 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: %d, used=%d, file=%d", |
991 | con->http.fd, con->http.used, con->file); | |
55809c6f | 992 | |
901b295d | 993 | if (con->http.error) |
994 | { | |
f3e786fc | 995 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: http error seen..."); |
589eb420 | 996 | return (cupsdCloseClient(con)); |
901b295d | 997 | } |
998 | ||
84f8a991 | 999 | #ifdef HAVE_SSL |
1000 | if (con->auto_ssl) | |
1001 | { | |
1002 | /* | |
1003 | * Automatically check for a SSL/TLS handshake... | |
1004 | */ | |
1005 | ||
1006 | con->auto_ssl = 0; | |
1007 | ||
d777dcf9 | 1008 | if (recv(con->http.fd, buf, 1, MSG_PEEK) == 1 && |
1009 | (!buf[0] || !strchr("DGHOPT", buf[0]))) | |
84f8a991 | 1010 | { |
1011 | /* | |
1012 | * Encrypt this connection... | |
1013 | */ | |
1014 | ||
f3e786fc | 1015 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1016 | "cupsdReadClient: Saw first byte %02X, auto-negotiating SSL/TLS session...", | |
1017 | buf[0] & 255); | |
84f8a991 | 1018 | |
589eb420 | 1019 | cupsdEncryptClient(con); |
84f8a991 | 1020 | return (1); |
1021 | } | |
1022 | } | |
1023 | #endif /* HAVE_SSL */ | |
1024 | ||
a74b005d | 1025 | switch (con->http.state) |
1026 | { | |
1027 | case HTTP_WAITING : | |
1028 | /* | |
1029 | * See if we've received a request line... | |
1030 | */ | |
1031 | ||
1032 | if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL) | |
93894a43 | 1033 | { |
f3e786fc | 1034 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1035 | "cupsdReadClient: httpGets returned EOF..."); | |
589eb420 | 1036 | return (cupsdCloseClient(con)); |
93894a43 | 1037 | } |
a74b005d | 1038 | |
1039 | /* | |
1040 | * Ignore blank request lines... | |
1041 | */ | |
1042 | ||
1043 | if (line[0] == '\0') | |
1044 | break; | |
1045 | ||
1046 | /* | |
1047 | * Clear other state variables... | |
1048 | */ | |
1049 | ||
1050 | httpClearFields(HTTP(con)); | |
1051 | ||
1479646d | 1052 | con->http.activity = time(NULL); |
1053 | con->http.version = HTTP_1_0; | |
1054 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
1055 | con->http.data_encoding = HTTP_ENCODE_LENGTH; | |
1056 | con->http.data_remaining = 0; | |
1057 | con->http._data_remaining = 0; | |
1058 | con->operation = HTTP_WAITING; | |
1059 | con->bytes = 0; | |
1060 | con->file = -1; | |
1061 | con->file_ready = 0; | |
1062 | con->pipe_pid = 0; | |
1063 | con->username[0] = '\0'; | |
1064 | con->password[0] = '\0'; | |
1065 | con->uri[0] = '\0'; | |
a74b005d | 1066 | |
589eb420 | 1067 | cupsdClearString(&con->command); |
1068 | cupsdClearString(&con->options); | |
fc757c63 | 1069 | |
a74b005d | 1070 | if (con->language != NULL) |
1071 | { | |
1072 | cupsLangFree(con->language); | |
1073 | con->language = NULL; | |
1074 | } | |
1075 | ||
1076 | /* | |
1077 | * Grab the request line... | |
1078 | */ | |
1079 | ||
970017a4 | 1080 | switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version)) |
a74b005d | 1081 | { |
1082 | case 1 : | |
f3e786fc | 1083 | cupsdLogMessage(CUPSD_LOG_ERROR, |
1084 | "Bad request line \"%s\" from %s!", line, | |
1085 | con->http.hostname); | |
589eb420 | 1086 | cupsdSendError(con, HTTP_BAD_REQUEST); |
1087 | return (cupsdCloseClient(con)); | |
a74b005d | 1088 | case 2 : |
1089 | con->http.version = HTTP_0_9; | |
1090 | break; | |
1091 | case 3 : | |
1092 | if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2) | |
1093 | { | |
f3e786fc | 1094 | cupsdLogMessage(CUPSD_LOG_ERROR, |
1095 | "Bad request line \"%s\" from %s!", line, | |
1096 | con->http.hostname); | |
589eb420 | 1097 | cupsdSendError(con, HTTP_BAD_REQUEST); |
1098 | return (cupsdCloseClient(con)); | |
a74b005d | 1099 | } |
1100 | ||
1101 | if (major < 2) | |
1102 | { | |
1103 | con->http.version = (http_version_t)(major * 100 + minor); | |
753453e4 | 1104 | if (con->http.version == HTTP_1_1 && KeepAlive) |
a74b005d | 1105 | con->http.keep_alive = HTTP_KEEPALIVE_ON; |
1106 | else | |
1107 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
1108 | } | |
1109 | else | |
1110 | { | |
589eb420 | 1111 | cupsdSendError(con, HTTP_NOT_SUPPORTED); |
1112 | return (cupsdCloseClient(con)); | |
a74b005d | 1113 | } |
1114 | break; | |
1115 | } | |
1116 | ||
f726e5b9 | 1117 | /* |
1118 | * Handle full URLs in the request line... | |
1119 | */ | |
1120 | ||
38e6ed71 | 1121 | if (con->uri[0] != '/' && strcmp(con->uri, "*")) |
f726e5b9 | 1122 | { |
f3e786fc | 1123 | char method[HTTP_MAX_URI], /* Method/scheme */ |
1124 | userpass[HTTP_MAX_URI], /* Username:password */ | |
1125 | hostname[HTTP_MAX_URI], /* Hostname */ | |
1126 | resource[HTTP_MAX_URI]; /* Resource path */ | |
1127 | int port; /* Port number */ | |
f726e5b9 | 1128 | |
1129 | ||
1130 | /* | |
1131 | * Separate the URI into its components... | |
1132 | */ | |
1133 | ||
1134 | httpSeparate(con->uri, method, userpass, hostname, &port, resource); | |
1135 | ||
1136 | /* | |
1137 | * Only allow URIs with the servername, localhost, or an IP | |
1138 | * address... | |
1139 | */ | |
1140 | ||
1141 | if (strcasecmp(hostname, ServerName) && | |
1142 | strcasecmp(hostname, "localhost") && | |
1143 | !isdigit(hostname[0])) | |
1144 | { | |
1145 | /* | |
1146 | * Nope, we don't do proxies... | |
1147 | */ | |
1148 | ||
f3e786fc | 1149 | cupsdLogMessage(CUPSD_LOG_ERROR, "Bad URI \"%s\" in request!", |
1150 | con->uri); | |
589eb420 | 1151 | cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED); |
1152 | return (cupsdCloseClient(con)); | |
f726e5b9 | 1153 | } |
1154 | ||
1155 | /* | |
1156 | * Copy the resource portion back into the URI; both resource and | |
1157 | * con->uri are HTTP_MAX_URI bytes in size... | |
1158 | */ | |
1159 | ||
1160 | strcpy(con->uri, resource); | |
1161 | } | |
1162 | ||
a74b005d | 1163 | /* |
1164 | * Process the request... | |
1165 | */ | |
1166 | ||
f3e786fc | 1167 | if (!strcmp(operation, "GET")) |
a74b005d | 1168 | con->http.state = HTTP_GET; |
f3e786fc | 1169 | else if (!strcmp(operation, "PUT")) |
a74b005d | 1170 | con->http.state = HTTP_PUT; |
f3e786fc | 1171 | else if (!strcmp(operation, "POST")) |
a74b005d | 1172 | con->http.state = HTTP_POST; |
f3e786fc | 1173 | else if (!strcmp(operation, "DELETE")) |
a74b005d | 1174 | con->http.state = HTTP_DELETE; |
f3e786fc | 1175 | else if (!strcmp(operation, "TRACE")) |
a74b005d | 1176 | con->http.state = HTTP_TRACE; |
f3e786fc | 1177 | else if (!strcmp(operation, "OPTIONS")) |
a74b005d | 1178 | con->http.state = HTTP_OPTIONS; |
f3e786fc | 1179 | else if (!strcmp(operation, "HEAD")) |
a74b005d | 1180 | con->http.state = HTTP_HEAD; |
1181 | else | |
1182 | { | |
f3e786fc | 1183 | cupsdLogMessage(CUPSD_LOG_ERROR, "Bad operation \"%s\"!", operation); |
589eb420 | 1184 | cupsdSendError(con, HTTP_BAD_REQUEST); |
1185 | return (cupsdCloseClient(con)); | |
a74b005d | 1186 | } |
1187 | ||
6a0c519d | 1188 | con->start = time(NULL); |
1189 | con->operation = con->http.state; | |
1190 | ||
f3e786fc | 1191 | cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %s %s HTTP/%d.%d", con->http.fd, |
1192 | operation, con->uri, | |
1193 | con->http.version / 100, con->http.version % 100); | |
a74b005d | 1194 | |
1195 | con->http.status = HTTP_OK; | |
a74b005d | 1196 | |
8f85ff77 | 1197 | case HTTP_OPTIONS : |
a74b005d | 1198 | case HTTP_DELETE : |
1199 | case HTTP_GET : | |
1200 | case HTTP_HEAD : | |
1201 | case HTTP_POST : | |
1202 | case HTTP_PUT : | |
1203 | case HTTP_TRACE : | |
1204 | /* | |
1205 | * Parse incoming parameters until the status changes... | |
1206 | */ | |
1207 | ||
1208 | status = httpUpdate(HTTP(con)); | |
1209 | ||
1210 | if (status != HTTP_OK && status != HTTP_CONTINUE) | |
1211 | { | |
589eb420 | 1212 | cupsdSendError(con, HTTP_BAD_REQUEST); |
1213 | return (cupsdCloseClient(con)); | |
a74b005d | 1214 | } |
1215 | break; | |
d21a7597 | 1216 | |
1217 | default : | |
1218 | break; /* Anti-compiler-warning-code */ | |
a74b005d | 1219 | } |
1220 | ||
1221 | /* | |
1222 | * Handle new transfers... | |
1223 | */ | |
1224 | ||
1225 | if (status == HTTP_OK) | |
1226 | { | |
5a617005 | 1227 | if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0]) |
08c0ff15 | 1228 | { |
1229 | /* | |
1230 | * Figure out the locale from the Accept-Language and Content-Type | |
1231 | * fields... | |
1232 | */ | |
1233 | ||
1234 | if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ',')) != NULL) | |
1235 | *ptr = '\0'; | |
1236 | ||
1237 | if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ';')) != NULL) | |
1238 | *ptr = '\0'; | |
1239 | ||
1240 | if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "charset=")) != NULL) | |
1241 | { | |
1242 | /* | |
1243 | * Combine language and charset, and trim any extra params in the | |
1244 | * content-type. | |
1245 | */ | |
1246 | ||
1247 | snprintf(locale, sizeof(locale), "%s.%s", | |
1248 | con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8); | |
1249 | ||
1250 | if ((ptr = strchr(locale, ',')) != NULL) | |
1251 | *ptr = '\0'; | |
1252 | } | |
1253 | else | |
1254 | snprintf(locale, sizeof(locale), "%s.%s", | |
1255 | con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], DefaultCharset); | |
1256 | ||
1257 | con->language = cupsLangGet(locale); | |
1258 | } | |
5a617005 | 1259 | else |
08c0ff15 | 1260 | con->language = cupsLangGet(DefaultLocale); |
a74b005d | 1261 | |
d2122fde | 1262 | decode_auth(con); |
a74b005d | 1263 | |
f3e786fc | 1264 | if (!strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) && |
753453e4 | 1265 | KeepAlive) |
b1ac1113 | 1266 | con->http.keep_alive = HTTP_KEEPALIVE_ON; |
1267 | ||
f3e786fc | 1268 | if (!con->http.fields[HTTP_FIELD_HOST][0] && |
d558743f | 1269 | con->http.version >= HTTP_1_1) |
a74b005d | 1270 | { |
d558743f | 1271 | /* |
1272 | * HTTP/1.1 and higher require the "Host:" field... | |
1273 | */ | |
1274 | ||
589eb420 | 1275 | if (!cupsdSendError(con, HTTP_BAD_REQUEST)) |
1276 | return (cupsdCloseClient(con)); | |
a74b005d | 1277 | } |
8f85ff77 | 1278 | else if (con->operation == HTTP_OPTIONS) |
1279 | { | |
1280 | /* | |
1281 | * Do OPTIONS command... | |
1282 | */ | |
1283 | ||
589eb420 | 1284 | if ((con->best = cupsdFindBest(con->uri, con->http.state)) != NULL && |
99baf768 | 1285 | con->best->type != AUTH_NONE) |
8f85ff77 | 1286 | { |
589eb420 | 1287 | if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL)) |
1288 | return (cupsdCloseClient(con)); | |
8f85ff77 | 1289 | } |
a75c006a | 1290 | |
8c367b6f | 1291 | if (!strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") && |
a75c006a | 1292 | con->http.tls == NULL) |
8f85ff77 | 1293 | { |
bcf61448 | 1294 | #ifdef HAVE_SSL |
a75c006a | 1295 | /* |
1296 | * Do encryption stuff... | |
1297 | */ | |
1298 | ||
589eb420 | 1299 | if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL)) |
1300 | return (cupsdCloseClient(con)); | |
a75c006a | 1301 | |
1302 | httpPrintf(HTTP(con), "Connection: Upgrade\r\n"); | |
b5cb0608 | 1303 | httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n"); |
1c6682dd | 1304 | httpPrintf(HTTP(con), "Content-Length: 0\r\n"); |
a75c006a | 1305 | httpPrintf(HTTP(con), "\r\n"); |
1306 | ||
589eb420 | 1307 | cupsdEncryptClient(con); |
a75c006a | 1308 | #else |
589eb420 | 1309 | if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED)) |
1310 | return (cupsdCloseClient(con)); | |
bcf61448 | 1311 | #endif /* HAVE_SSL */ |
a75c006a | 1312 | } |
1313 | ||
65c098da | 1314 | if (con->http.expect) |
1315 | { | |
1316 | /**** TODO: send expected header ****/ | |
1317 | } | |
1318 | ||
589eb420 | 1319 | if (!cupsdSendHeader(con, HTTP_OK, NULL)) |
1320 | return (cupsdCloseClient(con)); | |
8f85ff77 | 1321 | |
753453e4 | 1322 | httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n"); |
1c6682dd | 1323 | httpPrintf(HTTP(con), "Content-Length: 0\r\n"); |
8f85ff77 | 1324 | httpPrintf(HTTP(con), "\r\n"); |
1325 | } | |
3780dd72 | 1326 | else if (!is_path_absolute(con->uri)) |
a74b005d | 1327 | { |
1328 | /* | |
1329 | * Protect against malicious users! | |
1330 | */ | |
1331 | ||
589eb420 | 1332 | if (!cupsdSendError(con, HTTP_FORBIDDEN)) |
1333 | return (cupsdCloseClient(con)); | |
a74b005d | 1334 | } |
b38fb7fc | 1335 | else |
a74b005d | 1336 | { |
8c367b6f | 1337 | if (!strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") && |
b5cb0608 | 1338 | con->http.tls == NULL) |
b38fb7fc | 1339 | { |
bcf61448 | 1340 | #ifdef HAVE_SSL |
b38fb7fc | 1341 | /* |
1342 | * Do encryption stuff... | |
1343 | */ | |
f3d580b9 | 1344 | |
589eb420 | 1345 | if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL)) |
1346 | return (cupsdCloseClient(con)); | |
f969a074 | 1347 | |
b38fb7fc | 1348 | httpPrintf(HTTP(con), "Connection: Upgrade\r\n"); |
b5cb0608 | 1349 | httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n"); |
1c6682dd | 1350 | httpPrintf(HTTP(con), "Content-Length: 0\r\n"); |
b38fb7fc | 1351 | httpPrintf(HTTP(con), "\r\n"); |
1352 | ||
589eb420 | 1353 | cupsdEncryptClient(con); |
b38fb7fc | 1354 | #else |
589eb420 | 1355 | if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED)) |
1356 | return (cupsdCloseClient(con)); | |
bcf61448 | 1357 | #endif /* HAVE_SSL */ |
b38fb7fc | 1358 | } |
b5cb0608 | 1359 | |
589eb420 | 1360 | con->best = cupsdFindBest(con->uri, con->http.state); |
99baf768 | 1361 | |
1362 | if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_OK) | |
b38fb7fc | 1363 | { |
f3e786fc | 1364 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1365 | "cupsdReadClient: Unauthorized request for %s...\n", | |
1366 | con->uri); | |
589eb420 | 1367 | cupsdSendError(con, status); |
1368 | return (cupsdCloseClient(con)); | |
b38fb7fc | 1369 | } |
1370 | ||
65c098da | 1371 | if (con->http.expect) |
1372 | { | |
1373 | /**** TODO: send expected header ****/ | |
1374 | } | |
1375 | ||
b38fb7fc | 1376 | switch (con->http.state) |
1377 | { | |
1378 | case HTTP_GET_SEND : | |
f3e786fc | 1379 | if (!strncmp(con->uri, "/printers/", 10) && |
1380 | !strcmp(con->uri + strlen(con->uri) - 4, ".ppd")) | |
2a8fc30c | 1381 | { |
b38fb7fc | 1382 | /* |
1383 | * Send PPD file - get the real printer name since printer | |
1384 | * names are not case sensitive but filenames can be... | |
1385 | */ | |
2a8fc30c | 1386 | |
b38fb7fc | 1387 | con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ |
f3d580b9 | 1388 | |
589eb420 | 1389 | if ((p = cupsdFindPrinter(con->uri + 10)) != NULL) |
b38fb7fc | 1390 | snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); |
1391 | else | |
1392 | { | |
589eb420 | 1393 | if (!cupsdSendError(con, HTTP_NOT_FOUND)) |
1394 | return (cupsdCloseClient(con)); | |
a74b005d | 1395 | |
b38fb7fc | 1396 | break; |
1397 | } | |
b14d90ba | 1398 | } |
b38fb7fc | 1399 | |
d694b7ed | 1400 | if ((!strncmp(con->uri, "/admin", 6) && |
1401 | strncmp(con->uri, "/admin/conf/", 12) && | |
1402 | strncmp(con->uri, "/admin/log/", 11)) || | |
1403 | !strncmp(con->uri, "/printers", 9) || | |
1404 | !strncmp(con->uri, "/classes", 8) || | |
771b76de | 1405 | !strncmp(con->uri, "/help", 5) || |
d694b7ed | 1406 | !strncmp(con->uri, "/jobs", 5)) |
b14d90ba | 1407 | { |
b38fb7fc | 1408 | /* |
1409 | * Send CGI output... | |
1410 | */ | |
1411 | ||
d694b7ed | 1412 | if (!strncmp(con->uri, "/admin", 6)) |
b38fb7fc | 1413 | { |
f3e786fc | 1414 | cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi", |
1415 | ServerBin); | |
ff346847 | 1416 | |
1417 | if ((ptr = strchr(con->uri + 6, '?')) != NULL) | |
589eb420 | 1418 | cupsdSetStringf(&con->options, "admin%s", ptr); |
ff346847 | 1419 | else |
589eb420 | 1420 | cupsdSetString(&con->options, "admin"); |
b38fb7fc | 1421 | } |
d694b7ed | 1422 | else if (!strncmp(con->uri, "/printers", 9)) |
b38fb7fc | 1423 | { |
f3e786fc | 1424 | cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi", |
1425 | ServerBin); | |
589eb420 | 1426 | cupsdSetString(&con->options, con->uri + 9); |
b38fb7fc | 1427 | } |
d694b7ed | 1428 | else if (!strncmp(con->uri, "/classes", 8)) |
b38fb7fc | 1429 | { |
f3e786fc | 1430 | cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi", |
1431 | ServerBin); | |
589eb420 | 1432 | cupsdSetString(&con->options, con->uri + 8); |
b38fb7fc | 1433 | } |
771b76de | 1434 | else if (!strncmp(con->uri, "/jobs", 5)) |
b38fb7fc | 1435 | { |
f3e786fc | 1436 | cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi", |
1437 | ServerBin); | |
589eb420 | 1438 | cupsdSetString(&con->options, con->uri + 5); |
ff346847 | 1439 | } |
771b76de | 1440 | else |
1441 | { | |
f3e786fc | 1442 | cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi", |
1443 | ServerBin); | |
589eb420 | 1444 | cupsdSetString(&con->options, con->uri + 5); |
771b76de | 1445 | } |
b38fb7fc | 1446 | |
e060a567 | 1447 | if (con->options[0] == '/') |
2625096f | 1448 | _cups_strcpy(con->options, con->options + 1); |
e060a567 | 1449 | |
589eb420 | 1450 | if (!cupsdSendCommand(con, con->command, con->options, 0)) |
b38fb7fc | 1451 | { |
589eb420 | 1452 | if (!cupsdSendError(con, HTTP_NOT_FOUND)) |
1453 | return (cupsdCloseClient(con)); | |
b38fb7fc | 1454 | } |
1455 | else | |
589eb420 | 1456 | cupsdLogRequest(con, HTTP_OK); |
b38fb7fc | 1457 | |
1458 | if (con->http.version <= HTTP_1_0) | |
1459 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
b14d90ba | 1460 | } |
d694b7ed | 1461 | else if ((!strncmp(con->uri, "/admin/conf/", 12) && |
1462 | (strchr(con->uri + 12, '/') || | |
1463 | strlen(con->uri) == 12)) || | |
1464 | (!strncmp(con->uri, "/admin/log/", 11) && | |
1465 | (strchr(con->uri + 11, '/') || | |
1466 | strlen(con->uri) == 11))) | |
753453e4 | 1467 | { |
1468 | /* | |
1469 | * GET can only be done to configuration files under | |
1470 | * /admin/conf... | |
1471 | */ | |
1472 | ||
589eb420 | 1473 | if (!cupsdSendError(con, HTTP_FORBIDDEN)) |
1474 | return (cupsdCloseClient(con)); | |
753453e4 | 1475 | |
1476 | break; | |
1477 | } | |
b14d90ba | 1478 | else |
1479 | { | |
b38fb7fc | 1480 | /* |
1481 | * Serve a file... | |
1482 | */ | |
1483 | ||
6db7190f | 1484 | if ((filename = get_file(con, &filestats, buf, |
1485 | sizeof(buf))) == NULL) | |
b38fb7fc | 1486 | { |
589eb420 | 1487 | if (!cupsdSendError(con, HTTP_NOT_FOUND)) |
1488 | return (cupsdCloseClient(con)); | |
e6221033 | 1489 | |
1490 | break; | |
1491 | } | |
1492 | ||
d59a189c | 1493 | type = mimeFileType(MimeDatabase, filename, NULL); |
e6221033 | 1494 | |
589eb420 | 1495 | if (cupsdIsCGI(con, filename, &filestats, type)) |
e6221033 | 1496 | { |
f6b55e0c | 1497 | /* |
1498 | * Note: con->command and con->options were set by | |
589eb420 | 1499 | * cupsdIsCGI()... |
f6b55e0c | 1500 | */ |
1501 | ||
589eb420 | 1502 | if (!cupsdSendCommand(con, con->command, con->options, 0)) |
e6221033 | 1503 | { |
589eb420 | 1504 | if (!cupsdSendError(con, HTTP_NOT_FOUND)) |
1505 | return (cupsdCloseClient(con)); | |
e6221033 | 1506 | } |
1507 | else | |
589eb420 | 1508 | cupsdLogRequest(con, HTTP_OK); |
e6221033 | 1509 | |
1510 | if (con->http.version <= HTTP_1_0) | |
1511 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
1512 | break; | |
b38fb7fc | 1513 | } |
e6221033 | 1514 | |
1515 | if (!check_if_modified(con, &filestats)) | |
b38fb7fc | 1516 | { |
589eb420 | 1517 | if (!cupsdSendError(con, HTTP_NOT_MODIFIED)) |
1518 | return (cupsdCloseClient(con)); | |
b38fb7fc | 1519 | } |
1520 | else | |
1521 | { | |
b38fb7fc | 1522 | if (type == NULL) |
1523 | strcpy(line, "text/plain"); | |
1524 | else | |
1525 | snprintf(line, sizeof(line), "%s/%s", type->super, type->type); | |
1526 | ||
589eb420 | 1527 | if (!cupsdSendFile(con, HTTP_OK, filename, line, &filestats)) |
1528 | return (cupsdCloseClient(con)); | |
b38fb7fc | 1529 | } |
b14d90ba | 1530 | } |
b38fb7fc | 1531 | break; |
1532 | ||
1533 | case HTTP_POST_RECV : | |
1534 | /* | |
1535 | * See if the POST request includes a Content-Length field, and if | |
1536 | * so check the length against any limits that are set... | |
1537 | */ | |
b14d90ba | 1538 | |
f3e786fc | 1539 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "POST %s", con->uri); |
1540 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "CONTENT_TYPE = %s", | |
1541 | con->http.fields[HTTP_FIELD_CONTENT_TYPE]); | |
b14d90ba | 1542 | |
b38fb7fc | 1543 | if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] && |
1479646d | 1544 | MaxRequestSize > 0 && |
1545 | con->http.data_remaining > MaxRequestSize) | |
a74b005d | 1546 | { |
b38fb7fc | 1547 | /* |
1548 | * Request too large... | |
1549 | */ | |
1550 | ||
589eb420 | 1551 | if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE)) |
1552 | return (cupsdCloseClient(con)); | |
b38fb7fc | 1553 | |
1554 | break; | |
a74b005d | 1555 | } |
1479646d | 1556 | else if (con->http.data_remaining < 0) |
901b295d | 1557 | { |
1558 | /* | |
1559 | * Negative content lengths are invalid! | |
1560 | */ | |
1561 | ||
589eb420 | 1562 | if (!cupsdSendError(con, HTTP_BAD_REQUEST)) |
1563 | return (cupsdCloseClient(con)); | |
901b295d | 1564 | |
1565 | break; | |
1566 | } | |
a74b005d | 1567 | |
b38fb7fc | 1568 | /* |
1569 | * See what kind of POST request this is; for IPP requests the | |
1570 | * content-type field will be "application/ipp"... | |
a74b005d | 1571 | */ |
1572 | ||
f3e786fc | 1573 | if (!strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], |
1574 | "application/ipp")) | |
b38fb7fc | 1575 | con->request = ippNew(); |
d2e43d2e | 1576 | else if ((!strncmp(con->uri, "/admin", 6) && |
771b76de | 1577 | strncmp(con->uri, "/admin/conf/", 12) && |
1578 | strncmp(con->uri, "/admin/log/", 11)) || | |
1579 | !strncmp(con->uri, "/printers", 9) || | |
1580 | !strncmp(con->uri, "/classes", 8) || | |
1581 | !strncmp(con->uri, "/help", 5) || | |
1582 | !strncmp(con->uri, "/jobs", 5)) | |
a74b005d | 1583 | { |
b38fb7fc | 1584 | /* |
1585 | * CGI request... | |
1586 | */ | |
1587 | ||
771b76de | 1588 | if (!strncmp(con->uri, "/admin", 6)) |
a74b005d | 1589 | { |
f3e786fc | 1590 | cupsdSetStringf(&con->command, "%s/cgi-bin/admin.cgi", |
1591 | ServerBin); | |
e060a567 | 1592 | |
1593 | if ((ptr = strchr(con->uri + 6, '?')) != NULL) | |
589eb420 | 1594 | cupsdSetStringf(&con->options, "admin%s", ptr); |
e060a567 | 1595 | else |
589eb420 | 1596 | cupsdSetString(&con->options, "admin"); |
a74b005d | 1597 | } |
771b76de | 1598 | else if (!strncmp(con->uri, "/printers", 9)) |
a74b005d | 1599 | { |
f3e786fc | 1600 | cupsdSetStringf(&con->command, "%s/cgi-bin/printers.cgi", |
1601 | ServerBin); | |
589eb420 | 1602 | cupsdSetString(&con->options, con->uri + 9); |
b38fb7fc | 1603 | } |
771b76de | 1604 | else if (!strncmp(con->uri, "/classes", 8)) |
b38fb7fc | 1605 | { |
f3e786fc | 1606 | cupsdSetStringf(&con->command, "%s/cgi-bin/classes.cgi", |
1607 | ServerBin); | |
589eb420 | 1608 | cupsdSetString(&con->options, con->uri + 8); |
a74b005d | 1609 | } |
771b76de | 1610 | else if (!strncmp(con->uri, "/jobs", 5)) |
a74b005d | 1611 | { |
f3e786fc | 1612 | cupsdSetStringf(&con->command, "%s/cgi-bin/jobs.cgi", |
1613 | ServerBin); | |
589eb420 | 1614 | cupsdSetString(&con->options, con->uri + 5); |
a74b005d | 1615 | } |
771b76de | 1616 | else |
1617 | { | |
f3e786fc | 1618 | cupsdSetStringf(&con->command, "%s/cgi-bin/help.cgi", |
1619 | ServerBin); | |
589eb420 | 1620 | cupsdSetString(&con->options, con->uri + 5); |
771b76de | 1621 | } |
8b43895a | 1622 | |
b38fb7fc | 1623 | if (con->options[0] == '/') |
2625096f | 1624 | _cups_strcpy(con->options, con->options + 1); |
f63a2256 | 1625 | |
f3e786fc | 1626 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1627 | "cupsdReadClient: %d command=\"%s\", options = \"%s\"", | |
1628 | con->http.fd, con->command, con->options); | |
8b43895a | 1629 | |
b38fb7fc | 1630 | if (con->http.version <= HTTP_1_0) |
1631 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
1632 | } | |
e6221033 | 1633 | else |
8b43895a | 1634 | { |
e6221033 | 1635 | /* |
1636 | * POST to a file... | |
1637 | */ | |
1638 | ||
1639 | if ((filename = get_file(con, &filestats, buf, | |
1640 | sizeof(buf))) == NULL) | |
1641 | { | |
589eb420 | 1642 | if (!cupsdSendError(con, HTTP_NOT_FOUND)) |
1643 | return (cupsdCloseClient(con)); | |
e6221033 | 1644 | |
1645 | break; | |
1646 | } | |
1647 | ||
d59a189c | 1648 | type = mimeFileType(MimeDatabase, filename, NULL); |
e6221033 | 1649 | |
589eb420 | 1650 | if (!cupsdIsCGI(con, filename, &filestats, type)) |
e6221033 | 1651 | { |
1652 | /* | |
1653 | * Only POST to CGI's... | |
1654 | */ | |
1655 | ||
589eb420 | 1656 | if (!cupsdSendError(con, HTTP_UNAUTHORIZED)) |
1657 | return (cupsdCloseClient(con)); | |
e6221033 | 1658 | } |
8b43895a | 1659 | } |
8b43895a | 1660 | break; |
b14d90ba | 1661 | |
b38fb7fc | 1662 | case HTTP_PUT_RECV : |
753453e4 | 1663 | /* |
1664 | * Validate the resource name... | |
1665 | */ | |
1666 | ||
771b76de | 1667 | if (strncmp(con->uri, "/admin/conf/", 12) || |
1668 | strchr(con->uri + 12, '/') || | |
753453e4 | 1669 | strlen(con->uri) == 12) |
1670 | { | |
1671 | /* | |
1672 | * PUT can only be done to configuration files under | |
1673 | * /admin/conf... | |
1674 | */ | |
1675 | ||
589eb420 | 1676 | if (!cupsdSendError(con, HTTP_FORBIDDEN)) |
1677 | return (cupsdCloseClient(con)); | |
753453e4 | 1678 | |
1679 | break; | |
1680 | } | |
1681 | ||
1682 | /* | |
1683 | * See if the PUT request includes a Content-Length field, and if | |
1684 | * so check the length against any limits that are set... | |
1685 | */ | |
1686 | ||
f3e786fc | 1687 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "PUT %s", con->uri); |
1688 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "CONTENT_TYPE = %s", | |
1689 | con->http.fields[HTTP_FIELD_CONTENT_TYPE]); | |
753453e4 | 1690 | |
1691 | if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] && | |
1479646d | 1692 | MaxRequestSize > 0 && |
1693 | con->http.data_remaining > MaxRequestSize) | |
753453e4 | 1694 | { |
1695 | /* | |
1696 | * Request too large... | |
1697 | */ | |
1698 | ||
589eb420 | 1699 | if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE)) |
1700 | return (cupsdCloseClient(con)); | |
753453e4 | 1701 | |
1702 | break; | |
1703 | } | |
1479646d | 1704 | else if (con->http.data_remaining < 0) |
0cd4970e | 1705 | { |
1706 | /* | |
1707 | * Negative content lengths are invalid! | |
1708 | */ | |
1709 | ||
589eb420 | 1710 | if (!cupsdSendError(con, HTTP_BAD_REQUEST)) |
1711 | return (cupsdCloseClient(con)); | |
0cd4970e | 1712 | |
1713 | break; | |
1714 | } | |
753453e4 | 1715 | |
1716 | /* | |
1717 | * Open a temporary file to hold the request... | |
1718 | */ | |
1719 | ||
f3e786fc | 1720 | cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, |
1721 | request_id ++); | |
753453e4 | 1722 | con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); |
753453e4 | 1723 | |
f3e786fc | 1724 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1725 | "cupsdReadClient: %d REQUEST %s=%d", con->http.fd, | |
1726 | con->filename, con->file); | |
753453e4 | 1727 | |
1728 | if (con->file < 0) | |
1729 | { | |
589eb420 | 1730 | if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE)) |
1731 | return (cupsdCloseClient(con)); | |
753453e4 | 1732 | } |
db3c5bfd | 1733 | |
1734 | fchmod(con->file, 0640); | |
1735 | fchown(con->file, RunUser, Group); | |
1736 | fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); | |
753453e4 | 1737 | break; |
1738 | ||
b38fb7fc | 1739 | case HTTP_DELETE : |
1740 | case HTTP_TRACE : | |
589eb420 | 1741 | cupsdSendError(con, HTTP_NOT_IMPLEMENTED); |
1742 | return (cupsdCloseClient(con)); | |
b14d90ba | 1743 | |
b38fb7fc | 1744 | case HTTP_HEAD : |
771b76de | 1745 | if (!strncmp(con->uri, "/printers/", 10) && |
1746 | !strcmp(con->uri + strlen(con->uri) - 4, ".ppd")) | |
bd84e0d1 | 1747 | { |
b38fb7fc | 1748 | /* |
d588a92e | 1749 | * Send PPD file - get the real printer name since printer |
1750 | * names are not case sensitive but filenames can be... | |
b38fb7fc | 1751 | */ |
a74b005d | 1752 | |
d588a92e | 1753 | con->uri[strlen(con->uri) - 4] = '\0'; /* Drop ".ppd" */ |
1754 | ||
589eb420 | 1755 | if ((p = cupsdFindPrinter(con->uri + 10)) != NULL) |
d588a92e | 1756 | snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name); |
1757 | else | |
1758 | { | |
589eb420 | 1759 | if (!cupsdSendError(con, HTTP_NOT_FOUND)) |
1760 | return (cupsdCloseClient(con)); | |
d588a92e | 1761 | |
1762 | break; | |
1763 | } | |
b38fb7fc | 1764 | } |
a74b005d | 1765 | |
771b76de | 1766 | if ((!strncmp(con->uri, "/admin", 6) && |
1767 | strncmp(con->uri, "/admin/conf/", 12) && | |
1768 | strncmp(con->uri, "/admin/log/", 11)) || | |
1769 | !strncmp(con->uri, "/printers", 9) || | |
1770 | !strncmp(con->uri, "/classes", 8) || | |
1771 | !strncmp(con->uri, "/help", 5) || | |
1772 | !strncmp(con->uri, "/jobs", 5)) | |
b38fb7fc | 1773 | { |
1774 | /* | |
1775 | * CGI output... | |
1776 | */ | |
f3d580b9 | 1777 | |
589eb420 | 1778 | if (!cupsdSendHeader(con, HTTP_OK, "text/html")) |
1779 | return (cupsdCloseClient(con)); | |
f3d580b9 | 1780 | |
b38fb7fc | 1781 | if (httpPrintf(HTTP(con), "\r\n") < 0) |
589eb420 | 1782 | return (cupsdCloseClient(con)); |
a74b005d | 1783 | |
589eb420 | 1784 | cupsdLogRequest(con, HTTP_OK); |
a74b005d | 1785 | } |
771b76de | 1786 | else if ((!strncmp(con->uri, "/admin/conf/", 12) && |
1787 | (strchr(con->uri + 12, '/') || | |
1788 | strlen(con->uri) == 12)) || | |
1789 | (!strncmp(con->uri, "/admin/log/", 11) && | |
1790 | (strchr(con->uri + 11, '/') || | |
1791 | strlen(con->uri) == 11))) | |
753453e4 | 1792 | { |
1793 | /* | |
1794 | * HEAD can only be done to configuration files under | |
1795 | * /admin/conf... | |
1796 | */ | |
1797 | ||
589eb420 | 1798 | if (!cupsdSendError(con, HTTP_FORBIDDEN)) |
1799 | return (cupsdCloseClient(con)); | |
753453e4 | 1800 | |
1801 | break; | |
1802 | } | |
6db7190f | 1803 | else if ((filename = get_file(con, &filestats, buf, |
1804 | sizeof(buf))) == NULL) | |
a74b005d | 1805 | { |
589eb420 | 1806 | if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html")) |
1807 | return (cupsdCloseClient(con)); | |
6a0c519d | 1808 | |
589eb420 | 1809 | cupsdLogRequest(con, HTTP_NOT_FOUND); |
a74b005d | 1810 | } |
b38fb7fc | 1811 | else if (!check_if_modified(con, &filestats)) |
1812 | { | |
589eb420 | 1813 | if (!cupsdSendError(con, HTTP_NOT_MODIFIED)) |
1814 | return (cupsdCloseClient(con)); | |
6a0c519d | 1815 | |
589eb420 | 1816 | cupsdLogRequest(con, HTTP_NOT_MODIFIED); |
a74b005d | 1817 | } |
b38fb7fc | 1818 | else |
1819 | { | |
1820 | /* | |
1821 | * Serve a file... | |
1822 | */ | |
6a0c519d | 1823 | |
d59a189c | 1824 | type = mimeFileType(MimeDatabase, filename, NULL); |
b38fb7fc | 1825 | if (type == NULL) |
1826 | strcpy(line, "text/plain"); | |
1827 | else | |
1828 | snprintf(line, sizeof(line), "%s/%s", type->super, type->type); | |
a74b005d | 1829 | |
589eb420 | 1830 | if (!cupsdSendHeader(con, HTTP_OK, line)) |
1831 | return (cupsdCloseClient(con)); | |
a74b005d | 1832 | |
b38fb7fc | 1833 | if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", |
1834 | httpGetDateString(filestats.st_mtime)) < 0) | |
589eb420 | 1835 | return (cupsdCloseClient(con)); |
a74b005d | 1836 | |
753453e4 | 1837 | if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n", |
1838 | (unsigned long)filestats.st_size) < 0) | |
589eb420 | 1839 | return (cupsdCloseClient(con)); |
b38fb7fc | 1840 | |
589eb420 | 1841 | cupsdLogRequest(con, HTTP_OK); |
a74b005d | 1842 | } |
1843 | ||
b38fb7fc | 1844 | if (httpPrintf(HTTP(con), "\r\n") < 0) |
589eb420 | 1845 | return (cupsdCloseClient(con)); |
6a0c519d | 1846 | |
b38fb7fc | 1847 | con->http.state = HTTP_WAITING; |
1848 | break; | |
a74b005d | 1849 | |
b38fb7fc | 1850 | default : |
1851 | break; /* Anti-compiler-warning-code */ | |
1852 | } | |
a74b005d | 1853 | } |
1854 | } | |
1855 | ||
1856 | /* | |
1857 | * Handle any incoming data... | |
1858 | */ | |
1859 | ||
1860 | switch (con->http.state) | |
1861 | { | |
1862 | case HTTP_PUT_RECV : | |
f3e786fc | 1863 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1479646d | 1864 | "cupsdReadClient: %d con->data_encoding=HTTP_ENCODE_%s, " |
1865 | "con->data_remaining=" CUPS_LLFMT ", con->file=%d", | |
f3e786fc | 1866 | con->http.fd, |
1867 | con->http.data_encoding == HTTP_ENCODE_CHUNKED ? | |
1479646d | 1868 | "CHUNKED" : "LENGTH", |
1869 | CUPS_LLCAST con->http.data_remaining, con->file); | |
753453e4 | 1870 | |
1871 | if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0) | |
589eb420 | 1872 | return (cupsdCloseClient(con)); |
753453e4 | 1873 | else if (bytes > 0) |
1874 | { | |
1875 | con->bytes += bytes; | |
1876 | ||
f3e786fc | 1877 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1878 | "cupsdReadClient: %d writing %d bytes to %d", | |
1879 | con->http.fd, bytes, con->file); | |
753453e4 | 1880 | |
1881 | if (write(con->file, line, bytes) < bytes) | |
1882 | { | |
f3e786fc | 1883 | cupsdLogMessage(CUPSD_LOG_ERROR, |
1884 | "cupsdReadClient: Unable to write %d bytes to %s: %s", | |
1885 | bytes, con->filename, strerror(errno)); | |
753453e4 | 1886 | |
f3e786fc | 1887 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1888 | "cupsdReadClient: Closing data file %d...", | |
1889 | con->file); | |
6cd03542 | 1890 | |
753453e4 | 1891 | close(con->file); |
6a2cceab | 1892 | con->file = -1; |
753453e4 | 1893 | unlink(con->filename); |
589eb420 | 1894 | cupsdClearString(&con->filename); |
753453e4 | 1895 | |
589eb420 | 1896 | if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE)) |
1897 | return (cupsdCloseClient(con)); | |
753453e4 | 1898 | } |
1899 | } | |
1900 | ||
1901 | if (con->http.state == HTTP_WAITING) | |
1902 | { | |
1903 | /* | |
1904 | * End of file, see how big it is... | |
1905 | */ | |
1906 | ||
1907 | fstat(con->file, &filestats); | |
1908 | ||
f3e786fc | 1909 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1479646d | 1910 | "cupsdReadClient: %d Closing data file %d, size=" |
1911 | CUPS_LLFMT ".", | |
1912 | con->http.fd, con->file, | |
1913 | CUPS_LLCAST filestats.st_size); | |
753453e4 | 1914 | |
1915 | close(con->file); | |
6a2cceab | 1916 | con->file = -1; |
753453e4 | 1917 | |
1918 | if (filestats.st_size > MaxRequestSize && | |
1919 | MaxRequestSize > 0) | |
1920 | { | |
1921 | /* | |
1922 | * Request is too big; remove it and send an error... | |
1923 | */ | |
1924 | ||
f3e786fc | 1925 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1926 | "cupsdReadClient: %d Removing temp file %s", | |
1927 | con->http.fd, con->filename); | |
753453e4 | 1928 | unlink(con->filename); |
589eb420 | 1929 | cupsdClearString(&con->filename); |
753453e4 | 1930 | |
589eb420 | 1931 | if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE)) |
1932 | return (cupsdCloseClient(con)); | |
753453e4 | 1933 | } |
1934 | ||
1935 | /* | |
1936 | * Install the configuration file... | |
1937 | */ | |
1938 | ||
1939 | status = install_conf_file(con); | |
1940 | ||
1941 | /* | |
1942 | * Return the status to the client... | |
1943 | */ | |
1944 | ||
589eb420 | 1945 | if (!cupsdSendError(con, status)) |
1946 | return (cupsdCloseClient(con)); | |
753453e4 | 1947 | } |
a74b005d | 1948 | break; |
1949 | ||
1950 | case HTTP_POST_RECV : | |
f3e786fc | 1951 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1479646d | 1952 | "cupsdReadClient: %d con->data_encoding=HTTP_ENCODE_" |
1953 | "%s, con->data_remaining=" CUPS_LLFMT ", con->file=%d", | |
f3e786fc | 1954 | con->http.fd, |
1955 | con->http.data_encoding == HTTP_ENCODE_CHUNKED ? | |
1479646d | 1956 | "CHUNKED" : "LENGTH", |
1957 | CUPS_LLCAST con->http.data_remaining, con->file); | |
a74b005d | 1958 | |
b14d90ba | 1959 | if (con->request != NULL) |
1960 | { | |
1961 | /* | |
1962 | * Grab any request data from the connection... | |
1963 | */ | |
1964 | ||
bd176c9c | 1965 | if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR) |
1966 | { | |
f3e786fc | 1967 | cupsdLogMessage(CUPSD_LOG_ERROR, |
1968 | "cupsdReadClient: %d IPP Read Error!", | |
1969 | con->http.fd); | |
3955a26e | 1970 | |
589eb420 | 1971 | cupsdSendError(con, HTTP_BAD_REQUEST); |
1972 | return (cupsdCloseClient(con)); | |
bd176c9c | 1973 | } |
1974 | else if (ipp_state != IPP_DATA) | |
3023f981 | 1975 | { |
1976 | if (con->http.state == HTTP_POST_SEND) | |
1977 | { | |
589eb420 | 1978 | cupsdSendError(con, HTTP_BAD_REQUEST); |
1979 | return (cupsdCloseClient(con)); | |
3023f981 | 1980 | } |
1981 | ||
b14d90ba | 1982 | break; |
3023f981 | 1983 | } |
68ff4f01 | 1984 | else |
1985 | con->bytes += ippLength(con->request); | |
f63a2256 | 1986 | } |
b14d90ba | 1987 | |
6a2cceab | 1988 | if (con->file < 0 && con->http.state != HTTP_POST_SEND) |
f63a2256 | 1989 | { |
1990 | /* | |
1991 | * Create a file as needed for the request data... | |
1992 | */ | |
1d2c70a6 | 1993 | |
589eb420 | 1994 | cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++); |
f63a2256 | 1995 | con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640); |
b14d90ba | 1996 | |
f3e786fc | 1997 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: %d REQUEST %s=%d", con->http.fd, |
1998 | con->filename, con->file); | |
b14d90ba | 1999 | |
f63a2256 | 2000 | if (con->file < 0) |
2001 | { | |
589eb420 | 2002 | if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE)) |
2003 | return (cupsdCloseClient(con)); | |
b14d90ba | 2004 | } |
db3c5bfd | 2005 | |
2006 | fchmod(con->file, 0640); | |
2007 | fchown(con->file, RunUser, Group); | |
2008 | fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); | |
f63a2256 | 2009 | } |
b14d90ba | 2010 | |
bfa1abf0 | 2011 | if (con->http.state != HTTP_POST_SEND) |
a74b005d | 2012 | { |
bfa1abf0 | 2013 | if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0) |
589eb420 | 2014 | return (cupsdCloseClient(con)); |
bfa1abf0 | 2015 | else if (bytes > 0) |
2016 | { | |
2017 | con->bytes += bytes; | |
a74b005d | 2018 | |
f3e786fc | 2019 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2020 | "cupsdReadClient: %d writing %d bytes to %d", | |
2021 | con->http.fd, bytes, con->file); | |
bfa1abf0 | 2022 | |
2023 | if (write(con->file, line, bytes) < bytes) | |
a74b005d | 2024 | { |
f3e786fc | 2025 | cupsdLogMessage(CUPSD_LOG_ERROR, |
2026 | "cupsdReadClient: Unable to write %d bytes to %s: %s", | |
2027 | bytes, con->filename, strerror(errno)); | |
a61a2be6 | 2028 | |
f3e786fc | 2029 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2030 | "cupsdReadClient: Closing file %d...", | |
2031 | con->file); | |
6cd03542 | 2032 | |
bfa1abf0 | 2033 | close(con->file); |
6a2cceab | 2034 | con->file = -1; |
bfa1abf0 | 2035 | unlink(con->filename); |
589eb420 | 2036 | cupsdClearString(&con->filename); |
bfa1abf0 | 2037 | |
589eb420 | 2038 | if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE)) |
2039 | return (cupsdCloseClient(con)); | |
a74b005d | 2040 | } |
2041 | } | |
6faac3f3 | 2042 | else if (con->http.state == HTTP_POST_RECV) |
819347a7 | 2043 | return (1); /* ??? */ |
bd176c9c | 2044 | else if (con->http.state != HTTP_POST_SEND) |
589eb420 | 2045 | return (cupsdCloseClient(con)); |
a74b005d | 2046 | } |
93894a43 | 2047 | |
2048 | if (con->http.state == HTTP_POST_SEND) | |
2049 | { | |
6a2cceab | 2050 | if (con->file >= 0) |
bfa1abf0 | 2051 | { |
8b43895a | 2052 | fstat(con->file, &filestats); |
bd917997 | 2053 | |
f3e786fc | 2054 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1479646d | 2055 | "cupsdReadClient: %d Closing data file %d, " |
2056 | "size=" CUPS_LLFMT ".", | |
2057 | con->http.fd, con->file, | |
2058 | CUPS_LLCAST filestats.st_size); | |
bd917997 | 2059 | |
bfa1abf0 | 2060 | close(con->file); |
6a2cceab | 2061 | con->file = -1; |
8b43895a | 2062 | |
2063 | if (filestats.st_size > MaxRequestSize && | |
2064 | MaxRequestSize > 0) | |
2065 | { | |
2066 | /* | |
2067 | * Request is too big; remove it and send an error... | |
2068 | */ | |
2069 | ||
f3e786fc | 2070 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2071 | "cupsdReadClient: %d Removing temp file %s", | |
2072 | con->http.fd, con->filename); | |
8b43895a | 2073 | unlink(con->filename); |
589eb420 | 2074 | cupsdClearString(&con->filename); |
8b43895a | 2075 | |
2076 | if (con->request) | |
2077 | { | |
2078 | /* | |
2079 | * Delete any IPP request data... | |
2080 | */ | |
2081 | ||
2082 | ippDelete(con->request); | |
2083 | con->request = NULL; | |
2084 | } | |
2085 | ||
589eb420 | 2086 | if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE)) |
2087 | return (cupsdCloseClient(con)); | |
8b43895a | 2088 | } |
f63a2256 | 2089 | |
fc757c63 | 2090 | if (con->command) |
f63a2256 | 2091 | { |
589eb420 | 2092 | if (!cupsdSendCommand(con, con->command, con->options, 0)) |
f63a2256 | 2093 | { |
589eb420 | 2094 | if (!cupsdSendError(con, HTTP_NOT_FOUND)) |
2095 | return (cupsdCloseClient(con)); | |
f63a2256 | 2096 | } |
2097 | else | |
589eb420 | 2098 | cupsdLogRequest(con, HTTP_OK); |
f63a2256 | 2099 | } |
bfa1abf0 | 2100 | } |
93894a43 | 2101 | |
2102 | if (con->request) | |
589eb420 | 2103 | return (cupsdProcessIPPRequest(con)); |
93894a43 | 2104 | } |
a74b005d | 2105 | break; |
d21a7597 | 2106 | |
2107 | default : | |
2108 | break; /* Anti-compiler-warning-code */ | |
a74b005d | 2109 | } |
2110 | ||
2111 | if (!con->http.keep_alive && con->http.state == HTTP_WAITING) | |
589eb420 | 2112 | return (cupsdCloseClient(con)); |
a74b005d | 2113 | else |
2114 | return (1); | |
2115 | } | |
2116 | ||
2117 | ||
a74b005d | 2118 | /* |
589eb420 | 2119 | * 'cupsdSendCommand()' - Send output from a command via HTTP. |
a74b005d | 2120 | */ |
2121 | ||
1d5ef583 | 2122 | int /* O - 1 on success, 0 on failure */ |
f3e786fc | 2123 | cupsdSendCommand( |
2124 | cupsd_client_t *con, /* I - Client connection */ | |
2125 | char *command, /* I - Command to run */ | |
2126 | char *options, /* I - Command-line options */ | |
2127 | int root) /* I - Run as root? */ | |
a74b005d | 2128 | { |
1d5ef583 | 2129 | int fd; /* Standard input file descriptor */ |
f63a2256 | 2130 | |
2131 | ||
fc757c63 | 2132 | if (con->filename) |
f63a2256 | 2133 | fd = open(con->filename, O_RDONLY); |
2134 | else | |
2135 | fd = open("/dev/null", O_RDONLY); | |
2136 | ||
db3c5bfd | 2137 | if (fd < 0) |
2138 | { | |
f3e786fc | 2139 | cupsdLogMessage(CUPSD_LOG_ERROR, |
2140 | "cupsdSendCommand: %d Unable to open \"%s\" for reading: %s", | |
2141 | con->http.fd, con->filename ? con->filename : "/dev/null", | |
2142 | strerror(errno)); | |
db3c5bfd | 2143 | return (0); |
2144 | } | |
2145 | ||
2146 | fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); | |
2147 | ||
b38d93df | 2148 | con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root); |
f63a2256 | 2149 | |
2150 | close(fd); | |
a74b005d | 2151 | |
f3e786fc | 2152 | cupsdLogMessage(CUPSD_LOG_INFO, "Started \"%s\" (pid=%d)", command, |
2153 | con->pipe_pid); | |
b5cb0608 | 2154 | |
f3e786fc | 2155 | cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendCommand: %d file=%d", |
2156 | con->http.fd, con->file); | |
a74b005d | 2157 | |
2158 | if (con->pipe_pid == 0) | |
2159 | return (0); | |
2160 | ||
2161 | fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); | |
2162 | ||
f3e786fc | 2163 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2164 | "cupsdSendCommand: Adding fd %d to InputSet...", con->file); | |
2165 | cupsdLogMessage(CUPSD_LOG_DEBUG2, | |
2166 | "cupsdSendCommand: Adding fd %d to OutputSet...", | |
2167 | con->http.fd); | |
e668e627 | 2168 | |
f3bc1068 | 2169 | FD_SET(con->file, InputSet); |
2170 | FD_SET(con->http.fd, OutputSet); | |
a74b005d | 2171 | |
d694b7ed | 2172 | con->sent_header = 0; |
2173 | con->file_ready = 0; | |
2174 | con->got_fields = 0; | |
2175 | con->field_col = 0; | |
13486cb7 | 2176 | |
a74b005d | 2177 | return (1); |
2178 | } | |
2179 | ||
2180 | ||
2181 | /* | |
589eb420 | 2182 | * 'cupsdSendError()' - Send an error message via HTTP. |
a74b005d | 2183 | */ |
2184 | ||
f3e786fc | 2185 | int /* O - 1 if successful, 0 otherwise */ |
2186 | cupsdSendError(cupsd_client_t *con, /* I - Connection */ | |
2187 | http_status_t code) /* I - Error code */ | |
a74b005d | 2188 | { |
f3e786fc | 2189 | char message[1024]; /* Message for user */ |
a74b005d | 2190 | |
2191 | ||
6a0c519d | 2192 | /* |
2193 | * Put the request in the access_log file... | |
2194 | */ | |
2195 | ||
589eb420 | 2196 | cupsdLogRequest(con, code); |
6a0c519d | 2197 | |
f3e786fc | 2198 | cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendError: %d code=%d (%s)", |
2199 | con->http.fd, code, httpStatus(code)); | |
a61a2be6 | 2200 | |
a74b005d | 2201 | /* |
2202 | * To work around bugs in some proxies, don't use Keep-Alive for some | |
2203 | * error messages... | |
2204 | */ | |
2205 | ||
2206 | if (code >= HTTP_BAD_REQUEST) | |
2207 | con->http.keep_alive = HTTP_KEEPALIVE_OFF; | |
2208 | ||
2209 | /* | |
2210 | * Send an error message back to the client. If the error code is a | |
2211 | * 400 or 500 series, make sure the message contains some text, too! | |
2212 | */ | |
2213 | ||
589eb420 | 2214 | if (!cupsdSendHeader(con, code, NULL)) |
a74b005d | 2215 | return (0); |
2216 | ||
bcf61448 | 2217 | #ifdef HAVE_SSL |
a75c006a | 2218 | if (code == HTTP_UPGRADE_REQUIRED) |
a75c006a | 2219 | if (httpPrintf(HTTP(con), "Connection: Upgrade\r\n") < 0) |
2220 | return (0); | |
2221 | ||
b5cb0608 | 2222 | if (httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n") < 0) |
2223 | return (0); | |
bcf61448 | 2224 | #endif /* HAVE_SSL */ |
a75c006a | 2225 | |
55809c6f | 2226 | if ((con->http.version >= HTTP_1_1 && !con->http.keep_alive) || |
2227 | (code >= HTTP_BAD_REQUEST && code != HTTP_UPGRADE_REQUIRED)) | |
a74b005d | 2228 | { |
2229 | if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0) | |
2230 | return (0); | |
2231 | } | |
2232 | ||
2233 | if (code >= HTTP_BAD_REQUEST) | |
2234 | { | |
2235 | /* | |
2236 | * Send a human-readable error message. | |
2237 | */ | |
2238 | ||
04de52f8 | 2239 | snprintf(message, sizeof(message), |
970017a4 | 2240 | "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>" |
2241 | "<BODY><H1>%s</H1>%s</BODY></HTML>\n", | |
2242 | code, httpStatus(code), httpStatus(code), | |
2243 | con->language ? con->language->messages[code] : | |
2244 | httpStatus(code)); | |
a74b005d | 2245 | |
d694b7ed | 2246 | if (httpPrintf(HTTP(con), "Content-Type: text/html; charset=utf-8\r\n") < 0) |
a74b005d | 2247 | return (0); |
e0ba31cd | 2248 | if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", |
2249 | (int)strlen(message)) < 0) | |
a74b005d | 2250 | return (0); |
2251 | if (httpPrintf(HTTP(con), "\r\n") < 0) | |
2252 | return (0); | |
97d73ddb | 2253 | if (httpPrintf(HTTP(con), "%s", message) < 0) |
a74b005d | 2254 | return (0); |
2255 | } | |
2256 | else if (httpPrintf(HTTP(con), "\r\n") < 0) | |
2257 | return (0); | |
2258 | ||
2259 | con->http.state = HTTP_WAITING; | |
2260 | ||
2261 | return (1); | |
2262 | } | |
2263 | ||
2264 | ||
2265 | /* | |
589eb420 | 2266 | * 'cupsdSendFile()' - Send a file via HTTP. |
a74b005d | 2267 | */ |
2268 | ||
f3e786fc | 2269 | int /* O - 0 on failure, 1 on success */ |
2270 | cupsdSendFile(cupsd_client_t *con, /* I - Client connection */ | |
2271 | http_status_t code, /* I - HTTP status */ | |
2272 | char *filename, /* I - Filename */ | |
2273 | char *type, /* I - File type */ | |
2274 | struct stat *filestats)/* O - File information */ | |
a74b005d | 2275 | { |
2276 | con->file = open(filename, O_RDONLY); | |
2277 | ||
f3e786fc | 2278 | cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendFile: %d file=%d", con->http.fd, |
2279 | con->file); | |
a74b005d | 2280 | |
2281 | if (con->file < 0) | |
2282 | return (0); | |
2283 | ||
b2e10895 | 2284 | fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC); |
a74b005d | 2285 | |
2286 | con->pipe_pid = 0; | |
2287 | ||
589eb420 | 2288 | if (!cupsdSendHeader(con, code, type)) |
a74b005d | 2289 | return (0); |
2290 | ||
f3e786fc | 2291 | if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", |
2292 | httpGetDateString(filestats->st_mtime)) < 0) | |
a74b005d | 2293 | return (0); |
1479646d | 2294 | if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n", |
2295 | CUPS_LLCAST filestats->st_size) < 0) | |
a74b005d | 2296 | return (0); |
2297 | if (httpPrintf(HTTP(con), "\r\n") < 0) | |
2298 | return (0); | |
2299 | ||
f3e786fc | 2300 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2301 | "cupsdSendFile: Adding fd %d to OutputSet...", con->http.fd); | |
e668e627 | 2302 | |
f3bc1068 | 2303 | FD_SET(con->http.fd, OutputSet); |
a74b005d | 2304 | |
2305 | return (1); | |
2306 | } | |
2307 | ||
2308 | ||
2309 | /* | |
589eb420 | 2310 | * 'cupsdSendHeader()' - Send an HTTP request. |
a74b005d | 2311 | */ |
2312 | ||
f3e786fc | 2313 | int /* O - 1 on success, 0 on failure */ |
2314 | cupsdSendHeader(cupsd_client_t *con, /* I - Client to send to */ | |
2315 | http_status_t code, /* I - HTTP status code */ | |
2316 | char *type) /* I - MIME type of document */ | |
a74b005d | 2317 | { |
2318 | if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100, | |
ffe90a99 | 2319 | con->http.version % 100, code, httpStatus(code)) < 0) |
a74b005d | 2320 | return (0); |
2321 | if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0) | |
2322 | return (0); | |
90ad5874 | 2323 | if (ServerHeader) |
2324 | if (httpPrintf(HTTP(con), "Server: %s\r\n", ServerHeader) < 0) | |
2325 | return (0); | |
a74b005d | 2326 | if (con->http.keep_alive && con->http.version >= HTTP_1_0) |
2327 | { | |
2328 | if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0) | |
2329 | return (0); | |
f3e786fc | 2330 | if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", |
2331 | KeepAliveTimeout) < 0) | |
a74b005d | 2332 | return (0); |
2333 | } | |
8f85ff77 | 2334 | if (code == HTTP_METHOD_NOT_ALLOWED) |
9bfcbffc | 2335 | if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0) |
8f85ff77 | 2336 | return (0); |
2337 | ||
5ea8888e | 2338 | if (code == HTTP_UNAUTHORIZED) |
83e740a5 | 2339 | { |
a8c7842b | 2340 | int auth_type; /* Authentication type */ |
83e740a5 | 2341 | |
a8c7842b | 2342 | |
2343 | if (!con->best || con->best->type == AUTH_NONE) | |
2344 | auth_type = DefaultAuthType; | |
2345 | else | |
2346 | auth_type = con->best->type; | |
2347 | ||
2348 | if (auth_type != AUTH_DIGEST) | |
83e740a5 | 2349 | { |
f3e786fc | 2350 | if (httpPrintf(HTTP(con), |
2351 | "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0) | |
83e740a5 | 2352 | return (0); |
2353 | } | |
2354 | else | |
2355 | { | |
f3e786fc | 2356 | if (httpPrintf(HTTP(con), |
2357 | "WWW-Authenticate: Digest realm=\"CUPS\", nonce=\"%s\"\r\n", | |
2358 | con->http.hostname) < 0) | |
83e740a5 | 2359 | return (0); |
2360 | } | |
2361 | } | |
d694b7ed | 2362 | |
a74b005d | 2363 | if (con->language != NULL) |
2364 | { | |
2365 | if (httpPrintf(HTTP(con), "Content-Language: %s\r\n", | |
2366 | con->language->language) < 0) | |
2367 | return (0); | |
d694b7ed | 2368 | } |
a74b005d | 2369 | |
d694b7ed | 2370 | if (type != NULL) |
2371 | { | |
2372 | if (!strcmp(type, "text/html")) | |
2373 | { | |
f3e786fc | 2374 | if (httpPrintf(HTTP(con), |
2375 | "Content-Type: text/html; charset=utf-8\r\n") < 0) | |
a74b005d | 2376 | return (0); |
d694b7ed | 2377 | } |
2378 | else if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0) | |
a74b005d | 2379 | return (0); |
d694b7ed | 2380 | } |
a74b005d | 2381 | |
2382 | return (1); | |
2383 | } | |
2384 | ||
2385 | ||
0a968cfb | 2386 | /* |
589eb420 | 2387 | * 'cupsdUpdateCGI()' - Read status messages from CGI scripts and programs. |
0a968cfb | 2388 | */ |
2389 | ||
2390 | void | |
589eb420 | 2391 | cupsdUpdateCGI(void) |
0a968cfb | 2392 | { |
fd09381d | 2393 | char *ptr, /* Pointer to end of line in buffer */ |
2394 | message[1024]; /* Pointer to message text */ | |
2395 | int loglevel; /* Log level for message */ | |
0a968cfb | 2396 | |
2397 | ||
fd09381d | 2398 | while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel, |
2399 | message, sizeof(message))) != NULL) | |
2400 | if (!strchr(CGIStatusBuffer->buffer, '\n')) | |
2401 | break; | |
0a968cfb | 2402 | |
fd09381d | 2403 | if (ptr == NULL) |
0a968cfb | 2404 | { |
2405 | /* | |
2406 | * Fatal error on pipe - should never happen! | |
2407 | */ | |
2408 | ||
f3e786fc | 2409 | cupsdLogMessage(CUPSD_LOG_CRIT, |
2410 | "cupsdUpdateCGI: error reading from CGI error pipe - %s", | |
2411 | strerror(errno)); | |
0a968cfb | 2412 | } |
2413 | } | |
2414 | ||
2415 | ||
a74b005d | 2416 | /* |
589eb420 | 2417 | * 'cupsdWriteClient()' - Write data to a client as needed. |
a74b005d | 2418 | */ |
2419 | ||
ff49100f | 2420 | int /* O - 1 if success, 0 if fail */ |
f3e786fc | 2421 | cupsdWriteClient(cupsd_client_t *con) /* I - Client connection */ |
a74b005d | 2422 | { |
ff49100f | 2423 | int bytes; /* Number of bytes written */ |
ad4be4c3 | 2424 | char buf[16385]; /* Data buffer */ |
13486cb7 | 2425 | char *bufptr; /* Pointer into buffer */ |
ff49100f | 2426 | ipp_state_t ipp_state; /* IPP state value */ |
a74b005d | 2427 | |
2428 | ||
6a2cceab | 2429 | #ifdef DEBUG |
f3e786fc | 2430 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2431 | "cupsdWriteClient(con=%p) %d response=%p, file=%d pipe_pid=%d", | |
2432 | con, con->http.fd, con->response, con->file, con->pipe_pid); | |
6a2cceab | 2433 | #endif /* DEBUG */ |
2434 | ||
a74b005d | 2435 | if (con->http.state != HTTP_GET_SEND && |
2436 | con->http.state != HTTP_POST_SEND) | |
2437 | return (1); | |
2438 | ||
ff49100f | 2439 | if (con->response != NULL) |
bfa1abf0 | 2440 | { |
ff49100f | 2441 | ipp_state = ippWrite(&(con->http), con->response); |
2442 | bytes = ipp_state != IPP_ERROR && ipp_state != IPP_DATA; | |
bfa1abf0 | 2443 | } |
ad4be4c3 | 2444 | else if ((bytes = read(con->file, buf, sizeof(buf) - 1)) > 0) |
a74b005d | 2445 | { |
f3e786fc | 2446 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2447 | "cupsdWriteClient: Read %d bytes from file %d...", | |
2448 | bytes, con->file); | |
6a2cceab | 2449 | |
13486cb7 | 2450 | if (con->pipe_pid && !con->got_fields) |
2451 | { | |
2452 | /* | |
2453 | * Inspect the data for Content-Type and other fields. | |
2454 | */ | |
2455 | ||
2456 | buf[bytes] = '\0'; | |
2457 | ||
2458 | for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++) | |
2459 | if (*bufptr == '\n') | |
2460 | { | |
2461 | /* | |
2462 | * Send line to client... | |
2463 | */ | |
2464 | ||
2465 | if (bufptr > buf && bufptr[-1] == '\r') | |
2466 | bufptr[-1] = '\0'; | |
2467 | *bufptr++ = '\0'; | |
2468 | ||
f3e786fc | 2469 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "Script header: %s", buf); |
45d9c992 | 2470 | |
d694b7ed | 2471 | if (!con->sent_header) |
2472 | { | |
2473 | /* | |
2474 | * Handle redirection and CGI status codes... | |
2475 | */ | |
2476 | ||
2477 | if (!strncasecmp(buf, "Location:", 9)) | |
589eb420 | 2478 | cupsdSendHeader(con, HTTP_SEE_OTHER, NULL); |
d694b7ed | 2479 | else if (!strncasecmp(buf, "Status:", 7)) |
589eb420 | 2480 | cupsdSendHeader(con, atoi(buf + 7), NULL); |
d694b7ed | 2481 | else |
589eb420 | 2482 | cupsdSendHeader(con, HTTP_OK, NULL); |
d694b7ed | 2483 | |
2484 | if (con->http.version == HTTP_1_1) | |
2485 | { | |
2486 | con->http.data_encoding = HTTP_ENCODE_CHUNKED; | |
2487 | ||
2488 | if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0) | |
2489 | return (0); | |
2490 | } | |
2491 | ||
2492 | con->sent_header = 1; | |
2493 | } | |
2494 | ||
2495 | if (strncasecmp(buf, "Status:", 7)) | |
2496 | httpPrintf(HTTP(con), "%s\r\n", buf); | |
2497 | ||
f3e786fc | 2498 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdWriteClient: %d %s", |
2499 | con->http.fd, buf); | |
13486cb7 | 2500 | |
2501 | /* | |
2502 | * Update buffer... | |
2503 | */ | |
2504 | ||
2505 | bytes -= (bufptr - buf); | |
6a2cceab | 2506 | memmove(buf, bufptr, bytes + 1); |
13486cb7 | 2507 | bufptr = buf - 1; |
2508 | ||
2509 | /* | |
2510 | * See if the line was empty... | |
2511 | */ | |
2512 | ||
2513 | if (con->field_col == 0) | |
2514 | con->got_fields = 1; | |
2515 | else | |
2516 | con->field_col = 0; | |
2517 | } | |
2518 | else if (*bufptr != '\r') | |
2519 | con->field_col ++; | |
2520 | ||
f3e786fc | 2521 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2522 | "cupsdWriteClient: %d bytes=%d, got_fields=%d", | |
2523 | con->http.fd, bytes, con->got_fields); | |
ed5ceafb | 2524 | |
13486cb7 | 2525 | if (bytes > 0 && !con->got_fields) |
2526 | { | |
2527 | /* | |
2528 | * Remaining text needs to go out... | |
2529 | */ | |
2530 | ||
2531 | httpPrintf(HTTP(con), "%s", buf); | |
2532 | ||
2533 | con->http.activity = time(NULL); | |
2534 | return (1); | |
2535 | } | |
2536 | else if (bytes == 0) | |
2537 | { | |
2538 | con->http.activity = time(NULL); | |
2539 | return (1); | |
2540 | } | |
2541 | } | |
2542 | ||
a74b005d | 2543 | if (httpWrite(HTTP(con), buf, bytes) < 0) |
2544 | { | |
f3e786fc | 2545 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2546 | "cupsdWriteClient: %d Write of %d bytes failed!", | |
2547 | con->http.fd, bytes); | |
ed5ceafb | 2548 | |
589eb420 | 2549 | cupsdCloseClient(con); |
a74b005d | 2550 | return (0); |
2551 | } | |
6a0c519d | 2552 | |
2553 | con->bytes += bytes; | |
a74b005d | 2554 | } |
b14d90ba | 2555 | |
2556 | if (bytes <= 0) | |
a74b005d | 2557 | { |
f3e786fc | 2558 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdWriteClient: %d bytes < 0", |
2559 | con->http.fd); | |
ed5ceafb | 2560 | |
589eb420 | 2561 | cupsdLogRequest(con, HTTP_OK); |
6a0c519d | 2562 | |
d12c5b68 | 2563 | httpFlushWrite(HTTP(con)); |
2564 | ||
a74b005d | 2565 | if (con->http.data_encoding == HTTP_ENCODE_CHUNKED) |
2566 | { | |
2567 | if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0) | |
2568 | { | |
589eb420 | 2569 | cupsdCloseClient(con); |
a74b005d | 2570 | return (0); |
2571 | } | |
2572 | } | |
2573 | ||
96df88bb | 2574 | con->http.state = HTTP_WAITING; |
2575 | ||
f3e786fc | 2576 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2577 | "cupsdWriteClient: Removing fd %d from OutputSet...", | |
2578 | con->http.fd); | |
e668e627 | 2579 | |
f3bc1068 | 2580 | FD_CLR(con->http.fd, OutputSet); |
a74b005d | 2581 | |
6a2cceab | 2582 | if (con->file >= 0) |
a74b005d | 2583 | { |
6a2cceab | 2584 | if (FD_ISSET(con->file, InputSet)) |
2585 | { | |
f3e786fc | 2586 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2587 | "cupsdWriteClient: Removing fd %d from InputSet...", | |
2588 | con->file); | |
6a2cceab | 2589 | FD_CLR(con->file, InputSet); |
2590 | } | |
b2e10895 | 2591 | |
bfa1abf0 | 2592 | if (con->pipe_pid) |
c316e838 | 2593 | cupsdEndProcess(con->pipe_pid, 0); |
bfa1abf0 | 2594 | |
f3e786fc | 2595 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2596 | "cupsdWriteClient: %d Closing data file %d.", | |
2597 | con->http.fd, con->file); | |
bd917997 | 2598 | |
bfa1abf0 | 2599 | close(con->file); |
6a2cceab | 2600 | con->file = -1; |
96df88bb | 2601 | con->pipe_pid = 0; |
bfa1abf0 | 2602 | } |
a74b005d | 2603 | |
fc757c63 | 2604 | if (con->filename) |
a61a2be6 | 2605 | { |
f3e786fc | 2606 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2607 | "cupsdWriteClient: %d Removing temp file %s", | |
2608 | con->http.fd, con->filename); | |
d4c438d4 | 2609 | unlink(con->filename); |
589eb420 | 2610 | cupsdClearString(&con->filename); |
a61a2be6 | 2611 | } |
d4c438d4 | 2612 | |
b14d90ba | 2613 | if (con->request != NULL) |
2614 | { | |
2615 | ippDelete(con->request); | |
2616 | con->request = NULL; | |
2617 | } | |
2618 | ||
2619 | if (con->response != NULL) | |
2620 | { | |
2621 | ippDelete(con->response); | |
2622 | con->response = NULL; | |
2623 | } | |
96df88bb | 2624 | |
589eb420 | 2625 | cupsdClearString(&con->command); |
2626 | cupsdClearString(&con->options); | |
fc757c63 | 2627 | |
96df88bb | 2628 | if (!con->http.keep_alive) |
2629 | { | |
589eb420 | 2630 | cupsdCloseClient(con); |
96df88bb | 2631 | return (0); |
2632 | } | |
a74b005d | 2633 | } |
6a2cceab | 2634 | else |
2635 | { | |
2636 | con->file_ready = 0; | |
2637 | ||
2638 | if (con->pipe_pid && !FD_ISSET(con->file, InputSet)) | |
2639 | { | |
f3e786fc | 2640 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2641 | "cupsdWriteClient: Adding fd %d to InputSet...", | |
2642 | con->file); | |
6a2cceab | 2643 | FD_SET(con->file, InputSet); |
2644 | } | |
2645 | } | |
a74b005d | 2646 | |
a74b005d | 2647 | con->http.activity = time(NULL); |
2648 | ||
2649 | return (1); | |
2650 | } | |
2651 | ||
2652 | ||
a74b005d | 2653 | /* |
2654 | * 'check_if_modified()' - Decode an "If-Modified-Since" line. | |
2655 | */ | |
2656 | ||
f3e786fc | 2657 | static int /* O - 1 if modified since */ |
2658 | check_if_modified( | |
2659 | cupsd_client_t *con, /* I - Client connection */ | |
2660 | struct stat *filestats) /* I - File information */ | |
a74b005d | 2661 | { |
f3e786fc | 2662 | char *ptr; /* Pointer into field */ |
2663 | time_t date; /* Time/date value */ | |
1479646d | 2664 | off_t size; /* Size/length value */ |
a74b005d | 2665 | |
2666 | ||
2667 | size = 0; | |
2668 | date = 0; | |
2669 | ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE]; | |
2670 | ||
2671 | if (*ptr == '\0') | |
2672 | return (1); | |
2673 | ||
f3e786fc | 2674 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2675 | "check_if_modified: %d If-Modified-Since=\"%s\"", | |
2676 | con->http.fd, ptr); | |
4a64fdb7 | 2677 | |
a74b005d | 2678 | while (*ptr != '\0') |
2679 | { | |
4a64fdb7 | 2680 | while (isspace(*ptr) || *ptr == ';') |
a74b005d | 2681 | ptr ++; |
2682 | ||
2683 | if (strncasecmp(ptr, "length=", 7) == 0) | |
2684 | { | |
2685 | ptr += 7; | |
1479646d | 2686 | size = strtoll(ptr, NULL, 10); |
a74b005d | 2687 | |
2688 | while (isdigit(*ptr)) | |
2689 | ptr ++; | |
2690 | } | |
4a64fdb7 | 2691 | else if (isalpha(*ptr)) |
a74b005d | 2692 | { |
2693 | date = httpGetDateTime(ptr); | |
4a64fdb7 | 2694 | while (*ptr != '\0' && *ptr != ';') |
a74b005d | 2695 | ptr ++; |
2696 | } | |
2697 | } | |
2698 | ||
f3e786fc | 2699 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
1479646d | 2700 | "check_if_modified: %d sizes=" CUPS_LLFMT "," |
2701 | CUPS_LLFMT " dates=%d,%d", | |
2702 | con->http.fd, CUPS_LLCAST size, | |
2703 | CUPS_LLCAST filestats->st_size, (int)date, | |
f3e786fc | 2704 | (int)filestats->st_mtime); |
4a64fdb7 | 2705 | |
a74b005d | 2706 | return ((size != filestats->st_size && size != 0) || |
4a64fdb7 | 2707 | (date < filestats->st_mtime && date != 0) || |
2708 | (size == 0 && date == 0)); | |
a74b005d | 2709 | } |
2710 | ||
2711 | ||
2712 | /* | |
d2122fde | 2713 | * 'decode_auth()' - Decode an authorization string. |
a74b005d | 2714 | */ |
2715 | ||
93894a43 | 2716 | static void |
f3e786fc | 2717 | decode_auth(cupsd_client_t *con) /* I - Client to decode to */ |
a74b005d | 2718 | { |
d2122fde | 2719 | char *s, /* Authorization string */ |
2720 | value[1024]; /* Value string */ | |
2721 | const char *username; /* Certificate username */ | |
a74b005d | 2722 | |
a74b005d | 2723 | |
2724 | /* | |
b0d58d07 | 2725 | * Decode the string... |
a74b005d | 2726 | */ |
2727 | ||
f3d580b9 | 2728 | s = con->http.fields[HTTP_FIELD_AUTHORIZATION]; |
f3d580b9 | 2729 | |
f3e786fc | 2730 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
2731 | "decode_auth(%p): Authorization string = \"%s\"", con, s); | |
69f14ebb | 2732 | |
f3e786fc | 2733 | if (!strncmp(s, "Basic", 5)) |
d2122fde | 2734 | { |
2735 | s += 5; | |
2736 | while (isspace(*s)) | |
2737 | s ++; | |
f3d580b9 | 2738 | |
d2122fde | 2739 | httpDecode64(value, s); |
a74b005d | 2740 | |
d2122fde | 2741 | /* |
2742 | * Pull the username and password out... | |
2743 | */ | |
b0d58d07 | 2744 | |
d2122fde | 2745 | if ((s = strchr(value, ':')) == NULL) |
2746 | { | |
f3e786fc | 2747 | cupsdLogMessage(CUPSD_LOG_DEBUG, |
2748 | "decode_auth: %d no colon in auth string \"%s\"", | |
2749 | con->http.fd, value); | |
d2122fde | 2750 | return; |
2751 | } | |
2752 | ||
2753 | *s++ = '\0'; | |
b0d58d07 | 2754 | |
def978d5 | 2755 | strlcpy(con->username, value, sizeof(con->username)); |
2756 | strlcpy(con->password, s, sizeof(con->password)); | |
d2122fde | 2757 | } |
f3e786fc | 2758 | else if (!strncmp(s, "Local", 5)) |
d2122fde | 2759 | { |
2760 | s += 5; | |
2761 | while (isspace(*s)) | |
2762 | s ++; | |
b0d58d07 | 2763 | |
589eb420 | 2764 | if ((username = cupsdFindCert(s)) != NULL) |
def978d5 | 2765 | strlcpy(con->username, username, sizeof(con->username)); |
d2122fde | 2766 | } |
f3e786fc | 2767 | else if (!strncmp(s, "Digest", 6)) |
83e740a5 | 2768 | { |
2769 | /* | |
2770 | * Get the username and password from the Digest attributes... | |
2771 | */ | |
2772 | ||
b2e10895 | 2773 | if (httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "username", |
83e740a5 | 2774 | value)) |
def978d5 | 2775 | strlcpy(con->username, value, sizeof(con->username)); |
83e740a5 | 2776 | |
b2e10895 | 2777 | if (httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "response", |
83e740a5 | 2778 | value)) |
b2e10895 | 2779 | strlcpy(con->password, value, sizeof(con->password)); |
83e740a5 | 2780 | } |
41637133 | 2781 | |
f3e786fc | 2782 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "decode_auth: %d username=\"%s\"", |
2783 | con->http.fd, con->username); | |
a74b005d | 2784 | } |
2785 | ||
2786 | ||
b2e10895 | 2787 | /* |
2788 | * 'get_file()' - Get a filename and state info. | |
2789 | */ | |
2790 | ||
fa84bfc7 | 2791 | static char * /* O - Real filename */ |
f3e786fc | 2792 | get_file(cupsd_client_t *con, /* I - Client connection */ |
2793 | struct stat *filestats, /* O - File information */ | |
2794 | char *filename, /* IO - Filename buffer */ | |
2795 | int len) /* I - Buffer length */ | |
e31bfb6e | 2796 | { |
fa84bfc7 | 2797 | int status; /* Status of filesystem calls */ |
2798 | char *ptr; /* Pointer info filename */ | |
2799 | int plen; /* Remaining length after pointer */ | |
e31bfb6e | 2800 | |
2801 | ||
2802 | /* | |
2803 | * Need to add DocumentRoot global... | |
2804 | */ | |
2805 | ||
d694b7ed | 2806 | if (!strncmp(con->uri, "/ppd/", 5)) |
6db7190f | 2807 | snprintf(filename, len, "%s%s", ServerRoot, con->uri); |
d694b7ed | 2808 | else if (!strncmp(con->uri, "/admin/conf/", 12)) |
6db7190f | 2809 | snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11); |
d694b7ed | 2810 | else if (!strncmp(con->uri, "/admin/log/", 11)) |
2811 | { | |
2812 | if (!strcmp(con->uri + 11, "access_log") && AccessLog[0] == '/') | |
2813 | strlcpy(filename, AccessLog, len); | |
2814 | else if (!strcmp(con->uri + 11, "error_log") && ErrorLog[0] == '/') | |
2815 | strlcpy(filename, ErrorLog, len); | |
2816 | else if (!strcmp(con->uri + 11, "page_log") && PageLog[0] == '/') | |
2817 | strlcpy(filename, PageLog, len); | |
2818 | else | |
2819 | return (NULL); | |
2820 | } | |
f3d580b9 | 2821 | else if (con->language != NULL) |
6db7190f | 2822 | snprintf(filename, len, "%s/%s%s", DocumentRoot, con->language->language, |
e31bfb6e | 2823 | con->uri); |
2824 | else | |
6db7190f | 2825 | snprintf(filename, len, "%s%s", DocumentRoot, con->uri); |
e31bfb6e | 2826 | |
fa84bfc7 | 2827 | if ((ptr = strchr(filename, '?')) != NULL) |
2828 | *ptr = '\0'; | |
e31bfb6e | 2829 | |
2830 | /* | |
2831 | * Grab the status for this language; if there isn't a language-specific file | |
2832 | * then fallback to the default one... | |
2833 | */ | |
2834 | ||
2835 | if ((status = stat(filename, filestats)) != 0 && con->language != NULL) | |
2836 | { | |
2837 | /* | |
2838 | * Drop the language prefix and try the current directory... | |
2839 | */ | |
2840 | ||
d2e43d2e | 2841 | if (strncmp(con->uri, "/ppd/", 5) && |
2842 | strncmp(con->uri, "/admin/conf/", 12) && | |
2843 | strncmp(con->uri, "/admin/log/", 11)) | |
f3d580b9 | 2844 | { |
6db7190f | 2845 | snprintf(filename, len, "%s%s", DocumentRoot, con->uri); |
e31bfb6e | 2846 | |
fa84bfc7 | 2847 | if ((ptr = strchr(filename, '?')) != NULL) |
2848 | *ptr = '\0'; | |
2849 | ||
f3d580b9 | 2850 | status = stat(filename, filestats); |
2851 | } | |
e31bfb6e | 2852 | } |
2853 | ||
2854 | /* | |
2855 | * If we're found a directory, get the index.html file instead... | |
2856 | */ | |
2857 | ||
2858 | if (!status && S_ISDIR(filestats->st_mode)) | |
2859 | { | |
fa84bfc7 | 2860 | if (filename[strlen(filename) - 1] != '/') |
2861 | strlcat(filename, "/", len); | |
e31bfb6e | 2862 | |
fa84bfc7 | 2863 | ptr = filename + strlen(filename); |
2864 | plen = len - (ptr - filename); | |
2865 | ||
2866 | strlcpy(ptr, "index.html", plen); | |
e31bfb6e | 2867 | status = stat(filename, filestats); |
fa84bfc7 | 2868 | |
2869 | #ifdef HAVE_JAVA | |
2870 | if (status) | |
2871 | { | |
2872 | strlcpy(ptr, "index.class", plen); | |
2873 | status = stat(filename, filestats); | |
2874 | } | |
2875 | #endif /* HAVE_JAVA */ | |
2876 | ||
2877 | #ifdef HAVE_PERL | |
2878 | if (status) | |
2879 | { | |
2880 | strlcpy(ptr, "index.pl", plen); | |
2881 | status = stat(filename, filestats); | |
2882 | } | |
2883 | #endif /* HAVE_PERL */ | |
2884 | ||
2885 | #ifdef HAVE_PHP | |
2886 | if (status) | |
2887 | { | |
2888 | strlcpy(ptr, "index.php", plen); | |
2889 | status = stat(filename, filestats); | |
2890 | } | |
2891 | #endif /* HAVE_PHP */ | |
2892 | ||
2893 | #ifdef HAVE_PYTHON | |
2894 | if (status) | |
2895 | { | |
2896 | strlcpy(ptr, "index.pyc", plen); | |
2897 | status = stat(filename, filestats); | |
2898 | } | |
2899 | ||
2900 | if (status) | |
2901 | { | |
2902 | strlcpy(ptr, "index.py", plen); | |
2903 | status = stat(filename, filestats); | |
2904 | } | |
2905 | #endif /* HAVE_PYTHON */ | |
e31bfb6e | 2906 | } |
2907 | ||
f3e786fc | 2908 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_file: %d filename=%s size=%d", |
2909 | con->http.fd, filename, | |
2910 | status ? -1 : (int)filestats->st_size); | |
e31bfb6e | 2911 | |
ad4be4c3 | 2912 | if (!status) |
2913 | con->http.data_remaining = (int)filestats->st_size; | |
2914 | ||
e31bfb6e | 2915 | if (status) |
2916 | return (NULL); | |
2917 | else | |
2918 | return (filename); | |
2919 | } | |
2920 | ||
2921 | ||
753453e4 | 2922 | /* |
2923 | * 'install_conf_file()' - Install a configuration file. | |
2924 | */ | |
2925 | ||
2926 | static http_status_t /* O - Status */ | |
589eb420 | 2927 | install_conf_file(cupsd_client_t *con) /* I - Connection */ |
753453e4 | 2928 | { |
7b0fde61 | 2929 | cups_file_t *in, /* Input file */ |
753453e4 | 2930 | *out; /* Output file */ |
2931 | char buffer[1024]; /* Copy buffer */ | |
2932 | int bytes; /* Number of bytes */ | |
2933 | char conffile[1024], /* Configuration filename */ | |
2934 | newfile[1024], /* New config filename */ | |
2935 | oldfile[1024]; /* Old config filename */ | |
2936 | struct stat confinfo; /* Config file info */ | |
2937 | ||
2938 | ||
2939 | /* | |
2940 | * First construct the filenames... | |
2941 | */ | |
2942 | ||
2943 | snprintf(conffile, sizeof(conffile), "%s%s", ServerRoot, con->uri + 11); | |
2944 | snprintf(newfile, sizeof(newfile), "%s%s.N", ServerRoot, con->uri + 11); | |
2945 | snprintf(oldfile, sizeof(oldfile), "%s%s.O", ServerRoot, con->uri + 11); | |
2946 | ||
f3e786fc | 2947 | cupsdLogMessage(CUPSD_LOG_INFO, "Installing config file \"%s\"...", conffile); |
753453e4 | 2948 | |
2949 | /* | |
2950 | * Get the owner, group, and permissions of the configuration file. | |
2951 | * If it doesn't exist, assign it to the User and Group in the | |
2952 | * cupsd.conf file with mode 0640 permissions. | |
2953 | */ | |
2954 | ||
2955 | if (stat(conffile, &confinfo)) | |
2956 | { | |
2957 | confinfo.st_uid = User; | |
2958 | confinfo.st_gid = Group; | |
d59a189c | 2959 | confinfo.st_mode = ConfigFilePerm; |
753453e4 | 2960 | } |
2961 | ||
2962 | /* | |
2963 | * Open the request file and new config file... | |
2964 | */ | |
2965 | ||
7b0fde61 | 2966 | if ((in = cupsFileOpen(con->filename, "rb")) == NULL) |
753453e4 | 2967 | { |
f3e786fc | 2968 | cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open request file \"%s\" - %s", |
2969 | con->filename, strerror(errno)); | |
753453e4 | 2970 | return (HTTP_SERVER_ERROR); |
2971 | } | |
2972 | ||
7b0fde61 | 2973 | if ((out = cupsFileOpen(newfile, "wb")) == NULL) |
753453e4 | 2974 | { |
7b0fde61 | 2975 | cupsFileClose(in); |
f3e786fc | 2976 | cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open config file \"%s\" - %s", |
2977 | newfile, strerror(errno)); | |
753453e4 | 2978 | return (HTTP_SERVER_ERROR); |
2979 | } | |
2980 | ||
7b0fde61 | 2981 | fchmod(cupsFileNumber(out), confinfo.st_mode); |
2982 | fchown(cupsFileNumber(out), confinfo.st_uid, confinfo.st_gid); | |
753453e4 | 2983 | |
2984 | /* | |
2985 | * Copy from the request to the new config file... | |
2986 | */ | |
2987 | ||
7b0fde61 | 2988 | while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0) |
2989 | if (cupsFileWrite(out, buffer, bytes) < bytes) | |
753453e4 | 2990 | { |
f3e786fc | 2991 | cupsdLogMessage(CUPSD_LOG_ERROR, |
2992 | "Unable to copy to config file \"%s\" - %s", | |
2993 | newfile, strerror(errno)); | |
753453e4 | 2994 | |
7b0fde61 | 2995 | cupsFileClose(in); |
2996 | cupsFileClose(out); | |
753453e4 | 2997 | unlink(newfile); |
2998 | ||
2999 | return (HTTP_SERVER_ERROR); | |
3000 | } | |
3001 | ||
3002 | /* | |
3003 | * Close the files... | |
3004 | */ | |
3005 | ||
7b0fde61 | 3006 | cupsFileClose(in); |
3007 | if (cupsFileClose(out)) | |
753453e4 | 3008 | { |
f3e786fc | 3009 | cupsdLogMessage(CUPSD_LOG_ERROR, |
3010 | "Error file closing config file \"%s\" - %s", | |
3011 | newfile, strerror(errno)); | |
753453e4 | 3012 | |
3013 | unlink(newfile); | |
3014 | ||
3015 | return (HTTP_SERVER_ERROR); | |
3016 | } | |
3017 | ||
3018 | /* | |
3019 | * Remove the request file... | |
3020 | */ | |
3021 | ||
3022 | unlink(con->filename); | |
589eb420 | 3023 | cupsdClearString(&con->filename); |
753453e4 | 3024 | |
3025 | /* | |
3026 | * Unlink the old backup, rename the current config file to the backup | |
3027 | * filename, and rename the new config file to the config file name... | |
3028 | */ | |
3029 | ||
3030 | if (unlink(oldfile)) | |
3031 | if (errno != ENOENT) | |
3032 | { | |
f3e786fc | 3033 | cupsdLogMessage(CUPSD_LOG_ERROR, |
3034 | "Unable to remove backup config file \"%s\" - %s", | |
3035 | oldfile, strerror(errno)); | |
753453e4 | 3036 | |
3037 | unlink(newfile); | |
3038 | ||
3039 | return (HTTP_SERVER_ERROR); | |
3040 | } | |
3041 | ||
3042 | if (rename(conffile, oldfile)) | |
3043 | if (errno != ENOENT) | |
3044 | { | |
f3e786fc | 3045 | cupsdLogMessage(CUPSD_LOG_ERROR, |
3046 | "Unable to rename old config file \"%s\" - %s", | |
3047 | conffile, strerror(errno)); | |
753453e4 | 3048 | |
3049 | unlink(newfile); | |
3050 | ||
3051 | return (HTTP_SERVER_ERROR); | |
3052 | } | |
3053 | ||
3054 | if (rename(newfile, conffile)) | |
3055 | { | |
f3e786fc | 3056 | cupsdLogMessage(CUPSD_LOG_ERROR, |
3057 | "Unable to rename new config file \"%s\" - %s", | |
3058 | newfile, strerror(errno)); | |
753453e4 | 3059 | |
3060 | rename(oldfile, conffile); | |
3061 | unlink(newfile); | |
3062 | ||
3063 | return (HTTP_SERVER_ERROR); | |
3064 | } | |
3065 | ||
3066 | /* | |
3067 | * If the cupsd.conf file was updated, set the NeedReload flag... | |
3068 | */ | |
3069 | ||
d2e43d2e | 3070 | if (!strcmp(con->uri, "/admin/conf/cupsd.conf")) |
53510eae | 3071 | NeedReload = RELOAD_CUPSD; |
3072 | else | |
3073 | NeedReload = RELOAD_ALL; | |
753453e4 | 3074 | |
a782f61f | 3075 | ReloadTime = time(NULL); |
3076 | ||
753453e4 | 3077 | /* |
3078 | * Return that the file was created successfully... | |
3079 | */ | |
3080 | ||
3081 | return (HTTP_CREATED); | |
3082 | } | |
3083 | ||
3084 | ||
3780dd72 | 3085 | /* |
3086 | * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. ".."). | |
3087 | */ | |
3088 | ||
3089 | static int /* O - 0 if relative, 1 if absolute */ | |
3090 | is_path_absolute(const char *path) /* I - Input path */ | |
3091 | { | |
3092 | /* | |
3093 | * Check for a leading slash... | |
3094 | */ | |
3095 | ||
3096 | if (path[0] != '/') | |
3097 | return (0); | |
3098 | ||
3099 | /* | |
3100 | * Check for "/.." in the path... | |
3101 | */ | |
3102 | ||
3103 | while ((path = strstr(path, "/..")) != NULL) | |
98fe75ea | 3104 | { |
3780dd72 | 3105 | if (!path[3] || path[3] == '/') |
3106 | return (0); | |
3107 | ||
98fe75ea | 3108 | path ++; |
3109 | } | |
3110 | ||
3780dd72 | 3111 | /* |
3112 | * If we haven't found any relative paths, return 1 indicating an | |
3113 | * absolute path... | |
3114 | */ | |
3115 | ||
3116 | return (1); | |
3117 | } | |
3118 | ||
3119 | ||
a74b005d | 3120 | /* |
3121 | * 'pipe_command()' - Pipe the output of a command to the remote client. | |
3122 | */ | |
3123 | ||
879062a9 | 3124 | static int /* O - Process ID */ |
f3e786fc | 3125 | pipe_command(cupsd_client_t *con, /* I - Client connection */ |
3126 | int infile, /* I - Standard input for command */ | |
3127 | int *outfile, /* O - Standard output for command */ | |
3128 | char *command, /* I - Command to run */ | |
3129 | char *options, /* I - Options for command */ | |
3130 | int root) /* I - Run as root? */ | |
a74b005d | 3131 | { |
2ae14c39 | 3132 | int i; /* Looping var */ |
879062a9 | 3133 | int pid; /* Process ID */ |
3134 | char *commptr; /* Command string pointer */ | |
e420f467 | 3135 | char *uriptr; /* URI string pointer */ |
879062a9 | 3136 | int fds[2]; /* Pipe FDs */ |
3137 | int argc; /* Number of arguments */ | |
3138 | int envc; /* Number of environment variables */ | |
3139 | char argbuf[10240], /* Argument buffer */ | |
3140 | *argv[100], /* Argument strings */ | |
3141 | *envp[100]; /* Environment variables */ | |
2ae14c39 | 3142 | char content_length[1024], /* CONTENT_LENGTH environment variable */ |
3143 | content_type[1024], /* CONTENT_TYPE environment variable */ | |
2ae14c39 | 3144 | http_cookie[1024], /* HTTP_COOKIE environment variable */ |
3145 | http_user_agent[1024], /* HTTP_USER_AGENT environment variable */ | |
2ae14c39 | 3146 | lang[1024], /* LANG environment variable */ |
f6b55e0c | 3147 | *query_string, /* QUERY_STRING env variable */ |
2ae14c39 | 3148 | remote_addr[1024], /* REMOTE_ADDR environment variable */ |
3149 | remote_host[1024], /* REMOTE_HOST environment variable */ | |
3150 | remote_user[1024], /* REMOTE_USER environment variable */ | |
3151 | script_name[1024], /* SCRIPT_NAME environment variable */ | |
3152 | server_name[1024], /* SERVER_NAME environment variable */ | |
291355eb | 3153 | server_port[1024]; /* SERVER_PORT environment variable */ |
a74b005d | 3154 | |
05e63c18 | 3155 | |
a74b005d | 3156 | /* |
f6b55e0c | 3157 | * Parse a copy of the options string, which is of the form: |
3158 | * | |
3159 | * name argument+argument+argument | |
ff346847 | 3160 | * name?argument+argument+argument |
f6b55e0c | 3161 | * name param=value¶m=value |
ff346847 | 3162 | * name?param=value¶m=value |
f6b55e0c | 3163 | * |
3164 | * If the string contains an "=" character after the initial name, | |
3165 | * then we treat it as a HTTP GET form request and make a copy of | |
3166 | * the remaining string for the environment variable. | |
3167 | * | |
3168 | * The string is always parsed out as command-line arguments, to | |
3169 | * be consistent with Apache... | |
a74b005d | 3170 | */ |
3171 | ||
f3e786fc | 3172 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
3173 | "pipe_command: command=\"%s\", options=\"%s\"", | |
3174 | command, options); | |
ff346847 | 3175 | |
def978d5 | 3176 | strlcpy(argbuf, options, sizeof(argbuf)); |
a74b005d | 3177 | |
f6b55e0c | 3178 | argv[0] = argbuf; |
3179 | query_string = NULL; | |
a74b005d | 3180 | |
bfa1abf0 | 3181 | for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++) |
f6b55e0c | 3182 | { |
3183 | /* | |
3184 | * Break arguments whenever we see a + or space... | |
3185 | */ | |
3186 | ||
ff346847 | 3187 | if (*commptr == ' ' || *commptr == '+' || (*commptr == '?' && argc == 1)) |
a74b005d | 3188 | { |
f6b55e0c | 3189 | /* |
3190 | * Terminate the current string and skip trailing whitespace... | |
3191 | */ | |
3192 | ||
a74b005d | 3193 | *commptr++ = '\0'; |
3194 | ||
3195 | while (*commptr == ' ') | |
3196 | commptr ++; | |
3197 | ||
f6b55e0c | 3198 | /* |
3199 | * If we don't have a blank string, save it as another argument... | |
3200 | */ | |
3201 | ||
3202 | if (*commptr) | |
a74b005d | 3203 | { |
3204 | argv[argc] = commptr; | |
3205 | argc ++; | |
3206 | } | |
f6b55e0c | 3207 | else |
3208 | break; | |
da0bffec | 3209 | |
3210 | /* | |
f6b55e0c | 3211 | * If we see an "=" in the remaining string, make a copy of it since |
3212 | * it will be query data... | |
da0bffec | 3213 | */ |
3214 | ||
3215 | if (argc == 2 && strchr(commptr, '=') && con->operation == HTTP_GET) | |
589eb420 | 3216 | cupsdSetStringf(&query_string, "QUERY_STRING=%s", commptr); |
f6b55e0c | 3217 | |
3218 | /* | |
3219 | * Don't skip the first non-blank character... | |
3220 | */ | |
da0bffec | 3221 | |
a74b005d | 3222 | commptr --; |
3223 | } | |
da275f55 | 3224 | else if (*commptr == '%' && isxdigit(commptr[1] & 255) && |
3225 | isxdigit(commptr[2] & 255)) | |
a74b005d | 3226 | { |
f6b55e0c | 3227 | /* |
3228 | * Convert the %xx notation to the individual character. | |
3229 | */ | |
3230 | ||
a74b005d | 3231 | if (commptr[1] >= '0' && commptr[1] <= '9') |
3232 | *commptr = (commptr[1] - '0') << 4; | |
3233 | else | |
3234 | *commptr = (tolower(commptr[1]) - 'a' + 10) << 4; | |
3235 | ||
3236 | if (commptr[2] >= '0' && commptr[2] <= '9') | |
3237 | *commptr |= commptr[2] - '0'; | |
3238 | else | |
3239 | *commptr |= tolower(commptr[2]) - 'a' + 10; | |
3240 | ||
2625096f | 3241 | _cups_strcpy(commptr + 1, commptr + 3); |
f6b55e0c | 3242 | |
3243 | /* | |
3244 | * Check for a %00 and break if that is the case... | |
3245 | */ | |
3246 | ||
3247 | if (!*commptr) | |
3248 | break; | |
a74b005d | 3249 | } |
f6b55e0c | 3250 | } |
a74b005d | 3251 | |
3252 | argv[argc] = NULL; | |
3253 | ||
bfa1abf0 | 3254 | if (argv[0][0] == '\0') |
3255 | argv[0] = strrchr(command, '/') + 1; | |
b14d90ba | 3256 | |
3257 | /* | |
3258 | * Setup the environment variables as needed... | |
3259 | */ | |
3260 | ||
08c0ff15 | 3261 | if (con->language) |
f3e786fc | 3262 | snprintf(lang, sizeof(lang), "LANG=%s.UTF-8", con->language->language); |
08c0ff15 | 3263 | else |
3264 | strcpy(lang, "LANG=C"); | |
b2e10895 | 3265 | |
2ae14c39 | 3266 | strcpy(remote_addr, "REMOTE_ADDR="); |
3267 | httpAddrString(&(con->http.hostaddr), remote_addr + 12, | |
3268 | sizeof(remote_addr) - 12); | |
291355eb | 3269 | |
3270 | snprintf(remote_host, sizeof(remote_host), "REMOTE_HOST=%s", | |
3271 | con->http.hostname); | |
3272 | ||
3273 | snprintf(script_name, sizeof(script_name), "SCRIPT_NAME=%s", con->uri); | |
3274 | if ((uriptr = strchr(script_name, '?')) != NULL) | |
3275 | *uriptr = '\0'; | |
3276 | ||
3277 | sprintf(server_port, "SERVER_PORT=%d", con->serverport); | |
3278 | ||
3279 | snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s", | |
3280 | con->servername); | |
3281 | ||
3282 | envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0]))); | |
3283 | ||
3284 | envp[envc ++] = lang; | |
2ae14c39 | 3285 | envp[envc ++] = "REDIRECT_STATUS=1"; |
2ae14c39 | 3286 | envp[envc ++] = server_name; |
3287 | envp[envc ++] = server_port; | |
3288 | envp[envc ++] = remote_addr; | |
3289 | envp[envc ++] = remote_host; | |
291355eb | 3290 | envp[envc ++] = script_name; |
dd63ebe2 | 3291 | |
291355eb | 3292 | if (con->username[0]) |
2ae14c39 | 3293 | { |
291355eb | 3294 | snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username); |
dd63ebe2 | 3295 | |
291355eb | 3296 | envp[envc ++] = remote_user; |
2ae14c39 | 3297 | } |
7f0679f5 | 3298 | |
291355eb | 3299 | if (con->http.version == HTTP_1_1) |
3300 | envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1"; | |
3301 | else if (con->http.version == HTTP_1_0) | |
3302 | envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0"; | |
3303 | else | |
3304 | envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9"; | |
2ae14c39 | 3305 | |
3306 | if (con->http.cookie) | |
3307 | { | |
3308 | snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s", | |
3309 | con->http.cookie); | |
3310 | envp[envc ++] = http_cookie; | |
3311 | } | |
3312 | ||
3313 | if (con->http.fields[HTTP_FIELD_USER_AGENT][0]) | |
3314 | { | |
3315 | snprintf(http_user_agent, sizeof(http_user_agent), "HTTP_USER_AGENT=%s", | |
3316 | con->http.fields[HTTP_FIELD_USER_AGENT]); | |
3317 | envp[envc ++] = http_user_agent; | |
3318 | } | |
3319 | ||
b14d90ba | 3320 | if (con->operation == HTTP_GET) |
3321 | { | |
43ed090d | 3322 | for (i = 0; i < argc; i ++) |
f3e786fc | 3323 | cupsdLogMessage(CUPSD_LOG_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]); |
291355eb | 3324 | |
df3f73ff | 3325 | envp[envc ++] = "REQUEST_METHOD=GET"; |
f63a2256 | 3326 | |
f6b55e0c | 3327 | if (query_string) |
f63a2256 | 3328 | { |
3329 | /* | |
3330 | * Add GET form variables after ?... | |
3331 | */ | |
3332 | ||
df3f73ff | 3333 | envp[envc ++] = query_string; |
f63a2256 | 3334 | } |
b14d90ba | 3335 | } |
3336 | else | |
3337 | { | |
1479646d | 3338 | sprintf(content_length, "CONTENT_LENGTH=" CUPS_LLFMT, con->bytes); |
04de52f8 | 3339 | snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s", |
970017a4 | 3340 | con->http.fields[HTTP_FIELD_CONTENT_TYPE]); |
b14d90ba | 3341 | |
df3f73ff | 3342 | envp[envc ++] = "REQUEST_METHOD=POST"; |
3343 | envp[envc ++] = content_length; | |
3344 | envp[envc ++] = content_type; | |
b14d90ba | 3345 | } |
3346 | ||
30d60919 | 3347 | /* |
3348 | * Tell the CGI if we are using encryption... | |
3349 | */ | |
3350 | ||
b92c5a4f | 3351 | if (con->http.encryption == HTTP_ENCRYPT_ALWAYS) |
30d60919 | 3352 | envp[envc ++] = "HTTPS=ON"; |
41d6188e | 3353 | |
41d6188e | 3354 | /* |
3355 | * Terminate the environment array... | |
3356 | */ | |
30d60919 | 3357 | |
df3f73ff | 3358 | envp[envc] = NULL; |
3359 | ||
f3e786fc | 3360 | if (LogLevel == CUPSD_LOG_DEBUG2) |
2ae14c39 | 3361 | { |
3362 | for (i = 0; i < argc; i ++) | |
f3e786fc | 3363 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
3364 | "pipe_command: argv[%d] = \"%s\"", i, argv[i]); | |
2ae14c39 | 3365 | for (i = 0; i < envc; i ++) |
f3e786fc | 3366 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
3367 | "pipe_command: envp[%d] = \"%s\"", i, envp[i]); | |
2ae14c39 | 3368 | } |
3369 | ||
a74b005d | 3370 | /* |
3371 | * Create a pipe for the output... | |
3372 | */ | |
3373 | ||
8650fcf2 | 3374 | if (cupsdOpenPipe(fds)) |
1d6c68fb | 3375 | { |
589eb420 | 3376 | cupsdClearString(&query_string); |
f6b55e0c | 3377 | |
f3e786fc | 3378 | cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create pipes for CGI %s - %s", |
3379 | argv[0], strerror(errno)); | |
a74b005d | 3380 | return (0); |
1d6c68fb | 3381 | } |
a74b005d | 3382 | |
a665f6fc | 3383 | /* |
3384 | * Then execute the command... | |
3385 | */ | |
3386 | ||
3387 | if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1], | |
b38d93df | 3388 | -1, root, &pid) < 0) |
a665f6fc | 3389 | { |
3390 | /* | |
3391 | * Error - can't fork! | |
3392 | */ | |
3393 | ||
f3e786fc | 3394 | cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork for CGI %s - %s", argv[0], |
3395 | strerror(errno)); | |
a665f6fc | 3396 | |
3397 | cupsdClosePipe(fds); | |
3398 | pid = 0; | |
3399 | } | |
3400 | else | |
3401 | { | |
3402 | /* | |
3403 | * Fork successful - return the PID... | |
3404 | */ | |
3405 | ||
d9acc9ab | 3406 | if (con->username[0]) |
589eb420 | 3407 | cupsdAddCert(pid, con->username); |
a665f6fc | 3408 | |
f3e786fc | 3409 | cupsdLogMessage(CUPSD_LOG_DEBUG, "CGI %s started - PID = %d", command, pid); |
a665f6fc | 3410 | |
3411 | *outfile = fds[0]; | |
3412 | close(fds[1]); | |
3413 | } | |
4d520ad4 | 3414 | |
589eb420 | 3415 | cupsdClearString(&query_string); |
f6b55e0c | 3416 | |
5c8eff27 | 3417 | return (pid); |
a74b005d | 3418 | } |
3419 | ||
3420 | ||
dcfcaeac | 3421 | #if defined(HAVE_CDSASSL) |
3422 | /* | |
3423 | * 'CDSAReadFunc()' - Read function for CDSA decryption code. | |
3424 | */ | |
3425 | ||
f3e786fc | 3426 | static OSStatus /* O - -1 on error, 0 on success */ |
3427 | CDSAReadFunc( | |
3428 | SSLConnectionRef connection, /* I - SSL/TLS connection */ | |
3429 | void *data, /* I - Data buffer */ | |
3430 | size_t *dataLength) /* IO - Number of bytes */ | |
dcfcaeac | 3431 | { |
f3e786fc | 3432 | ssize_t bytes; /* Number of bytes read */ |
dcfcaeac | 3433 | |
3434 | ||
3435 | bytes = recv((int)connection, data, *dataLength, 0); | |
3436 | if (bytes >= 0) | |
3437 | { | |
3438 | *dataLength = bytes; | |
3439 | return (0); | |
3440 | } | |
3441 | else | |
3442 | return (-1); | |
3443 | } | |
3444 | ||
3445 | ||
3446 | /* | |
3447 | * 'CDSAWriteFunc()' - Write function for CDSA encryption code. | |
3448 | */ | |
3449 | ||
f3e786fc | 3450 | static OSStatus /* O - -1 on error, 0 on success */ |
3451 | CDSAWriteFunc( | |
3452 | SSLConnectionRef connection, /* I - SSL/TLS connection */ | |
3453 | const void *data, /* I - Data buffer */ | |
3454 | size_t *dataLength) /* IO - Number of bytes */ | |
dcfcaeac | 3455 | { |
3456 | ssize_t bytes; | |
3457 | ||
3458 | ||
3459 | bytes = write((int)connection, data, *dataLength); | |
3460 | if (bytes >= 0) | |
3461 | { | |
3462 | *dataLength = bytes; | |
3463 | return (0); | |
3464 | } | |
3465 | else | |
3466 | return (-1); | |
3467 | } | |
3468 | #endif /* HAVE_CDSASSL */ | |
3469 | ||
3470 | ||
93894a43 | 3471 | /* |
b2e10895 | 3472 | * End of "$Id$". |
a74b005d | 3473 | */ |