annotate src/gt_xfer.c @ 0:d39e1d0d75b6

initial add
author paulo@hit-nxdomain.opendns.com
date Sat, 20 Feb 2010 21:18:28 -0800
parents
children
rev   line source
paulo@0 1 /*
paulo@0 2 * $Id: gt_xfer.c,v 1.103 2005/01/05 14:08:40 mkern Exp $
paulo@0 3 *
paulo@0 4 * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net)
paulo@0 5 *
paulo@0 6 * This program is free software; you can redistribute it and/or modify it
paulo@0 7 * under the terms of the GNU General Public License as published by the
paulo@0 8 * Free Software Foundation; either version 2, or (at your option) any
paulo@0 9 * later version.
paulo@0 10 *
paulo@0 11 * This program is distributed in the hope that it will be useful, but
paulo@0 12 * WITHOUT ANY WARRANTY; without even the implied warranty of
paulo@0 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
paulo@0 14 * General Public License for more details.
paulo@0 15 */
paulo@0 16
paulo@0 17 #include "gt_gnutella.h"
paulo@0 18
paulo@0 19 #include "gt_xfer_obj.h"
paulo@0 20 #include "gt_xfer.h"
paulo@0 21 #include "gt_http_client.h"
paulo@0 22 #include "gt_http_server.h"
paulo@0 23 #include "gt_share.h"
paulo@0 24 #include "gt_share_file.h"
paulo@0 25 #include "gt_packet.h"
paulo@0 26 #include "gt_node.h"
paulo@0 27 #include "gt_node_list.h"
paulo@0 28 #include "gt_netorg.h"
paulo@0 29 #include "gt_connect.h"
paulo@0 30 #include "gt_bind.h"
paulo@0 31 #include "gt_utils.h"
paulo@0 32
paulo@0 33 #include "encoding/url.h"
paulo@0 34
paulo@0 35 #include "transfer/source.h"
paulo@0 36
paulo@0 37 /******************************************************************************/
paulo@0 38
paulo@0 39 /* maximum number of push connections in limbo each remote user */
paulo@0 40 #define PUSH_MAX_LIMBO gt_config_get_int("transfer/push_max_in_limbo=5")
paulo@0 41
paulo@0 42 /******************************************************************************/
paulo@0 43
paulo@0 44 /* an alternative to carrying the push TTL around in the source url */
paulo@0 45 #define PUSH_MAX_TTL 12
paulo@0 46
paulo@0 47 /* maximum time to keep push connections in "limbo" while awaiting giftd
paulo@0 48 * to reissue Chunks */
paulo@0 49 #define PUSH_LIMBO_TIMEOUT (4 * MINUTES)
paulo@0 50
paulo@0 51 /* how long to wait for a PUSH reply before timing out in order to free the
paulo@0 52 * Chunk */
paulo@0 53 #define PUSH_WAIT_INTERVAL (30 * SECONDS)
paulo@0 54
paulo@0 55 /* minimum interval between pushes */
paulo@0 56 #define PUSH_MIN_DEFER_TIME (30 * ESECONDS)
paulo@0 57
paulo@0 58 /* maximum amount of time a push will be forced to wait before being sent */
paulo@0 59 #define PUSH_MAX_DEFER_TIME (10 * EMINUTES)
paulo@0 60
paulo@0 61 /*****************************************************************************/
paulo@0 62
paulo@0 63 /* this stores information about an indirect ("pushed") download */
paulo@0 64 typedef struct gt_push_source
paulo@0 65 {
paulo@0 66 gt_guid_t *guid;
paulo@0 67 in_addr_t ip;
paulo@0 68 in_addr_t src_ip; /* whether this push was to a local source */
paulo@0 69 List *xfers; /* xfers for this source */
paulo@0 70 List *connections; /* connection for this source */
paulo@0 71 time_t last_sent; /* time of last push sent to this source */
paulo@0 72 double defer_time; /* min time to wait before sending another push
paulo@0 73 * doubles every push to PUSH_MAX_DEFER_TIME */
paulo@0 74 } GtPushSource;
paulo@0 75
paulo@0 76 /*****************************************************************************/
paulo@0 77
paulo@0 78 /* Maps guid->{list of unique GtPushSources} */
paulo@0 79 static Dataset *gt_push_requests;
paulo@0 80
paulo@0 81 /******************************************************************************/
paulo@0 82
paulo@0 83 static void push_source_reset_last_sent (GtPushSource *push_src);
paulo@0 84 static void push_source_set_last_sent (gt_guid_t *guid, in_addr_t ip);
paulo@0 85 static BOOL push_source_should_send (gt_guid_t *guid, in_addr_t ip);
paulo@0 86
paulo@0 87 /*****************************************************************************/
paulo@0 88
paulo@0 89 /*
paulo@0 90 * The source URL is stored on disk and could be outdated if the format has
paulo@0 91 * changed. This updates it with any changes when we first read it from
paulo@0 92 * the state file on startup.
paulo@0 93 */
paulo@0 94 static void replace_url (Source *src, GtSource *gt)
paulo@0 95 {
paulo@0 96 char *url;
paulo@0 97
paulo@0 98 if (!(url = gt_source_serialize (gt)))
paulo@0 99 return;
paulo@0 100
paulo@0 101 /* swap urls */
paulo@0 102 FREE (src->url);
paulo@0 103 src->url = url;
paulo@0 104 }
paulo@0 105
paulo@0 106 /******************************************************************************/
paulo@0 107
paulo@0 108 static FileShare *lookup_index (GtTransfer *xfer, char *request)
paulo@0 109 {
paulo@0 110 FileShare *file;
paulo@0 111 char *index_str;
paulo@0 112 char *filename;
paulo@0 113 char *decoded;
paulo@0 114 uint32_t index;
paulo@0 115
paulo@0 116 filename = request;
paulo@0 117 index_str = string_sep (&filename, "/");
paulo@0 118
paulo@0 119 if (!filename || !index_str)
paulo@0 120 return NULL;
paulo@0 121
paulo@0 122 index = ATOUL (index_str);
paulo@0 123
paulo@0 124 decoded = gt_url_decode (filename);
paulo@0 125 file = gt_share_local_lookup_by_index (index, decoded);
paulo@0 126
paulo@0 127 free (decoded);
paulo@0 128
paulo@0 129 /* the filename may or may not be url encoded */
paulo@0 130 if (!file)
paulo@0 131 file = gt_share_local_lookup_by_index (index, filename);
paulo@0 132
paulo@0 133 return file;
paulo@0 134 }
paulo@0 135
paulo@0 136 static FileShare *lookup_urns (GtTransfer *xfer, char *urns)
paulo@0 137 {
paulo@0 138 FileShare *file = NULL;
paulo@0 139 char *urn;
paulo@0 140
paulo@0 141 /*
paulo@0 142 * Try to lookup all urns provided in the header,
paulo@0 143 * until one is found.
paulo@0 144 */
paulo@0 145 while (!file && !string_isempty (urns))
paulo@0 146 {
paulo@0 147 urn = string_sep_set (&urns, ", ");
paulo@0 148 file = gt_share_local_lookup_by_urn (urn);
paulo@0 149 }
paulo@0 150
paulo@0 151 return file;
paulo@0 152 }
paulo@0 153
paulo@0 154 static FileShare *lookup_uri_res (GtTransfer *xfer, char *request)
paulo@0 155 {
paulo@0 156 FileShare *file = NULL;
paulo@0 157 char *resolver = NULL;
paulo@0 158 char *urn;
paulo@0 159
paulo@0 160 resolver = string_sep (&request, "?");
paulo@0 161 urn = string_sep (&request, " ");
paulo@0 162
paulo@0 163 if (resolver && !strcasecmp (resolver, "N2R"))
paulo@0 164 {
paulo@0 165 string_trim (request);
paulo@0 166 file = lookup_urns (xfer, urn);
paulo@0 167 }
paulo@0 168
paulo@0 169 if (file && HTTP_DEBUG)
paulo@0 170 GT->dbg (GT, "file=%s", share_get_hpath (file));
paulo@0 171
paulo@0 172 return file;
paulo@0 173 }
paulo@0 174
paulo@0 175 static Share *lookup_hpath (char *namespace, GtTransfer *xfer, char *request)
paulo@0 176 {
paulo@0 177 char *hpath;
paulo@0 178 Share *share;
paulo@0 179
paulo@0 180 /*
paulo@0 181 * Reconstruct the hpath
paulo@0 182 */
paulo@0 183 if (!(hpath = stringf_dup ("/%s/%s", namespace, request)))
paulo@0 184 return NULL;
paulo@0 185
paulo@0 186 if (HTTP_DEBUG)
paulo@0 187 GT->dbg (GT, "request by hpath: %s", hpath);
paulo@0 188
paulo@0 189 share = GT->share_lookup (GT, SHARE_LOOKUP_HPATH, hpath);
paulo@0 190 free (hpath);
paulo@0 191
paulo@0 192 return share;
paulo@0 193 }
paulo@0 194
paulo@0 195 /* Take a request for a file, i.e. everything after GET in:
paulo@0 196 *
paulo@0 197 * "GET /get/1279/filename.mp3"
paulo@0 198 *
paulo@0 199 * and convert it to a localized path to a file.
paulo@0 200 *
paulo@0 201 * Path has been "secured" already if necessary.
paulo@0 202 *
paulo@0 203 * The path returned must be static.
paulo@0 204 * TODO: this interface is a bit bizarre */
paulo@0 205 char *gt_localize_request (GtTransfer *xfer, char *s_path, int *authorized)
paulo@0 206 {
paulo@0 207 static char open_path[PATH_MAX];
paulo@0 208 char *namespace;
paulo@0 209 char *path, *path0;
paulo@0 210 char *content_urns;
paulo@0 211 FileShare *file;
paulo@0 212
paulo@0 213 /* TODO: use authorized for Browse Host (BH) requests */
paulo@0 214 if (!STRCMP (s_path, "/"))
paulo@0 215 {
paulo@0 216 /* *authorized = TRUE; */
paulo@0 217 if (HTTP_DEBUG)
paulo@0 218 GT->DBGFN (GT, "received unimplemented Browse Host request");
paulo@0 219
paulo@0 220 return NULL;
paulo@0 221 }
paulo@0 222
paulo@0 223 if (authorized)
paulo@0 224 *authorized = FALSE;
paulo@0 225
paulo@0 226 if (!(path0 = path = STRDUP (s_path)))
paulo@0 227 return NULL;
paulo@0 228
paulo@0 229 if (HTTP_DEBUG)
paulo@0 230 GT->dbg (GT, "path=%s", path);
paulo@0 231
paulo@0 232 /* get rid of leading slash */
paulo@0 233 string_sep (&path, "/");
paulo@0 234 namespace = string_sep (&path, "/");
paulo@0 235
paulo@0 236 if (!namespace || !path)
paulo@0 237 {
paulo@0 238 GT->DBGFN (GT, "null namespace or path: %s %s\n", namespace, path);
paulo@0 239 free (path0);
paulo@0 240 return NULL;
paulo@0 241 }
paulo@0 242
paulo@0 243 /*
paulo@0 244 * If the client supplied "X-Gnutella-Content-URN: ", lookup
paulo@0 245 * by that instead of the request.
paulo@0 246 */
paulo@0 247 content_urns = dataset_lookupstr (xfer->header, "x-gnutella-content-urn");
paulo@0 248
paulo@0 249 if (content_urns)
paulo@0 250 file = lookup_urns (xfer, content_urns);
paulo@0 251 else if (!strcasecmp (namespace, "get"))
paulo@0 252 file = lookup_index (xfer, path);
paulo@0 253 else if (!strcasecmp (namespace, "uri-res"))
paulo@0 254 file = lookup_uri_res (xfer, path);
paulo@0 255 else
paulo@0 256 file = lookup_hpath (namespace, xfer, path);
paulo@0 257
paulo@0 258 /*
paulo@0 259 * Set xfer->content_urn [which replies with 'X-Gnutella-Content-URN']
paulo@0 260 * to a comma-separated list of all URNs for this file.
paulo@0 261 */
paulo@0 262 xfer->content_urns = gt_share_local_get_urns (file);
paulo@0 263
paulo@0 264 if (!file)
paulo@0 265 {
paulo@0 266 if (HTTP_DEBUG)
paulo@0 267 GT->DBGFN (GT, "bad request: /%s/%s", namespace, path);
paulo@0 268
paulo@0 269 free (path0);
paulo@0 270 return NULL;
paulo@0 271 }
paulo@0 272
paulo@0 273 free (path0);
paulo@0 274
paulo@0 275 if (!share_complete (file))
paulo@0 276 return NULL;
paulo@0 277
paulo@0 278 /* argh, need to return static data */
paulo@0 279 snprintf (open_path, sizeof (open_path) - 1, "%s", share_get_hpath (file));
paulo@0 280
paulo@0 281 /* try to fill in the hash */
paulo@0 282 xfer->hash = share_dsp_hash (file, "SHA1");
paulo@0 283
paulo@0 284 return open_path;
paulo@0 285 }
paulo@0 286
paulo@0 287 /******************************************************************************/
paulo@0 288
paulo@0 289 static char *index_request (char *request, size_t size,
paulo@0 290 uint32_t index, const char *filename)
paulo@0 291 {
paulo@0 292 /*
paulo@0 293 * The filename may not have ever been set. Fail the request in
paulo@0 294 * this case.
paulo@0 295 */
paulo@0 296 if (!filename || string_isempty (filename))
paulo@0 297 return NULL;
paulo@0 298
paulo@0 299 /*
paulo@0 300 * Filename is encoded, we don't support sending unecoded requests
paulo@0 301 * anymore. NOTE: filename doesnt have leading '/' here, that may change
paulo@0 302 */
paulo@0 303 snprintf (request, size - 1, "/get/%u/%s", index, filename);
paulo@0 304 return request;
paulo@0 305 }
paulo@0 306
paulo@0 307 /*
paulo@0 308 * Setup a request string. Try request-by-hash (/uri-res/N2R?urn:sha1:..),
paulo@0 309 * but if there are any problems, fallback to a "/get/<index>/<filename>"
paulo@0 310 * request.
paulo@0 311 */
paulo@0 312 static char *request_str (Source *source, uint32_t index, char *filename)
paulo@0 313 {
paulo@0 314 static char request[RW_BUFFER];
paulo@0 315 char *hash = source->hash;
paulo@0 316 GtSource *gt;
paulo@0 317
paulo@0 318 gt = source->udata;
paulo@0 319 assert (gt != NULL);
paulo@0 320
paulo@0 321 /*
paulo@0 322 * Do a uri-res request unless one has failed already or
paulo@0 323 * if we have no filename and thus no choice but to use the hash.
paulo@0 324 * (doesn't happen currently but will for download-mesh sources
paulo@0 325 * that only have the hash and no filename)
paulo@0 326 */
paulo@0 327 if (hash && (!gt->uri_res_failed || string_isempty (filename)))
paulo@0 328 {
paulo@0 329 char *str0, *str;
paulo@0 330
paulo@0 331 if (!(str0 = STRDUP (hash)))
paulo@0 332 return index_request (request, sizeof (request), index, filename);
paulo@0 333
paulo@0 334 str = str0;
paulo@0 335 string_sep (&str, ":");
paulo@0 336
paulo@0 337 /* hashes are canonically uppercase on the gnet */
paulo@0 338 string_upper (str);
paulo@0 339
paulo@0 340 if (str)
paulo@0 341 {
paulo@0 342 snprintf (request, sizeof (request) - 1,
paulo@0 343 "/uri-res/N2R?urn:sha1:%s", str);
paulo@0 344 free (str0);
paulo@0 345 return request;
paulo@0 346 }
paulo@0 347
paulo@0 348 free (str0);
paulo@0 349 }
paulo@0 350
paulo@0 351 return index_request (request, sizeof (request), index, filename);
paulo@0 352 }
paulo@0 353
paulo@0 354 /*****************************************************************************/
paulo@0 355 /* PUSH HANDLING */
paulo@0 356
paulo@0 357 /*
paulo@0 358 * This code has to deal with some tricky race conditions involving
paulo@0 359 * chunk timeouts and pushes.
paulo@0 360 */
paulo@0 361
paulo@0 362 static GtPushSource *gt_push_source_new (gt_guid_t *guid, in_addr_t ip,
paulo@0 363 in_addr_t src_ip)
paulo@0 364 {
paulo@0 365 GtPushSource *src;
paulo@0 366
paulo@0 367 if (!(src = MALLOC (sizeof (GtPushSource))))
paulo@0 368 return NULL;
paulo@0 369
paulo@0 370 src->guid = gt_guid_dup (guid);
paulo@0 371 src->ip = ip;
paulo@0 372 src->src_ip = src_ip;
paulo@0 373 src->xfers = NULL;
paulo@0 374 src->connections = NULL;
paulo@0 375
paulo@0 376 push_source_reset_last_sent (src);
paulo@0 377
paulo@0 378 return src;
paulo@0 379 }
paulo@0 380
paulo@0 381 static void gt_push_source_free (GtPushSource *src)
paulo@0 382 {
paulo@0 383 if (!src)
paulo@0 384 return;
paulo@0 385
paulo@0 386 assert (src->xfers == NULL);
paulo@0 387 assert (src->connections == NULL);
paulo@0 388
paulo@0 389 free (src->guid);
paulo@0 390 free (src);
paulo@0 391 }
paulo@0 392
paulo@0 393 /*****************************************************************************/
paulo@0 394
paulo@0 395 /* TODO: break this into two parts, first part looks in the
paulo@0 396 * list for matching ip. If none is found, look for
paulo@0 397 * a firewalled (local ip) push source. */
paulo@0 398 static int find_ip (GtPushSource *src, in_addr_t *ip)
paulo@0 399 {
paulo@0 400 /* If the source is a local IP address behind a non-local one,
paulo@0 401 * authorize by just the client guid. Otherwise, use the IP. */
paulo@0 402 if (gt_is_local_ip (src->ip, src->src_ip) || src->ip == *ip)
paulo@0 403 return 0;
paulo@0 404
paulo@0 405 return -1;
paulo@0 406 }
paulo@0 407
paulo@0 408 static List *lookup_source_list (gt_guid_t *guid)
paulo@0 409 {
paulo@0 410 List *src_list;
paulo@0 411
paulo@0 412 if (!(src_list = dataset_lookup (gt_push_requests, guid, 16)))
paulo@0 413 return NULL;
paulo@0 414
paulo@0 415 return src_list;
paulo@0 416 }
paulo@0 417
paulo@0 418 static GtPushSource *push_source_lookup (gt_guid_t *guid, in_addr_t ip)
paulo@0 419 {
paulo@0 420 List *requests;
paulo@0 421 List *list;
paulo@0 422
paulo@0 423 if (!(requests = lookup_source_list (guid)))
paulo@0 424 return NULL;
paulo@0 425
paulo@0 426 list = list_find_custom (requests, &ip, (ListForeachFunc)find_ip);
paulo@0 427 return list_nth_data (list, 0);
paulo@0 428 }
paulo@0 429
paulo@0 430 /*****************************************************************************/
paulo@0 431
paulo@0 432 static void insert_source_list (gt_guid_t *guid, List *src_list)
paulo@0 433 {
paulo@0 434 if (!gt_push_requests)
paulo@0 435 gt_push_requests = dataset_new (DATASET_HASH);
paulo@0 436
paulo@0 437 dataset_insert (&gt_push_requests, guid, 16, src_list, 0);
paulo@0 438 }
paulo@0 439
paulo@0 440 static void add_push_source (List *pushes, gt_guid_t *guid, in_addr_t ip,
paulo@0 441 in_addr_t src_ip)
paulo@0 442 {
paulo@0 443 GtPushSource *src;
paulo@0 444 List *old_list;
paulo@0 445
paulo@0 446 if (!(src = gt_push_source_new (guid, ip, src_ip)))
paulo@0 447 return;
paulo@0 448
paulo@0 449 if ((old_list = list_find_custom (pushes, &ip, (ListForeachFunc)find_ip)))
paulo@0 450 {
paulo@0 451 /* push source is already there */
paulo@0 452 gt_push_source_free (src);
paulo@0 453 return;
paulo@0 454 }
paulo@0 455
paulo@0 456 pushes = list_prepend (pushes, src);
paulo@0 457 insert_source_list (guid, pushes);
paulo@0 458 }
paulo@0 459
paulo@0 460 void gt_push_source_add (gt_guid_t *guid, in_addr_t ip, in_addr_t src_ip)
paulo@0 461 {
paulo@0 462 List *pushes;
paulo@0 463
paulo@0 464 pushes = lookup_source_list (guid);
paulo@0 465 add_push_source (pushes, guid, ip, src_ip);
paulo@0 466 }
paulo@0 467
paulo@0 468 /*****************************************************************************/
paulo@0 469 /* Timing controls for push requests */
paulo@0 470
paulo@0 471 static void push_source_reset_last_sent (GtPushSource *push_src)
paulo@0 472 {
paulo@0 473 push_src->last_sent = gt_uptime (); /* wrong */
paulo@0 474 push_src->defer_time = 0.0;
paulo@0 475 }
paulo@0 476
paulo@0 477 static void push_source_set_last_sent (gt_guid_t *guid, in_addr_t ip)
paulo@0 478 {
paulo@0 479 GtPushSource *src;
paulo@0 480
paulo@0 481 if (!(src = push_source_lookup (guid, ip)))
paulo@0 482 return;
paulo@0 483
paulo@0 484 time (&src->last_sent);
paulo@0 485 }
paulo@0 486
paulo@0 487 static BOOL push_source_should_send (gt_guid_t *guid, in_addr_t ip)
paulo@0 488 {
paulo@0 489 GtPushSource *src;
paulo@0 490 double deferred;
paulo@0 491 time_t now;
paulo@0 492
paulo@0 493 time (&now);
paulo@0 494
paulo@0 495 if (!(src = push_source_lookup (guid, ip)))
paulo@0 496 return FALSE;
paulo@0 497
paulo@0 498 deferred = difftime (now, src->last_sent);
paulo@0 499
paulo@0 500 /* randomize the defer time a bit in order to not send pushes for all
paulo@0 501 * downloads at once */
paulo@0 502 if (deferred < src->defer_time + -10.0 + 20.0 * rand() / (RAND_MAX + 1.0))
paulo@0 503 return FALSE;
paulo@0 504
paulo@0 505 /*
paulo@0 506 * Double the defer time interval (the minimum time between sent
paulo@0 507 * pushes for this source).
paulo@0 508 */
paulo@0 509 src->defer_time *= 2;
paulo@0 510 if (src->defer_time >= PUSH_MAX_DEFER_TIME)
paulo@0 511 src->defer_time = PUSH_MAX_DEFER_TIME;
paulo@0 512
paulo@0 513 if (src->defer_time == 0)
paulo@0 514 src->defer_time = PUSH_MIN_DEFER_TIME;
paulo@0 515
paulo@0 516 return TRUE;
paulo@0 517 }
paulo@0 518
paulo@0 519 /*****************************************************************************/
paulo@0 520
paulo@0 521 static void flush_inputs (TCPC *c)
paulo@0 522 {
paulo@0 523 int ret;
paulo@0 524
paulo@0 525 assert (c->fd >= 0);
paulo@0 526
paulo@0 527 /* queued writes arent used by the HTTP system in this plugin,
paulo@0 528 * so this should always be true */
paulo@0 529 ret = tcp_flush (c, TRUE);
paulo@0 530 assert (ret == 0);
paulo@0 531
paulo@0 532 input_remove_all (c->fd);
paulo@0 533 }
paulo@0 534
paulo@0 535 static void continue_download (GtPushSource *push_src, GtTransfer *xfer,
paulo@0 536 TCPC *c)
paulo@0 537 {
paulo@0 538 Chunk *chunk;
paulo@0 539
paulo@0 540 chunk = gt_transfer_get_chunk (xfer);
paulo@0 541
paulo@0 542 /* remove all previous inputs */
paulo@0 543 flush_inputs (c);
paulo@0 544
paulo@0 545 /* HACK: remove the detach timeout */
paulo@0 546 timer_remove_zero (&xfer->detach_timer);
paulo@0 547
paulo@0 548 /* connect the TCPC and GtTransfer */
paulo@0 549 gt_transfer_set_tcpc (xfer, c);
paulo@0 550
paulo@0 551 /* update the IP and port for placing in Host: */
paulo@0 552 peer_addr (c->fd, &xfer->ip, &xfer->port);
paulo@0 553
paulo@0 554 /* the connection and the chunk have met up */
paulo@0 555 gt_transfer_status (xfer, SOURCE_WAITING, "Received GIV response");
paulo@0 556
paulo@0 557 if (HTTP_DEBUG)
paulo@0 558 GT->DBGSOCK (GT, c, "Continuing download for %s", xfer->request);
paulo@0 559
paulo@0 560 input_add (c->fd, xfer, INPUT_WRITE,
paulo@0 561 (InputCallback)gt_http_client_start, TIMEOUT_DEF);
paulo@0 562 }
paulo@0 563
paulo@0 564 static void reset_conn (int fd, input_id id, TCPC *c)
paulo@0 565 {
paulo@0 566 /*
paulo@0 567 * We should only get here if some data was sent, or if it timed out. In
paulo@0 568 * which case we should close this connection, because it shouldn't be
paulo@0 569 * sending anything.
paulo@0 570 */
paulo@0 571 if (HTTP_DEBUG)
paulo@0 572 {
paulo@0 573 if (fd == -1)
paulo@0 574 GT->DBGSOCK (GT, c, "connection timed out");
paulo@0 575 else
paulo@0 576 GT->DBGSOCK (GT, c, "connection closed or sent invalid data");
paulo@0 577 }
paulo@0 578
paulo@0 579 gt_push_source_remove_conn (c);
paulo@0 580 tcp_close (c);
paulo@0 581 }
paulo@0 582
paulo@0 583 static void store_conn (GtPushSource *src, TCPC *c)
paulo@0 584 {
paulo@0 585 flush_inputs (c);
paulo@0 586
paulo@0 587 input_add (c->fd, c, INPUT_READ,
paulo@0 588 (InputCallback)reset_conn, PUSH_LIMBO_TIMEOUT);
paulo@0 589
paulo@0 590 assert (!list_find (src->connections, c));
paulo@0 591 src->connections = list_prepend (src->connections, c);
paulo@0 592
paulo@0 593 if (HTTP_DEBUG)
paulo@0 594 GT->DBGSOCK (GT, c, "storing connection");
paulo@0 595 }
paulo@0 596
paulo@0 597 BOOL gt_push_source_add_conn (gt_guid_t *guid, in_addr_t ip, TCPC *c)
paulo@0 598 {
paulo@0 599 GtTransfer *xfer;
paulo@0 600 GtPushSource *push_src;
paulo@0 601
paulo@0 602 if (!(push_src = push_source_lookup (guid, ip)))
paulo@0 603 {
paulo@0 604 if (HTTP_DEBUG)
paulo@0 605 {
paulo@0 606 GT->err (GT, "couldn't find push source %s:[%s]",
paulo@0 607 gt_guid_str (guid), net_ip_str (ip));
paulo@0 608 }
paulo@0 609
paulo@0 610 tcp_close (c);
paulo@0 611 return FALSE;
paulo@0 612 }
paulo@0 613
paulo@0 614 /*
paulo@0 615 * Don't allow too many connections in flight from the same remote user.
paulo@0 616 */
paulo@0 617 if (list_length (push_src->connections) >= PUSH_MAX_LIMBO)
paulo@0 618 {
paulo@0 619 if (HTTP_DEBUG)
paulo@0 620 {
paulo@0 621 GT->DBGSOCK (GT, c, "too many push connections from %s, closing",
paulo@0 622 gt_guid_str (guid));
paulo@0 623 }
paulo@0 624
paulo@0 625 tcp_close (c);
paulo@0 626 return FALSE;
paulo@0 627 }
paulo@0 628
paulo@0 629 /*
paulo@0 630 * Since we now know this push source is alive, reset the push send
paulo@0 631 * tracking time: in case the connection is lost, we'll resend the push
paulo@0 632 * right away instead of waiting.
paulo@0 633 */
paulo@0 634 push_source_reset_last_sent (push_src);
paulo@0 635
paulo@0 636 /*
paulo@0 637 * Store the connection if there are no GtTransfer requests from
paulo@0 638 * giFT at the moment.
paulo@0 639 */
paulo@0 640 if (!push_src->xfers)
paulo@0 641 {
paulo@0 642 store_conn (push_src, c);
paulo@0 643 return FALSE;
paulo@0 644 }
paulo@0 645
paulo@0 646 xfer = list_nth_data (push_src->xfers, 0);
paulo@0 647 push_src->xfers = list_remove (push_src->xfers, xfer);
paulo@0 648
paulo@0 649 continue_download (push_src, xfer, c);
paulo@0 650 return TRUE;
paulo@0 651 }
paulo@0 652
paulo@0 653 /* return TRUE if there's a connection residing on this push source */
paulo@0 654 static BOOL push_source_lookup_conn (gt_guid_t *guid, in_addr_t ip)
paulo@0 655 {
paulo@0 656 GtPushSource *push_src;
paulo@0 657
paulo@0 658 if (!(push_src = push_source_lookup (guid, ip)))
paulo@0 659 return FALSE;
paulo@0 660
paulo@0 661 if (push_src->connections)
paulo@0 662 {
paulo@0 663 if (HTTP_DEBUG)
paulo@0 664 GT->DBGFN (GT, "found push connection for %s", net_ip_str (ip));
paulo@0 665
paulo@0 666 return TRUE;
paulo@0 667 }
paulo@0 668
paulo@0 669 return FALSE;
paulo@0 670 }
paulo@0 671
paulo@0 672 static void store_xfer (GtPushSource *src, GtTransfer *xfer)
paulo@0 673 {
paulo@0 674 assert (!list_find (src->xfers, xfer));
paulo@0 675 src->xfers = list_prepend (src->xfers, xfer);
paulo@0 676 }
paulo@0 677
paulo@0 678 BOOL gt_push_source_add_xfer (gt_guid_t *guid, in_addr_t ip,
paulo@0 679 in_addr_t src_ip, GtTransfer *xfer)
paulo@0 680 {
paulo@0 681 TCPC *c;
paulo@0 682 GtPushSource *push_src;
paulo@0 683
paulo@0 684 assert (xfer != NULL);
paulo@0 685
paulo@0 686 /* create the source if it doesn't exist already */
paulo@0 687 gt_push_source_add (guid, ip, src_ip);
paulo@0 688
paulo@0 689 if (!(push_src = push_source_lookup (guid, ip)))
paulo@0 690 {
paulo@0 691 if (HTTP_DEBUG)
paulo@0 692 {
paulo@0 693 GT->err (GT, "couldn't find push source (%s:[%s]) for chunk %s",
paulo@0 694 gt_guid_str (guid), net_ip_str (ip), xfer->request);
paulo@0 695 }
paulo@0 696
paulo@0 697 return FALSE;
paulo@0 698 }
paulo@0 699
paulo@0 700 /*
paulo@0 701 * Store the GtTransfer if there are no connections to service it
paulo@0 702 * at the moment.
paulo@0 703 */
paulo@0 704 if (!push_src->connections)
paulo@0 705 {
paulo@0 706 store_xfer (push_src, xfer);
paulo@0 707 return FALSE;
paulo@0 708 }
paulo@0 709
paulo@0 710 c = list_nth_data (push_src->connections, 0);
paulo@0 711 push_src->connections = list_remove (push_src->connections, c);
paulo@0 712
paulo@0 713 continue_download (push_src, xfer, c);
paulo@0 714 return TRUE;
paulo@0 715 }
paulo@0 716
paulo@0 717 /*****************************************************************************/
paulo@0 718
paulo@0 719 static BOOL remove_xfer (GtPushSource *src, GtTransfer *xfer)
paulo@0 720 {
paulo@0 721 src->xfers = list_remove (src->xfers, xfer);
paulo@0 722 return FALSE;
paulo@0 723 }
paulo@0 724
paulo@0 725 static void remove_xfer_list (ds_data_t *key, ds_data_t *value,
paulo@0 726 GtTransfer *xfer)
paulo@0 727 {
paulo@0 728 List *src_list = value->data;
paulo@0 729
paulo@0 730 list_foreach (src_list, (ListForeachFunc)remove_xfer, xfer);
paulo@0 731 }
paulo@0 732
paulo@0 733 /*
paulo@0 734 * The chunk is being cancelled, so remove it from being tracked.
paulo@0 735 *
paulo@0 736 * After this, if the push recipient connects, we will have to wait
paulo@0 737 * for another chunk timeout before transmitting.
paulo@0 738 */
paulo@0 739 void gt_push_source_remove_xfer (GtTransfer *xfer)
paulo@0 740 {
paulo@0 741 if (!xfer)
paulo@0 742 return;
paulo@0 743
paulo@0 744 dataset_foreach (gt_push_requests, DS_FOREACH(remove_xfer_list), xfer);
paulo@0 745 }
paulo@0 746
paulo@0 747 static BOOL remove_conn (GtPushSource *src, TCPC *c)
paulo@0 748 {
paulo@0 749 src->connections = list_remove (src->connections, c);
paulo@0 750 return FALSE;
paulo@0 751 }
paulo@0 752
paulo@0 753 static void remove_conn_list (ds_data_t *key, ds_data_t *value, TCPC *c)
paulo@0 754 {
paulo@0 755 List *src_list = value->data;
paulo@0 756
paulo@0 757 list_foreach (src_list, (ListForeachFunc)remove_conn, c);
paulo@0 758 }
paulo@0 759
paulo@0 760 /*
paulo@0 761 * The connection from this push download closed
paulo@0 762 */
paulo@0 763 void gt_push_source_remove_conn (TCPC *c)
paulo@0 764 {
paulo@0 765 if (!c)
paulo@0 766 return;
paulo@0 767
paulo@0 768 dataset_foreach (gt_push_requests, DS_FOREACH(remove_conn_list), c);
paulo@0 769 }
paulo@0 770
paulo@0 771 static BOOL cleanup_xfer (GtTransfer *xfer, void *udata)
paulo@0 772 {
paulo@0 773 gt_push_source_remove_xfer (xfer);
paulo@0 774 return TRUE;
paulo@0 775 }
paulo@0 776
paulo@0 777 static BOOL cleanup_conn (TCPC *c, void *udata)
paulo@0 778 {
paulo@0 779 gt_push_source_remove_conn (c);
paulo@0 780 tcp_close (c);
paulo@0 781 return TRUE;
paulo@0 782 }
paulo@0 783
paulo@0 784 static void remove_push_source (GtPushSource *src)
paulo@0 785 {
paulo@0 786 List *src_list;
paulo@0 787
paulo@0 788 src_list = lookup_source_list (src->guid);
paulo@0 789 src_list = list_remove (src_list, src);
paulo@0 790
paulo@0 791 insert_source_list (src->guid, src_list);
paulo@0 792 }
paulo@0 793
paulo@0 794 void gt_push_source_remove (gt_guid_t *guid, in_addr_t ip, in_addr_t src_ip)
paulo@0 795 {
paulo@0 796 GtPushSource *src;
paulo@0 797
paulo@0 798 if (!(src = push_source_lookup (guid, ip)))
paulo@0 799 return;
paulo@0 800
paulo@0 801 /* cleanup all the chunks and connections */
paulo@0 802 src->xfers =
paulo@0 803 list_foreach_remove (src->xfers, (ListForeachFunc)cleanup_xfer,
paulo@0 804 NULL);
paulo@0 805 src->connections =
paulo@0 806 list_foreach_remove (src->connections, (ListForeachFunc)cleanup_conn,
paulo@0 807 NULL);
paulo@0 808
paulo@0 809 /* remove this source from the global list */
paulo@0 810 remove_push_source (src);
paulo@0 811
paulo@0 812 gt_push_source_free (src);
paulo@0 813 }
paulo@0 814
paulo@0 815 /*****************************************************************************/
paulo@0 816
paulo@0 817 static BOOL detach_timeout (void *udata)
paulo@0 818 {
paulo@0 819 GtTransfer *xfer = udata;
paulo@0 820
paulo@0 821 /* Added this on 2004-12-22 to track observed assertion failure in
paulo@0 822 * gt_transfer_get_chunk. -- mkern
paulo@0 823 */
paulo@0 824 if (!xfer->chunk || xfer->chunk->udata != xfer)
paulo@0 825 {
paulo@0 826 GT->DBGFN (GT, "Detach timeout troubles. status = %d, "
paulo@0 827 "text = %s, xfer->ip = %s, "
paulo@0 828 "xfer = %p, xfer->chunk->udata = %p, "
paulo@0 829 "xfer->detach_timer = 0x%X",
paulo@0 830 xfer->detach_status, xfer->detach_msgtxt,
paulo@0 831 net_ip_str (xfer->ip), xfer,
paulo@0 832 xfer->chunk->udata, xfer->detach_timer);
paulo@0 833 }
paulo@0 834
paulo@0 835 /* Sometimes gt_transfer_status will trigger an
paulo@0 836 * assert (xfer->chunk->udata == xfer) failure in gt_transfer_get_chunk.
paulo@0 837 * But why? Is xfer already freed? Does it have another chunk and the
paulo@0 838 * timer was not removed?
paulo@0 839 */
paulo@0 840 gt_transfer_status (xfer, xfer->detach_status, xfer->detach_msgtxt);
paulo@0 841 gt_transfer_close (xfer, TRUE);
paulo@0 842
paulo@0 843 return FALSE;
paulo@0 844 }
paulo@0 845
paulo@0 846 /*
paulo@0 847 * Attach a timer that will "detach" the GtTransfer from the Chunk by
paulo@0 848 * cancelling it, but pretend that the Source is in some other state besides
paulo@0 849 * "cancelled" or "timed out" by changing the status text.
paulo@0 850 *
paulo@0 851 * This is useful to keep a semi-consistent UI in certain situations, such as
paulo@0 852 * sending out push requests, and cancelling requests when the remote side has
paulo@0 853 * queued our request.
paulo@0 854 */
paulo@0 855 static void detach_transfer_in (GtTransfer *xfer, SourceStatus status,
paulo@0 856 char *status_txt, time_t interval)
paulo@0 857 {
paulo@0 858 char *msg;
paulo@0 859
paulo@0 860 msg = STRDUP (status_txt);
paulo@0 861
paulo@0 862 gt_transfer_status (xfer, status, msg);
paulo@0 863 xfer->detach_status = status;
paulo@0 864
paulo@0 865 free (xfer->detach_msgtxt);
paulo@0 866 xfer->detach_msgtxt = msg;
paulo@0 867
paulo@0 868 xfer->detach_timer = timer_add (interval,
paulo@0 869 (TimerCallback)detach_timeout, xfer);
paulo@0 870 }
paulo@0 871
paulo@0 872 /*
paulo@0 873 * Detach an GtTransfer from its Chunk.
paulo@0 874 */
paulo@0 875 static void detach_transfer (GtTransfer *xfer, SourceStatus status,
paulo@0 876 char *msgtxt)
paulo@0 877 {
paulo@0 878 /*
paulo@0 879 * Cancelling from p->download_start will cause download_pause() to crash.
paulo@0 880 * So, the detach must happen from timer context.
paulo@0 881 */
paulo@0 882 detach_transfer_in (xfer, status, msgtxt, 2 * SECONDS);
paulo@0 883 }
paulo@0 884
paulo@0 885 /*****************************************************************************/
paulo@0 886
paulo@0 887 static void send_push (GtTransfer *xfer, GtSource *gt, TCPC *server)
paulo@0 888 {
paulo@0 889 GtPacket *packet;
paulo@0 890
paulo@0 891 if (!(packet = gt_packet_new (GT_MSG_PUSH, PUSH_MAX_TTL, NULL)))
paulo@0 892 return;
paulo@0 893
paulo@0 894 gt_packet_put_ustr (packet, gt->guid, 16);
paulo@0 895 gt_packet_put_uint32 (packet, gt->index);
paulo@0 896 gt_packet_put_ip (packet, GT_NODE(server)->my_ip);
paulo@0 897 gt_packet_put_port (packet, GT_SELF->gt_port);
paulo@0 898
paulo@0 899 if (gt_packet_error (packet))
paulo@0 900 {
paulo@0 901 gt_packet_free (packet);
paulo@0 902 return;
paulo@0 903 }
paulo@0 904
paulo@0 905 gt_packet_send (server, packet);
paulo@0 906 gt_packet_free (packet);
paulo@0 907
paulo@0 908 /*
paulo@0 909 * Don't wait for the whole Chunk timeout -- that keeps the Chunk
paulo@0 910 * occupied for too long if there are other active sources (the Chunk
paulo@0 911 * also times out longer and longer each time, so this gets worse
paulo@0 912 * the longer the transfer is inactive).
paulo@0 913 *
paulo@0 914 * This is really an infelicity of the Chunk system.
paulo@0 915 */
paulo@0 916 detach_transfer_in (xfer, SOURCE_QUEUED_REMOTE, "Sent PUSH request",
paulo@0 917 PUSH_WAIT_INTERVAL);
paulo@0 918
paulo@0 919 /* set the last time we sent a push to now */
paulo@0 920 push_source_set_last_sent (gt->guid, gt->user_ip);
paulo@0 921 }
paulo@0 922
paulo@0 923 static BOOL send_push_to_server (in_addr_t server_ip, in_port_t server_port,
paulo@0 924 GtTransfer *xfer, GtSource *gt)
paulo@0 925 {
paulo@0 926 GtNode *server;
paulo@0 927
paulo@0 928 if (!(server = gt_node_lookup (server_ip, server_port)))
paulo@0 929 {
paulo@0 930 server = gt_node_register (server_ip, server_port,
paulo@0 931 GT_NODE_ULTRA);
paulo@0 932 }
paulo@0 933
paulo@0 934 if (!server)
paulo@0 935 {
paulo@0 936 GT->err (GT, "couldn't register server");
paulo@0 937 return FALSE;
paulo@0 938 }
paulo@0 939
paulo@0 940 if (server->state & (GT_NODE_CONNECTED | GT_NODE_CONNECTING_2))
paulo@0 941 {
paulo@0 942 assert (GT_CONN(server) != NULL);
paulo@0 943
paulo@0 944 /* Server is in a state for receiving packets -- send the push */
paulo@0 945 send_push (xfer, gt, GT_CONN(server));
paulo@0 946 return TRUE;
paulo@0 947 }
paulo@0 948 else if (server->state & GT_NODE_CONNECTING_1)
paulo@0 949 {
paulo@0 950 /* dont try to connect again; wait till we're connected */
paulo@0 951 return FALSE;
paulo@0 952 }
paulo@0 953 else if (gt_conn_need_connections (GT_NODE_ULTRA) > 0 &&
paulo@0 954 !server->tried_connect &&
paulo@0 955 gt_connect (server) >= 0)
paulo@0 956 {
paulo@0 957 /*
paulo@0 958 * We've tried to connect to the server so we could deliver the push
paulo@0 959 * request eventually NOTE: this doesnt send a push until the next
paulo@0 960 * chunk timeout.
paulo@0 961 */
paulo@0 962 return FALSE;
paulo@0 963 }
paulo@0 964
paulo@0 965 return FALSE;
paulo@0 966 }
paulo@0 967
paulo@0 968 static void handle_push_download (Chunk *chunk, GtTransfer *xfer, GtSource *gt)
paulo@0 969 {
paulo@0 970 GtNode *server;
paulo@0 971
paulo@0 972 /*
paulo@0 973 * If this succeeds, we already have a connection to this
paulo@0 974 * user and the transfer will continue by using that connection.
paulo@0 975 *
paulo@0 976 * TODO: the gt_push_source_add() should be used by some
paulo@0 977 * per-source data structure
paulo@0 978 */
paulo@0 979 if (gt_push_source_add_xfer (gt->guid, gt->user_ip, gt->server_ip, xfer))
paulo@0 980 return;
paulo@0 981
paulo@0 982 /*
paulo@0 983 * Dont send pushes too often. Maybe should use a global queue instead.
paulo@0 984 *
paulo@0 985 * NOTE: we can't free the xfer here because we have stored it.
paulo@0 986 */
paulo@0 987 if (push_source_should_send (gt->guid, gt->user_ip) == FALSE)
paulo@0 988 {
paulo@0 989 /* don't occupy the Chunk forever */
paulo@0 990 detach_transfer_in (xfer, SOURCE_QUEUED_REMOTE, "Awaiting connection",
paulo@0 991 PUSH_WAIT_INTERVAL);
paulo@0 992 return;
paulo@0 993 }
paulo@0 994
paulo@0 995 /*
paulo@0 996 * Next, try to find the server that supplied this result,
paulo@0 997 * and send them a push.
paulo@0 998 */
paulo@0 999 if (send_push_to_server (gt->server_ip, gt->server_port, xfer, gt))
paulo@0 1000 return;
paulo@0 1001
paulo@0 1002 /*
paulo@0 1003 * Finally, try sending to a random connected server.
paulo@0 1004 *
paulo@0 1005 * TODO: these should be rate-limited, either globally or
paulo@0 1006 * per-source.
paulo@0 1007 */
paulo@0 1008 if ((server = gt_conn_random (GT_NODE_ULTRA, GT_NODE_CONNECTED)))
paulo@0 1009 {
paulo@0 1010 send_push_to_server (server->ip, server->gt_port, xfer, gt);
paulo@0 1011 return;
paulo@0 1012 }
paulo@0 1013
paulo@0 1014 detach_transfer (xfer, SOURCE_QUEUED_REMOTE, "No PUSH route");
paulo@0 1015 }
paulo@0 1016
paulo@0 1017 static BOOL set_request (GtTransfer *xfer, Chunk *chunk, Source *source,
paulo@0 1018 GtSource *gt_src)
paulo@0 1019 {
paulo@0 1020 char *request;
paulo@0 1021
paulo@0 1022 if (!chunk || !xfer)
paulo@0 1023 return FALSE;
paulo@0 1024
paulo@0 1025 request = request_str (source, gt_src->index, gt_src->filename);
paulo@0 1026
paulo@0 1027 if (!gt_transfer_set_request (xfer, request))
paulo@0 1028 {
paulo@0 1029 GT->DBGFN (GT, "UI made an invalid request for '%s'", request);
paulo@0 1030 return FALSE;
paulo@0 1031 }
paulo@0 1032
paulo@0 1033 /* connect the xfer and the chunk */
paulo@0 1034 gt_transfer_set_chunk (xfer, chunk);
paulo@0 1035
paulo@0 1036 return TRUE;
paulo@0 1037 }
paulo@0 1038
paulo@0 1039 static BOOL should_push (GtSource *gt)
paulo@0 1040 {
paulo@0 1041 TCPC *persistent;
paulo@0 1042
paulo@0 1043 /* we cannot push if there is no guid to send the push to */
paulo@0 1044 if (gt_guid_is_empty (gt->guid))
paulo@0 1045 return FALSE;
paulo@0 1046
paulo@0 1047 persistent = gt_http_connection_lookup (GT_TRANSFER_DOWNLOAD,
paulo@0 1048 gt->user_ip,
paulo@0 1049 gt->user_port);
paulo@0 1050
paulo@0 1051 /* need to close the connection to re-add it to the list, because
paulo@0 1052 * _lookup removes it from the persistent connection list */
paulo@0 1053 gt_http_connection_close (GT_TRANSFER_DOWNLOAD, persistent, FALSE);
paulo@0 1054
paulo@0 1055 /* if we already have a connection don't send a push */
paulo@0 1056 if (persistent)
paulo@0 1057 return FALSE;
paulo@0 1058
paulo@0 1059 /* now check for a persistent "pushed" connection, which would be stored
paulo@0 1060 * separately from a directly connected one */
paulo@0 1061 if (push_source_lookup_conn (gt->guid, gt->user_ip))
paulo@0 1062 return TRUE;
paulo@0 1063
paulo@0 1064 /* send a push if the IP is local */
paulo@0 1065 if (gt_is_local_ip (gt->user_ip, gt->server_ip))
paulo@0 1066 return TRUE;
paulo@0 1067
paulo@0 1068 /* don't send a push if we cannot receive incoming connections */
paulo@0 1069 if (gt_bind_is_firewalled())
paulo@0 1070 return FALSE;
paulo@0 1071
paulo@0 1072 /* send a push if they set the firewalled bit */
paulo@0 1073 if (gt->firewalled)
paulo@0 1074 return TRUE;
paulo@0 1075
paulo@0 1076 /* the last connection attempt failed, so try a push */
paulo@0 1077 if (gt->connect_failed)
paulo@0 1078 return TRUE;
paulo@0 1079
paulo@0 1080 return FALSE;
paulo@0 1081 }
paulo@0 1082
paulo@0 1083 static void handle_download (Chunk *chunk, GtTransfer *xfer, GtSource *gt)
paulo@0 1084 {
paulo@0 1085 /*
paulo@0 1086 * Send a push, or connect directly.
paulo@0 1087 */
paulo@0 1088 if (should_push (gt))
paulo@0 1089 {
paulo@0 1090 /* (possibly) retry a connection attempt next time */
paulo@0 1091 gt->connect_failed = FALSE;
paulo@0 1092
paulo@0 1093 handle_push_download (chunk, xfer, gt);
paulo@0 1094 }
paulo@0 1095 else
paulo@0 1096 {
paulo@0 1097 gt_http_client_get (chunk, xfer);
paulo@0 1098 }
paulo@0 1099 }
paulo@0 1100
paulo@0 1101 static BOOL download_is_queued (GtSource *gt)
paulo@0 1102 {
paulo@0 1103 /* back out if the request is still too early */
paulo@0 1104 if (time (NULL) < gt->retry_time)
paulo@0 1105 return TRUE;
paulo@0 1106
paulo@0 1107 return FALSE;
paulo@0 1108 }
paulo@0 1109
paulo@0 1110 int gnutella_download_start (Protocol *p, Transfer *transfer, Chunk *chunk,
paulo@0 1111 Source *source)
paulo@0 1112 {
paulo@0 1113 GtTransfer *xfer;
paulo@0 1114 GtSource *gt;
paulo@0 1115 off_t start;
paulo@0 1116 off_t stop;
paulo@0 1117
paulo@0 1118 gt = source->udata;
paulo@0 1119 assert (gt != NULL);
paulo@0 1120
paulo@0 1121 /* giftd should send us only deactivated Chunks */
paulo@0 1122 assert (chunk->udata == NULL);
paulo@0 1123
paulo@0 1124 /* free the Source URL and update it with any format changes */
paulo@0 1125 replace_url (source, gt);
paulo@0 1126
paulo@0 1127 /* thank you, pretender :) */
paulo@0 1128 start = chunk->start + chunk->transmit;
paulo@0 1129 stop = chunk->stop;
paulo@0 1130
paulo@0 1131 if (!(xfer = gt_transfer_new (GT_TRANSFER_DOWNLOAD, source,
paulo@0 1132 gt->user_ip, gt->user_port, start, stop)))
paulo@0 1133 {
paulo@0 1134 GT->DBGFN (GT, "gt_transfer_new failed");
paulo@0 1135 return FALSE;
paulo@0 1136 }
paulo@0 1137
paulo@0 1138 if (!set_request (xfer, chunk, source, gt))
paulo@0 1139 {
paulo@0 1140 gt_transfer_close (xfer, TRUE);
paulo@0 1141 return FALSE;
paulo@0 1142 }
paulo@0 1143
paulo@0 1144 if (download_is_queued (gt))
paulo@0 1145 {
paulo@0 1146 detach_transfer (xfer, SOURCE_QUEUED_REMOTE, gt->status_txt);
paulo@0 1147 return TRUE;
paulo@0 1148 }
paulo@0 1149
paulo@0 1150 handle_download (chunk, xfer, gt);
paulo@0 1151
paulo@0 1152 return TRUE;
paulo@0 1153 }
paulo@0 1154
paulo@0 1155 void gnutella_download_stop (Protocol *p, Transfer *transfer, Chunk *chunk,
paulo@0 1156 Source *source, BOOL complete)
paulo@0 1157 {
paulo@0 1158 gt_download_cancel (chunk, NULL);
paulo@0 1159 }
paulo@0 1160
paulo@0 1161 int gnutella_upload_start (Protocol *p, Transfer *transfer, Chunk *chunk,
paulo@0 1162 Source *source, unsigned long avail)
paulo@0 1163 {
paulo@0 1164 return TRUE;
paulo@0 1165 }
paulo@0 1166
paulo@0 1167 void gnutella_upload_stop (Protocol *p, Transfer *transfer, Chunk *chunk,
paulo@0 1168 Source *source)
paulo@0 1169 {
paulo@0 1170 gt_upload_cancel (chunk, NULL);
paulo@0 1171 }
paulo@0 1172
paulo@0 1173 int gnutella_chunk_suspend (Protocol *p, Transfer *transfer, Chunk *chunk,
paulo@0 1174 Source *source)
paulo@0 1175 {
paulo@0 1176 return gt_chunk_suspend (chunk, transfer, NULL);
paulo@0 1177 }
paulo@0 1178
paulo@0 1179 int gnutella_chunk_resume (Protocol *p, Transfer *transfer, Chunk *chunk,
paulo@0 1180 Source *source)
paulo@0 1181 {
paulo@0 1182 return gt_chunk_resume (chunk, transfer, NULL);
paulo@0 1183 }