Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
diff src/gt_http_server.c @ 0:d39e1d0d75b6
initial add
author | paulo@hit-nxdomain.opendns.com |
---|---|
date | Sat, 20 Feb 2010 21:18:28 -0800 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/gt_http_server.c Sat Feb 20 21:18:28 2010 -0800 1.3 @@ -0,0 +1,946 @@ 1.4 +/* 1.5 + * $Id: gt_http_server.c,v 1.73 2005/01/04 15:03:40 mkern Exp $ 1.6 + * 1.7 + * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net) 1.8 + * 1.9 + * This program is free software; you can redistribute it and/or modify it 1.10 + * under the terms of the GNU General Public License as published by the 1.11 + * Free Software Foundation; either version 2, or (at your option) any 1.12 + * later version. 1.13 + * 1.14 + * This program is distributed in the hope that it will be useful, but 1.15 + * WITHOUT ANY WARRANTY; without even the implied warranty of 1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1.17 + * General Public License for more details. 1.18 + */ 1.19 + 1.20 +#include "gt_gnutella.h" 1.21 + 1.22 +#include "gt_xfer_obj.h" 1.23 +#include "gt_xfer.h" 1.24 + 1.25 +#include "gt_http_server.h" 1.26 +#include "gt_http_client.h" 1.27 + 1.28 +#include "gt_accept.h" 1.29 +#include "gt_ban.h" 1.30 +#include "gt_version.h" 1.31 + 1.32 +/*****************************************************************************/ 1.33 + 1.34 +/* convenient shorthand */ 1.35 +#define CONTENT_URN_FIELD "X-Gnutella-Content-URN" 1.36 + 1.37 +#define INCOMING_TIMEOUT (1 * MINUTES) 1.38 + 1.39 +/*****************************************************************************/ 1.40 + 1.41 +struct http_incoming 1.42 +{ 1.43 + TCPC *c; 1.44 + timer_id timer; 1.45 +}; 1.46 + 1.47 +/*****************************************************************************/ 1.48 + 1.49 +static void server_handle_get (GtTransfer *xfer); 1.50 +static void get_client_request (int fd, input_id id, 1.51 + struct http_incoming *http); 1.52 +static void send_http_response (int fd, input_id id, GtTransfer *xfer); 1.53 + 1.54 +/*****************************************************************************/ 1.55 +/* SERVER HELPERS */ 1.56 + 1.57 +static char *lookup_http_code (int code, char **desc) 1.58 +{ 1.59 + char *err; 1.60 + char *txt; 1.61 + 1.62 + switch (code) 1.63 + { 1.64 + case 200: err = "OK"; 1.65 + txt = "Success"; 1.66 + break; 1.67 + case 206: err = "Partial Content"; 1.68 + txt = "Resume successful"; 1.69 + break; 1.70 + case 403: err = "Forbidden"; 1.71 + txt = "You do not have access to this file"; 1.72 + break; 1.73 + case 404: err = "Not Found"; 1.74 + txt = "File is not available"; 1.75 + break; 1.76 + case 500: err = "Internal Server Error"; 1.77 + txt = "Stale file entry, retry later"; 1.78 + break; 1.79 + case 501: err = "Not Implemented"; 1.80 + txt = "???"; 1.81 + break; 1.82 + case 503: err = "Service Unavailable"; 1.83 + txt = "Upload queue is currently full, please try again later"; 1.84 + break; 1.85 + default: err = NULL; 1.86 + txt = NULL; 1.87 + break; 1.88 + } 1.89 + 1.90 + if (desc) 1.91 + *desc = txt; 1.92 + 1.93 + return err; 1.94 +} 1.95 + 1.96 +static String *alloc_header (int code) 1.97 +{ 1.98 + char *code_text; 1.99 + String *s; 1.100 + 1.101 + /* so that we can communicate both the numerical code and the human 1.102 + * readable string */ 1.103 + if (!(code_text = lookup_http_code (code, NULL))) 1.104 + return FALSE; 1.105 + 1.106 + if (!(s = string_new (NULL, 0, 0, TRUE))) 1.107 + return FALSE; 1.108 + 1.109 + string_appendf (s, "HTTP/1.1 %i %s\r\n", code, code_text); 1.110 + 1.111 + return s; 1.112 +} 1.113 + 1.114 +static void construct_header_va (String *s, int code, va_list args) 1.115 +{ 1.116 + char *key; 1.117 + char *value; 1.118 + 1.119 + /* Add "Server: " header */ 1.120 + string_appendf (s, "Server: %s\r\n", gt_version ()); 1.121 + 1.122 + for (;;) 1.123 + { 1.124 + if (!(key = va_arg (args, char *))) 1.125 + break; 1.126 + 1.127 + if (!(value = va_arg (args, char *))) 1.128 + continue; 1.129 + 1.130 + string_appendf (s, "%s: %s\r\n", key, value); 1.131 + } 1.132 + 1.133 + /* append final message terminator */ 1.134 + string_append (s, "\r\n"); 1.135 +} 1.136 + 1.137 +static String *construct_header (int code, ...) 1.138 +{ 1.139 + String *s; 1.140 + va_list args; 1.141 + 1.142 + if (!(s = alloc_header (code))) 1.143 + return NULL; 1.144 + 1.145 + va_start (args, code); 1.146 + construct_header_va (s, code, args); 1.147 + va_end (args); 1.148 + 1.149 + return s; 1.150 +} 1.151 + 1.152 +/* 1.153 + * Construct and send a server reply. 1.154 + */ 1.155 +static BOOL gt_http_server_send (TCPC *c, int code, ...) 1.156 +{ 1.157 + String *s; 1.158 + int ret; 1.159 + size_t len; 1.160 + va_list args; 1.161 + 1.162 + if (!(s = alloc_header (code))) 1.163 + return FALSE; 1.164 + 1.165 + va_start (args, code); 1.166 + 1.167 + construct_header_va (s, code, args); 1.168 + 1.169 + va_end (args); 1.170 + 1.171 + if (HTTP_DEBUG) 1.172 + GT->DBGSOCK (GT, c, "sending reply to client =\n%s", s->str); 1.173 + 1.174 + len = s->len; 1.175 + ret = tcp_send (c, s->str, s->len); 1.176 + 1.177 + string_free (s); 1.178 + 1.179 + return (ret == len); 1.180 +} 1.181 + 1.182 +static char *get_error_page (GtTransfer *xfer, int code) 1.183 +{ 1.184 + char *page; 1.185 + char *err; 1.186 + char *errtxt = NULL; 1.187 + 1.188 + if (!(err = lookup_http_code (code, &errtxt))) 1.189 + return 0; 1.190 + 1.191 + page = stringf ("<h1>%i %s</h1><br>%s.", code, err, errtxt); 1.192 + 1.193 + return page; 1.194 +} 1.195 + 1.196 +static BOOL supports_queue (GtTransfer *xfer) 1.197 +{ 1.198 + char *features; 1.199 + 1.200 + if (dataset_lookupstr (xfer->header, "x-queue")) 1.201 + return TRUE; 1.202 + 1.203 + if ((features = dataset_lookupstr (xfer->header, "x-features"))) 1.204 + { 1.205 + /* XXX: case-sensitive */ 1.206 + if (strstr (features, "queue")) 1.207 + return TRUE; 1.208 + } 1.209 + 1.210 + return FALSE; 1.211 +} 1.212 + 1.213 +static char *get_queue_line (GtTransfer *xfer) 1.214 +{ 1.215 + String *s; 1.216 + 1.217 + /* do nothing if not queued */ 1.218 + if (xfer->queue_pos == 0) 1.219 + return NULL; 1.220 + 1.221 + if (!(s = string_new (NULL, 0, 0, TRUE))) 1.222 + return NULL; 1.223 + 1.224 + string_appendf (s, "position=%d,length=%d,pollMin=%d,pollMax=%d", 1.225 + xfer->queue_pos, xfer->queue_ttl, 45, 120); 1.226 + 1.227 + return string_free_keep (s); 1.228 +} 1.229 + 1.230 +static String *get_error_header (GtTransfer *xfer, int code, 1.231 + const char *error_page) 1.232 +{ 1.233 + size_t len; 1.234 + char content_len[256]; 1.235 + char *queue_line = NULL; 1.236 + char *content_type = "text/html"; 1.237 + String *s; 1.238 + 1.239 + len = strlen (error_page); 1.240 + snprintf (content_len, sizeof (content_len), "%u", len); 1.241 + 1.242 + if (code == 503 && supports_queue (xfer)) 1.243 + queue_line = get_queue_line (xfer); 1.244 + 1.245 + /* don't send a content-type header if there is no entity body */ 1.246 + if (len == 0) 1.247 + content_type = NULL; 1.248 + 1.249 + s = construct_header (code, 1.250 + "Content-Type", content_type, 1.251 + "Content-Length", content_len, 1.252 + CONTENT_URN_FIELD, xfer->content_urns, 1.253 + "X-Queue", queue_line, 1.254 + NULL); 1.255 + 1.256 + free (queue_line); 1.257 + return s; 1.258 +} 1.259 + 1.260 +static void send_error_reply (int fd, input_id id, GtTransfer *xfer) 1.261 +{ 1.262 + String *s; 1.263 + const char *error_page; 1.264 + int ret; 1.265 + TCPC *c; 1.266 + 1.267 + c = gt_transfer_get_tcpc (xfer); 1.268 + 1.269 + if (!(error_page = get_error_page (xfer, xfer->code))) 1.270 + { 1.271 + gt_transfer_close (xfer, TRUE); 1.272 + return; 1.273 + } 1.274 + 1.275 + /* 1.276 + * If the remote end supports queueing or supplied 1.277 + * "X-Gnutella-Content-URN",, it's a Gnutella client and we don't want to 1.278 + * keep sending the error page, because over many requests from this 1.279 + * client the bandwidth could add up. 1.280 + */ 1.281 + if (supports_queue (xfer) || 1.282 + dataset_lookupstr (xfer->header, "x-gnutella-content-urn")) 1.283 + { 1.284 + error_page = ""; /* empty */ 1.285 + } 1.286 + 1.287 + if (!(s = get_error_header (xfer, xfer->code, error_page))) 1.288 + { 1.289 + gt_transfer_close (xfer, TRUE); 1.290 + return; 1.291 + } 1.292 + 1.293 + string_append (s, error_page); 1.294 + 1.295 + if (HTTP_DEBUG) 1.296 + GT->DBGSOCK (GT, c, "sending reply to client =\n%s", s->str); 1.297 + 1.298 + /* send the whole page at once */ 1.299 + ret = tcp_send (c, s->str, s->len); 1.300 + 1.301 + /* if the whole thing was sent keep the connection open */ 1.302 + if (ret == s->len) 1.303 + { 1.304 + xfer->transmitted_hdrs = TRUE; 1.305 + xfer->remaining_len = 0; 1.306 + } 1.307 + 1.308 + string_free (s); 1.309 + 1.310 + gt_transfer_close (xfer, FALSE); 1.311 +} 1.312 + 1.313 +static void gt_http_server_send_error_and_close (GtTransfer *xfer, int code) 1.314 +{ 1.315 + TCPC *c; 1.316 + 1.317 + c = gt_transfer_get_tcpc (xfer); 1.318 + 1.319 + xfer->code = code; 1.320 + 1.321 + input_remove_all (c->fd); 1.322 + input_add (c->fd, xfer, INPUT_WRITE, 1.323 + (InputCallback)send_error_reply, TIMEOUT_DEF); 1.324 +} 1.325 + 1.326 +/*****************************************************************************/ 1.327 + 1.328 +/* parse the Range: bytes=0-10000 format */ 1.329 +static void parse_client_request_range (Dataset *dataset, 1.330 + off_t *r_start, off_t *r_stop) 1.331 +{ 1.332 + char *range; 1.333 + off_t start; 1.334 + off_t stop; 1.335 + 1.336 + if (!r_start && !r_stop) 1.337 + return; 1.338 + 1.339 + if (r_start) 1.340 + *r_start = 0; 1.341 + if (r_stop) 1.342 + *r_stop = 0; 1.343 + 1.344 + /* leave stop as 0 if we can't figure anything out yet. This is expected 1.345 + * to be handled separately by GET and PUSH */ 1.346 + if (!(range = dataset_lookupstr (dataset, "range"))) 1.347 + return; 1.348 + 1.349 + /* WARNING: this butchers the data in the dataset! */ 1.350 + string_sep (&range, "bytes"); 1.351 + string_sep_set (&range, " ="); 1.352 + 1.353 + if (!range) 1.354 + { 1.355 + if (HTTP_DEBUG) 1.356 + GT->DBGFN (GT, "error parsing Range: header"); 1.357 + 1.358 + return; 1.359 + } 1.360 + 1.361 + start = (off_t) ATOI (string_sep (&range, "-")); 1.362 + stop = (off_t) ATOI (string_sep (&range, " ")); 1.363 + 1.364 + /* 1.365 + * The end of the range is optional (e.g. "Range: 0-"), and in that case 1.366 + * stop == 0. In the case of a single byte file, stop == 1. 1.367 + * 1.368 + * TODO: this is broken for one-byte requests at the start of the file. 1.369 + */ 1.370 + if (stop > 0) 1.371 + stop = stop + 1; 1.372 + 1.373 + if (r_start) 1.374 + *r_start = start; 1.375 + if (r_stop) 1.376 + *r_stop = stop; 1.377 +} 1.378 + 1.379 +/* 1.380 + * Parse requests of the forms: 1.381 + * 1.382 + * /get/47/File maybe with spaces HTTP 1.383 + * /get/47/files_%20_url_encoded HTTP/1.1 1.384 + * /uri-res/N2R?urn:sha1:ABCD HTTP/1.0 1.385 + * 1.386 + * and combinations thereof. The "HTTP" trailer is mandatory. 1.387 + */ 1.388 +static void get_request_and_version (char *data, char **request, 1.389 + char **version) 1.390 +{ 1.391 + size_t len; 1.392 + char *next; 1.393 + char *dup; 1.394 + char *http_version = NULL; 1.395 + 1.396 + *request = NULL; 1.397 + *version = NULL; 1.398 + 1.399 + /* trim whitespace inbetween command and request */ 1.400 + string_trim (data); 1.401 + 1.402 + if (!(dup = STRDUP (data))) 1.403 + return; 1.404 + 1.405 + string_upper (dup); 1.406 + 1.407 + next = dup; 1.408 + 1.409 + /* find the last instance of "HTTP" in the string */ 1.410 + while ((next = strstr (next, "HTTP"))) 1.411 + { 1.412 + http_version = next; 1.413 + next += sizeof ("HTTP") - 1; 1.414 + } 1.415 + 1.416 + /* the rest of the string must be the request */ 1.417 + if (http_version != NULL && http_version != dup) 1.418 + { 1.419 + len = http_version - dup; 1.420 + data[len - 1] = 0; 1.421 + 1.422 + *request = data; 1.423 + *version = data + len; 1.424 + } 1.425 + 1.426 + free (dup); 1.427 +} 1.428 + 1.429 +/* 1.430 + * Break down the clients HTTP request 1.431 + */ 1.432 +static int parse_client_request (Dataset **r_dataset, char **r_command, 1.433 + char **r_request, char **r_version, 1.434 + off_t *r_start, off_t *r_stop, char *hdr) 1.435 +{ 1.436 + Dataset *dataset = NULL; 1.437 + char *command; /* GET */ 1.438 + char *request; /* /file.tar.gz */ 1.439 + char *version; /* HTTP/1.1 */ 1.440 + char *req_line; 1.441 + 1.442 + if (!hdr) 1.443 + return FALSE; 1.444 + 1.445 + /* 1.446 + * Get the first line of the request 1.447 + */ 1.448 + req_line = string_sep_set (&hdr, "\r\n"); 1.449 + 1.450 + /* get the command (GET, HEAD, etc.) */ 1.451 + command = string_sep (&req_line, " "); 1.452 + 1.453 + /* 1.454 + * Handle non-url-encoded requests as well as encoded 1.455 + * ones and get the request and version from this HTTP line. 1.456 + */ 1.457 + get_request_and_version (req_line, &request, &version); 1.458 + 1.459 + if (HTTP_DEBUG) 1.460 + { 1.461 + GT->DBGFN (GT, "command=%s version=%s request=%s", 1.462 + command, version, request); 1.463 + } 1.464 + 1.465 + if (!request || string_isempty (request)) 1.466 + return FALSE; 1.467 + 1.468 + if (r_command) 1.469 + *r_command = command; 1.470 + if (r_request) 1.471 + *r_request = request; 1.472 + if (r_version) 1.473 + *r_version = version; 1.474 + 1.475 + gt_http_header_parse (hdr, &dataset); 1.476 + 1.477 + if (r_dataset) 1.478 + *r_dataset = dataset; 1.479 + 1.480 + /* handle Range: header */ 1.481 + parse_client_request_range (dataset, r_start, r_stop); 1.482 + 1.483 + if (r_start && r_stop) 1.484 + { 1.485 + if (HTTP_DEBUG) 1.486 + GT->dbg (GT, "range: [%i, %i)", *r_start, *r_stop); 1.487 + } 1.488 + 1.489 + return TRUE; 1.490 +} 1.491 + 1.492 +/*****************************************************************************/ 1.493 + 1.494 +/* 1.495 + * Send the request reply back to the client 1.496 + * 1.497 + * NOTE: 1.498 + * This is used by both GET / and PUSH / 1.499 + */ 1.500 +static void reply_to_client_request (GtTransfer *xfer) 1.501 +{ 1.502 + TCPC *c; 1.503 + Chunk *chunk; 1.504 + off_t entity_size; 1.505 + char range[128]; 1.506 + char length[32]; 1.507 + BOOL ret; 1.508 + 1.509 + if (!xfer) 1.510 + return; 1.511 + 1.512 + c = gt_transfer_get_tcpc (xfer); 1.513 + chunk = gt_transfer_get_chunk (xfer); 1.514 + 1.515 + /* 1.516 + * Determine the "total" entity body that we have locally, not necessarily 1.517 + * the data that we are uploading. HTTP demands this, but OpenFT really 1.518 + * doesn't give a shit. 1.519 + * 1.520 + * NOTE: 1.521 + * This only works to standard when operating on a GET / request, PUSH's 1.522 + * merely use the range! 1.523 + */ 1.524 + if (xfer->open_path_size) 1.525 + entity_size = xfer->open_path_size; 1.526 + else 1.527 + entity_size = xfer->stop - xfer->start; 1.528 + 1.529 + /* NOTE: we are "working" around the fact that HTTP's Content-Range 1.530 + * reply is inclusive for the last byte, whereas giFT's is not. */ 1.531 + snprintf (range, sizeof (range) - 1, "bytes %i-%i/%i", 1.532 + (int) xfer->start, (int) (xfer->stop - 1), (int) entity_size); 1.533 + 1.534 + snprintf (length, sizeof (length) - 1, "%i", 1.535 + (int) (xfer->stop - xfer->start)); 1.536 + 1.537 + ret = gt_http_server_send (c, xfer->code, 1.538 + "Content-Range", range, 1.539 + "Content-Length", length, 1.540 + "Content-Type", xfer->content_type, 1.541 + CONTENT_URN_FIELD, xfer->content_urns, 1.542 + NULL); 1.543 + 1.544 + /* if we transmitted all headers successfully, set transmitted_hdrs 1.545 + * to keep the connection alive, possibly */ 1.546 + if (ret) 1.547 + xfer->transmitted_hdrs = TRUE; 1.548 +} 1.549 + 1.550 +/*****************************************************************************/ 1.551 + 1.552 +static void http_incoming_free (struct http_incoming *incoming) 1.553 +{ 1.554 + timer_remove (incoming->timer); 1.555 + free (incoming); 1.556 +} 1.557 + 1.558 +static void http_incoming_close (struct http_incoming *incoming) 1.559 +{ 1.560 + gt_http_connection_close (GT_TRANSFER_UPLOAD, incoming->c, TRUE); 1.561 + http_incoming_free (incoming); 1.562 +} 1.563 + 1.564 +static BOOL http_incoming_timeout (struct http_incoming *incoming) 1.565 +{ 1.566 + http_incoming_close (incoming); 1.567 + return FALSE; 1.568 +} 1.569 + 1.570 +static struct http_incoming *http_incoming_alloc (TCPC *c) 1.571 +{ 1.572 + struct http_incoming *incoming; 1.573 + 1.574 + incoming = malloc (sizeof (struct http_incoming)); 1.575 + if (!incoming) 1.576 + return NULL; 1.577 + 1.578 + incoming->c = c; 1.579 + incoming->timer = timer_add (INCOMING_TIMEOUT, 1.580 + (TimerCallback)http_incoming_timeout, 1.581 + incoming); 1.582 + 1.583 + return incoming; 1.584 +} 1.585 + 1.586 +void gt_http_server_dispatch (int fd, input_id id, TCPC *c) 1.587 +{ 1.588 + struct http_incoming *incoming; 1.589 + 1.590 + if (net_sock_error (c->fd)) 1.591 + { 1.592 + gt_http_connection_close (GT_TRANSFER_UPLOAD, c, TRUE); 1.593 + return; 1.594 + } 1.595 + 1.596 + if (!(incoming = http_incoming_alloc (c))) 1.597 + { 1.598 + gt_http_connection_close (GT_TRANSFER_UPLOAD, c, TRUE); 1.599 + return; 1.600 + } 1.601 + 1.602 + /* keep track of this incoming connection */ 1.603 + /* gt_http_connection_insert (GT_TRANSFER_UPLOAD, c); */ 1.604 + 1.605 + input_remove (id); 1.606 + input_add (c->fd, incoming, INPUT_READ, 1.607 + (InputCallback)get_client_request, 0); 1.608 +} 1.609 + 1.610 +/* 1.611 + * Handle the client's GET commands. 1.612 + */ 1.613 +static void get_client_request (int fd, input_id id, struct http_incoming *http) 1.614 +{ 1.615 + GtTransfer *xfer; 1.616 + TCPC *c; 1.617 + Dataset *dataset = NULL; 1.618 + char *command = NULL; 1.619 + char *request = NULL; 1.620 + char *version = NULL; 1.621 + off_t start = 0; 1.622 + off_t stop = 0; 1.623 + FDBuf *buf; 1.624 + unsigned char *data; 1.625 + size_t data_len = 0; 1.626 + int n; 1.627 + 1.628 + c = http->c; 1.629 + buf = tcp_readbuf (c); 1.630 + 1.631 + if ((n = fdbuf_delim (buf, "\n")) < 0) 1.632 + { 1.633 + http_incoming_close (http); 1.634 + return; 1.635 + } 1.636 + 1.637 + if (gt_fdbuf_full (buf)) 1.638 + { 1.639 + http_incoming_close (http); 1.640 + return; 1.641 + } 1.642 + 1.643 + if (n > 0) 1.644 + return; 1.645 + 1.646 + data = fdbuf_data (buf, &data_len); 1.647 + 1.648 + if (!gt_http_header_terminated (data, data_len)) 1.649 + return; 1.650 + 1.651 + fdbuf_release (buf); 1.652 + 1.653 + if (HTTP_DEBUG) 1.654 + GT->DBGSOCK (GT, c, "client request:\n%s", data); 1.655 + 1.656 + /* parse the client's request and determine how we should proceed */ 1.657 + if (!parse_client_request (&dataset, &command, &request, &version, 1.658 + &start, &stop, data)) 1.659 + { 1.660 + GT->DBGSOCK (GT, c, "invalid http header"); 1.661 + http_incoming_close (http); 1.662 + return; 1.663 + } 1.664 + 1.665 + /* discard incoming connection timeout maintainance structure */ 1.666 + http_incoming_free (http); 1.667 + 1.668 + /* 1.669 + * We have enough information now to actually allocate the transfer 1.670 + * structure and pass it along to all logic that follows this 1.671 + * 1.672 + * NOTE: 1.673 + * Each individual handler can determine when it wants to let giFT 1.674 + * in on this 1.675 + */ 1.676 + xfer = gt_transfer_new (GT_TRANSFER_UPLOAD, NULL, 1.677 + net_peer (c->fd), 0, start, stop); 1.678 + 1.679 + /* connect the connection and the xfer in unholy matrimony */ 1.680 + gt_transfer_set_tcpc (xfer, c); 1.681 + 1.682 + /* assign all our own memory */ 1.683 + xfer->command = STRDUP (command); 1.684 + xfer->header = dataset; 1.685 + xfer->version = STRDUP (version); 1.686 + 1.687 + if (!gt_transfer_set_request (xfer, request)) 1.688 + { 1.689 + if (HTTP_DEBUG) 1.690 + GT->DBGSOCK (GT, c, "invalid request \"s\"", request); 1.691 + 1.692 + gt_transfer_close (xfer, TRUE); 1.693 + return; 1.694 + } 1.695 + 1.696 + /* no need for this function again */ 1.697 + input_remove (id); 1.698 + 1.699 + /* figure out how to handle this request */ 1.700 + if (!strcasecmp (xfer->command, "GET") || 1.701 + !strcasecmp (xfer->command, "HEAD")) 1.702 + { 1.703 + server_handle_get (xfer); 1.704 + return; 1.705 + } 1.706 + 1.707 + gt_http_server_send_error_and_close (xfer, 501); 1.708 +} 1.709 + 1.710 +/*****************************************************************************/ 1.711 + 1.712 +static Transfer *start_upload (GtTransfer *xfer, Chunk **chunk) 1.713 +{ 1.714 + Transfer *transfer; 1.715 + char *user; 1.716 + 1.717 + user = net_ip_str (xfer->ip); 1.718 + 1.719 + transfer = GT->upload_start (GT, chunk, user, xfer->share_authd, 1.720 + xfer->start, xfer->stop); 1.721 + 1.722 + assert (transfer != NULL); 1.723 + 1.724 + return transfer; 1.725 +} 1.726 + 1.727 +/* setup the structure for uploading. this will be called from within 1.728 + * client space for PUSH requests as well */ 1.729 +int gt_server_setup_upload (GtTransfer *xfer) 1.730 +{ 1.731 + Transfer *transfer; /* giFT structure */ 1.732 + Chunk *chunk; 1.733 + TCPC *c; 1.734 + 1.735 + if (!xfer) 1.736 + return FALSE; 1.737 + 1.738 + c = gt_transfer_get_tcpc (xfer); 1.739 + assert (xfer->chunk == NULL); 1.740 + 1.741 + /* 1.742 + * Ban the host if they don't have access -- this gives no information 1.743 + * about whether we have the file or not, and i think this is in violation 1.744 + * of the HTTP spec (supposed to return "404 not found" before 403, but 1.745 + * i'm not sure. 1.746 + */ 1.747 + if (gt_ban_ipv4_is_banned (c->host)) 1.748 + { 1.749 + xfer->code = 403; 1.750 + return FALSE; 1.751 + } 1.752 + 1.753 + /* open the file that was requested before we go bringing giFT into 1.754 + * this */ 1.755 + if (!(xfer->f = gt_transfer_open_request (xfer, &xfer->code))) 1.756 + return FALSE; 1.757 + 1.758 + /* assign stop a value before we proceed */ 1.759 + if (xfer->stop == 0) 1.760 + { 1.761 + struct stat st; 1.762 + 1.763 + if (!file_stat (xfer->open_path, &st) || st.st_size == 0) 1.764 + { 1.765 + /* stupid bastards have a 0 length file */ 1.766 + GT->DBGSOCK (GT, c, "cannot satisfy %s: invalid share", 1.767 + xfer->open_path); 1.768 + return FALSE; 1.769 + } 1.770 + 1.771 + xfer->stop = st.st_size; 1.772 + xfer->remaining_len = xfer->stop - xfer->start; 1.773 + } 1.774 + 1.775 + /* we can now be certain that we are handling a download request from 1.776 + * the client. allocate the appropriate structures to hook into giFT */ 1.777 + if (!(transfer = start_upload (xfer, &chunk))) 1.778 + { 1.779 + GT->DBGFN (GT, "unable to register upload with the daemon"); 1.780 + return FALSE; 1.781 + } 1.782 + 1.783 + /* override 200 w/ 206 if the request is not the whole file size */ 1.784 + if (xfer->remaining_len != xfer->share_authd->size) 1.785 + xfer->code = 206; 1.786 + 1.787 + /* assign the circular references for passing the chunk along */ 1.788 + gt_transfer_set_chunk (xfer, chunk); 1.789 + 1.790 + /* finally, seek the file descriptor where it needs to be */ 1.791 + fseek (xfer->f, xfer->start, SEEK_SET); 1.792 + 1.793 + return TRUE; 1.794 +} 1.795 + 1.796 +static void server_handle_get (GtTransfer *xfer) 1.797 +{ 1.798 + TCPC *c; 1.799 + 1.800 + c = gt_transfer_get_tcpc (xfer); 1.801 + assert (xfer->chunk == NULL); 1.802 + 1.803 + /* WARNING: this block is duplicated in http_client:client_push_request */ 1.804 + if (!gt_server_setup_upload (xfer)) 1.805 + { 1.806 + if (xfer->code == 200) 1.807 + xfer->code = 404; 1.808 + 1.809 + gt_http_server_send_error_and_close (xfer, xfer->code); 1.810 + return; 1.811 + } 1.812 + 1.813 + input_add (c->fd, xfer, INPUT_WRITE, 1.814 + (InputCallback)send_http_response, TIMEOUT_DEF); 1.815 +} 1.816 + 1.817 +static void send_http_response (int fd, input_id id, GtTransfer *xfer) 1.818 +{ 1.819 + TCPC *c; 1.820 + 1.821 + c = gt_transfer_get_tcpc (xfer); 1.822 + 1.823 + if (net_sock_error (c->fd)) 1.824 + { 1.825 + gt_transfer_close (xfer, TRUE); 1.826 + return; 1.827 + } 1.828 + 1.829 + /* ok, send client the header */ 1.830 + reply_to_client_request (xfer); 1.831 + 1.832 + if (!strcasecmp (xfer->command, "HEAD")) 1.833 + { 1.834 + gt_transfer_close (xfer, TRUE); 1.835 + return; 1.836 + } 1.837 + 1.838 + /* disable header read timer */ 1.839 + timer_remove_zero (&xfer->header_timer); 1.840 + 1.841 + input_remove (id); 1.842 + input_add (c->fd, xfer, INPUT_WRITE, 1.843 + (InputCallback)gt_server_upload_file, 0); 1.844 +} 1.845 + 1.846 +/* 1.847 + * Uploads the file requests 1.848 + */ 1.849 +void gt_server_upload_file (int fd, input_id id, GtTransfer *xfer) 1.850 +{ 1.851 + TCPC *c; 1.852 + Chunk *chunk; 1.853 + char buf[RW_BUFFER]; 1.854 + size_t read_len; 1.855 + size_t size; 1.856 + int sent_len = 0; 1.857 + off_t remainder; 1.858 + 1.859 + c = gt_transfer_get_tcpc (xfer); 1.860 + chunk = gt_transfer_get_chunk (xfer); 1.861 + 1.862 + assert (xfer->f != NULL); 1.863 + 1.864 + /* number of bytes left to be uploaded by this chunk */ 1.865 + if ((remainder = xfer->remaining_len) <= 0) 1.866 + { 1.867 + /* for whatever reason this function may have been called when we have 1.868 + * already overrun the transfer...in that case we will simply fall 1.869 + * through to the end-of-transfer condition */ 1.870 + gt_transfer_write (xfer, chunk, NULL, 0); 1.871 + return; 1.872 + } 1.873 + 1.874 + size = sizeof (buf); 1.875 + 1.876 + if (size > remainder) 1.877 + size = remainder; 1.878 + 1.879 + /* 1.880 + * Ask giFT for the size we should send. If this returns 0, the upload 1.881 + * was suspended. 1.882 + */ 1.883 + if ((size = upload_throttle (chunk, size)) == 0) 1.884 + return; 1.885 + 1.886 + /* read as much as we can from the local file */ 1.887 + if (!(read_len = fread (buf, sizeof (char), size, xfer->f))) 1.888 + { 1.889 + GT->DBGFN (GT, "unable to read from %s: %s", xfer->open_path, 1.890 + GIFT_STRERROR ()); 1.891 + gt_transfer_status (xfer, SOURCE_CANCELLED, "Local read error"); 1.892 + gt_transfer_close (xfer, TRUE); 1.893 + return; 1.894 + } 1.895 + 1.896 + if ((sent_len = tcp_send (c, buf, MIN (read_len, remainder))) <= 0) 1.897 + { 1.898 + gt_transfer_status (xfer, SOURCE_CANCELLED, 1.899 + "Unable to send data block"); 1.900 + gt_transfer_close (xfer, TRUE); 1.901 + return; 1.902 + } 1.903 + 1.904 + /* check if the file was too small for the transfer TODO: this isn't 1.905 + * checked earlier, but should be */ 1.906 + if (read_len != size) 1.907 + { 1.908 + gt_transfer_status (xfer, SOURCE_CANCELLED, "Unexpected end of file"); 1.909 + gt_transfer_close (xfer, TRUE); 1.910 + return; 1.911 + } 1.912 + 1.913 + /* 1.914 + * Check for short send(). This could use fseek(), but I have this 1.915 + * growing feeling that using stdio everywhere is a bad idea. 1.916 + */ 1.917 + if (read_len != sent_len) 1.918 + { 1.919 + gt_transfer_status (xfer, SOURCE_CANCELLED, "Short send()"); 1.920 + gt_transfer_close (xfer, TRUE); 1.921 + return; 1.922 + } 1.923 + 1.924 + /* 1.925 + * Call gt_upload to report back to giFT. This will also cancel 1.926 + * the transfer if the upload has completed. 1.927 + */ 1.928 + gt_transfer_write (xfer, chunk, buf, sent_len); 1.929 +} 1.930 + 1.931 +/*****************************************************************************/ 1.932 + 1.933 +void gt_http_server_reset (TCPC *c) 1.934 +{ 1.935 + /* 1.936 + * This can happen because the GtTransfer and TCPC can be decoupled, as in 1.937 + * the case of a push request sent. 1.938 + */ 1.939 + if (!c) 1.940 + return; 1.941 + 1.942 + /* finish all queued writes before we reset */ 1.943 + tcp_flush (c, TRUE); 1.944 + 1.945 + /* reset the input state */ 1.946 + input_remove_all (c->fd); 1.947 + input_add (c->fd, c, INPUT_READ, 1.948 + (InputCallback)gt_http_server_dispatch, 2 * MINUTES); 1.949 +}