Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
diff src/transfer/source.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/transfer/source.c Sat Feb 20 21:18:28 2010 -0800 1.3 @@ -0,0 +1,754 @@ 1.4 +/* 1.5 + * $Id: source.c,v 1.12 2005/01/04 14:31:44 mkern Exp $ 1.6 + * 1.7 + * Copyright (C) 2002-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 +#include "gt_share_file.h" 1.22 + 1.23 +#include "encoding/url.h" 1.24 + 1.25 +#include "transfer/source.h" 1.26 +#include "transfer/download.h" 1.27 + 1.28 +/*****************************************************************************/ 1.29 + 1.30 +/* 1.31 + * Most of the goop in this file is for specifying each parameter for the giFT 1.32 + * source URL. The source URL is supposed to encode all the information 1.33 + * necessary for contacting a source. 1.34 + */ 1.35 + 1.36 +/*****************************************************************************/ 1.37 + 1.38 +typedef BOOL (*UnserializeFunc) (GtSource *gt, const char *key, 1.39 + const char *value); 1.40 +typedef BOOL (*SerializeFunc) (GtSource *gt, String *s); 1.41 + 1.42 +#define URL_OPT_SERIALIZE(name) \ 1.43 + BOOL gt_src_spew_##name (GtSource *gt, String *s) 1.44 + 1.45 +#define URL_OPT_UNSERIALIZE(name) \ 1.46 + BOOL gt_src_parse_##name (GtSource *gt, const char *key, const char *value) 1.47 + 1.48 +#define DECLARE_URL_OPT(name) \ 1.49 + static URL_OPT_SERIALIZE(name); \ 1.50 + static URL_OPT_UNSERIALIZE(name) 1.51 + 1.52 +/*****************************************************************************/ 1.53 + 1.54 +DECLARE_URL_OPT(ip); 1.55 +DECLARE_URL_OPT(port); 1.56 +DECLARE_URL_OPT(server_ip); 1.57 +DECLARE_URL_OPT(server_port); 1.58 +DECLARE_URL_OPT(guid); 1.59 +DECLARE_URL_OPT(fw); 1.60 +DECLARE_URL_OPT(index); 1.61 +DECLARE_URL_OPT(name); 1.62 + 1.63 +/* 1.64 + * Options that can go in our source URL format. 1.65 + */ 1.66 +static struct url_option 1.67 +{ 1.68 + const char *key; /* key in url (i.e. "port" in "port=6346") */ 1.69 + SerializeFunc serialize; 1.70 + UnserializeFunc unserialize; 1.71 +} gt_source_url_options[] = 1.72 +{ 1.73 + { "ip", gt_src_spew_ip, gt_src_parse_ip }, 1.74 + { "port", gt_src_spew_port, gt_src_parse_port }, 1.75 + { "sip", gt_src_spew_server_ip, gt_src_parse_server_ip }, 1.76 + { "sport", gt_src_spew_server_port, gt_src_parse_server_port }, 1.77 + { "fw", gt_src_spew_fw, gt_src_parse_fw }, 1.78 + { "guid", gt_src_spew_guid, gt_src_parse_guid }, 1.79 + { "index", gt_src_spew_index, gt_src_parse_index }, 1.80 + { "name", gt_src_spew_name, gt_src_parse_name }, 1.81 + { NULL, NULL, NULL } 1.82 +}; 1.83 + 1.84 +/*****************************************************************************/ 1.85 + 1.86 +/* 1.87 + * These functions return TRUE if they produced some output. 1.88 + */ 1.89 + 1.90 +static URL_OPT_SERIALIZE(ip) 1.91 +{ 1.92 + if (!gt->user_ip) 1.93 + return FALSE; 1.94 + 1.95 + string_appendf (s, "ip=%s", net_ip_str (gt->user_ip)); 1.96 + return TRUE; 1.97 +} 1.98 + 1.99 +static URL_OPT_SERIALIZE(port) 1.100 +{ 1.101 + if (gt->user_port == 6346) 1.102 + return FALSE; 1.103 + 1.104 + string_appendf (s, "port=%hu", gt->user_port); 1.105 + return TRUE; 1.106 +} 1.107 + 1.108 +static URL_OPT_SERIALIZE(name) 1.109 +{ 1.110 + if (!gt->filename) 1.111 + return FALSE; 1.112 + 1.113 + string_appendf (s, "name=%s", gt->filename); 1.114 + return TRUE; 1.115 +} 1.116 + 1.117 +static URL_OPT_SERIALIZE(guid) 1.118 +{ 1.119 + if (gt_guid_is_empty (gt->guid)) 1.120 + return FALSE; 1.121 + 1.122 + string_appendf (s, "guid=%s", gt_guid_str (gt->guid)); 1.123 + return TRUE; 1.124 +} 1.125 + 1.126 +static URL_OPT_SERIALIZE(server_ip) 1.127 +{ 1.128 + if (!gt->server_ip) 1.129 + return FALSE; 1.130 + 1.131 + string_appendf (s, "sip=%s", net_ip_str (gt->server_ip)); 1.132 + return TRUE; 1.133 +} 1.134 + 1.135 +static URL_OPT_SERIALIZE(server_port) 1.136 +{ 1.137 + if (gt->server_port == 6346) 1.138 + return FALSE; 1.139 + 1.140 + string_appendf (s, "sport=%hu", gt->server_port); 1.141 + return TRUE; 1.142 +} 1.143 + 1.144 +static URL_OPT_SERIALIZE(fw) 1.145 +{ 1.146 + if (!gt->firewalled) 1.147 + return FALSE; 1.148 + 1.149 + string_append (s, "fw=1"); 1.150 + return TRUE; 1.151 +} 1.152 + 1.153 +static URL_OPT_SERIALIZE(index) 1.154 +{ 1.155 + if (!gt->index) 1.156 + return FALSE; 1.157 + 1.158 + string_appendf (s, "index=%u", gt->index); 1.159 + return TRUE; 1.160 +} 1.161 + 1.162 +/*****************************************************************************/ 1.163 + 1.164 +/* 1.165 + * These functions return TRUE if they were successful. 1.166 + */ 1.167 + 1.168 +static URL_OPT_UNSERIALIZE(ip) 1.169 +{ 1.170 + in_addr_t ip; 1.171 + 1.172 + ip = net_ip (value); 1.173 + 1.174 + if (ip == 0 || ip == INADDR_NONE) 1.175 + return FALSE; 1.176 + 1.177 + gt->user_ip = ip; 1.178 + return TRUE; 1.179 +} 1.180 + 1.181 +static URL_OPT_UNSERIALIZE(port) 1.182 +{ 1.183 + unsigned long port; 1.184 + 1.185 + port = gift_strtoul (value); 1.186 + 1.187 + if (port == ULONG_MAX || port >= 65536) 1.188 + return FALSE; 1.189 + 1.190 + gt->user_port = port; 1.191 + return TRUE; 1.192 +} 1.193 + 1.194 +static URL_OPT_UNSERIALIZE(name) 1.195 +{ 1.196 + char *name; 1.197 + 1.198 + if (!(name = STRDUP (value))) 1.199 + return FALSE; 1.200 + 1.201 + gt->filename = name; 1.202 + return TRUE; 1.203 +} 1.204 + 1.205 +static URL_OPT_UNSERIALIZE(guid) 1.206 +{ 1.207 + gt_guid_t *guid; 1.208 + 1.209 + if (!(guid = gt_guid_bin (value))) 1.210 + return FALSE; 1.211 + 1.212 + free (gt->guid); 1.213 + gt->guid = guid; 1.214 + 1.215 + return TRUE; 1.216 +} 1.217 + 1.218 +static URL_OPT_UNSERIALIZE(server_ip) 1.219 +{ 1.220 + in_addr_t ip; 1.221 + 1.222 + ip = net_ip (value); 1.223 + 1.224 + if (ip == 0 || ip == INADDR_NONE) 1.225 + return FALSE; 1.226 + 1.227 + gt->server_ip = ip; 1.228 + return TRUE; 1.229 +} 1.230 + 1.231 +static URL_OPT_UNSERIALIZE(server_port) 1.232 +{ 1.233 + unsigned long port; 1.234 + 1.235 + port = gift_strtoul (value); 1.236 + 1.237 + if (port == ULONG_MAX || port >= 65536) 1.238 + return FALSE; 1.239 + 1.240 + gt->server_port = port; 1.241 + return TRUE; 1.242 +} 1.243 + 1.244 +static URL_OPT_UNSERIALIZE(fw) 1.245 +{ 1.246 + unsigned long fw; 1.247 + 1.248 + fw = gift_strtoul (value); 1.249 + 1.250 + if (fw != 0 && fw != 1) 1.251 + return FALSE; 1.252 + 1.253 + if (fw) 1.254 + gt->firewalled = TRUE; 1.255 + else 1.256 + gt->firewalled = FALSE; 1.257 + 1.258 + return TRUE; 1.259 +} 1.260 + 1.261 +static URL_OPT_UNSERIALIZE(index) 1.262 +{ 1.263 + unsigned long index; 1.264 + 1.265 + index = gift_strtoul (value); 1.266 + 1.267 + if (index == ULONG_MAX) 1.268 + return FALSE; 1.269 + 1.270 + gt->index = (uint32_t)index; 1.271 + return TRUE; 1.272 +} 1.273 + 1.274 +/*****************************************************************************/ 1.275 + 1.276 +/* 1.277 + * Old Gnutella URL format: 1.278 + * 1.279 + * Gnutella://<u-ip>:<u-port>@<s-ip>:<s-port>[[FW]]:<client-guid>/<index>/<name> 1.280 + * 1.281 + * server_port is the server's gnutella port. This should probably pass 1.282 + * back both the gnutella port instead and the peer's connecting port, to 1.283 + * help in disambiguating different users behind the same firewall. 1.284 + */ 1.285 +static BOOL parse_old_url (char *url, 1.286 + uint32_t *r_user_ip, uint16_t *r_user_port, 1.287 + uint32_t *r_server_ip, uint16_t *r_server_port, 1.288 + BOOL *firewalled, char **r_pushid, 1.289 + uint32_t *r_index, char **r_fname) 1.290 +{ 1.291 + char *port_and_flags; 1.292 + char *flag; 1.293 + 1.294 + string_sep (&url, "://"); 1.295 + 1.296 + /* TODO: check for more errors */ 1.297 + 1.298 + *r_user_ip = net_ip (string_sep (&url, ":")); 1.299 + *r_user_port = gift_strtoul (string_sep (&url, "@")); 1.300 + *r_server_ip = net_ip (string_sep (&url, ":")); 1.301 + 1.302 + /* handle bracketed flags after port. ugh, this is so ugly */ 1.303 + port_and_flags = string_sep (&url, ":"); 1.304 + *r_server_port = gift_strtoul (string_sep (&port_and_flags, "[")); 1.305 + 1.306 + if (!string_isempty (port_and_flags)) 1.307 + { 1.308 + /* grab any flags inside the brackets */ 1.309 + while ((flag = string_sep_set (&port_and_flags, ",]"))) 1.310 + { 1.311 + if (!STRCMP (flag, "FW")) 1.312 + *firewalled = TRUE; 1.313 + } 1.314 + } 1.315 + 1.316 + *r_pushid = string_sep (&url, "/"); 1.317 + *r_index = gift_strtoul (string_sep (&url, "/")); 1.318 + *r_fname = url; 1.319 + 1.320 + return TRUE; 1.321 +} 1.322 + 1.323 +static struct url_option *lookup_url_option (const char *key) 1.324 +{ 1.325 + struct url_option *url_opt; 1.326 + 1.327 + url_opt = >_source_url_options[0]; 1.328 + 1.329 + while (url_opt->key != NULL) 1.330 + { 1.331 + if (strcmp (url_opt->key, key) == 0) 1.332 + return url_opt; 1.333 + 1.334 + url_opt++; 1.335 + } 1.336 + 1.337 + return NULL; 1.338 +} 1.339 + 1.340 +/* 1.341 + * New parameter-based URL format: 1.342 + * 1.343 + * Gnutella:?ip=<u-ip>&port=<u-port>&sip=<s-ip>&sport=<s-port>[&fw=<FW>]... 1.344 + * 1.345 + * Parameters we don't understand are placed in gt_src->extra Dataset, so we 1.346 + * should be forwards and backwards compatible when adding new parameters. 1.347 + */ 1.348 +static BOOL parse_new_url (char *url, GtSource *gt) 1.349 +{ 1.350 + char *option; 1.351 + char *key; 1.352 + char *value; 1.353 + 1.354 + /* skip prefix */ 1.355 + string_sep (&url, ":?"); 1.356 + 1.357 + while ((option = string_sep (&url, "&"))) 1.358 + { 1.359 + struct url_option *url_opt; 1.360 + 1.361 + value = option; 1.362 + key = string_sep (&value, "="); 1.363 + 1.364 + if (string_isempty (key) || string_isempty (value)) 1.365 + continue; 1.366 + 1.367 + /* look up the key in our list of possible options */ 1.368 + if ((url_opt = lookup_url_option (key))) 1.369 + { 1.370 + /* unserialize the specified key */ 1.371 + if (url_opt->unserialize (gt, key, value)) 1.372 + continue; 1.373 + 1.374 + /* fail through on failure to store failed keys */ 1.375 + } 1.376 + 1.377 + /* store the unfound keys in the extra parameter dataset */ 1.378 + dataset_insertstr (>->extra, key, value); 1.379 + } 1.380 + 1.381 + return TRUE; 1.382 +} 1.383 + 1.384 +/*****************************************************************************/ 1.385 + 1.386 +static GtSource *handle_old_url (char *url) 1.387 +{ 1.388 + GtSource *gt; 1.389 + char *fname = NULL; 1.390 + char *guid_ascii = NULL; 1.391 + 1.392 + if (!(gt = gt_source_new ())) 1.393 + return NULL; 1.394 + 1.395 + if (!parse_old_url (url, >->user_ip, >->user_port, 1.396 + >->server_ip, >->server_port, 1.397 + >->firewalled, &guid_ascii, >->index, &fname)) 1.398 + { 1.399 + gt_source_free (gt); 1.400 + return NULL; 1.401 + } 1.402 + 1.403 + gt->filename = NULL; 1.404 + if (!string_isempty (fname)) 1.405 + gt->filename = STRDUP (fname); 1.406 + 1.407 + gt->guid = NULL; 1.408 + if (!string_isempty (guid_ascii)) 1.409 + gt->guid = gt_guid_bin (guid_ascii); 1.410 + 1.411 + return gt; 1.412 +} 1.413 + 1.414 +static GtSource *handle_new_url (char *url) 1.415 +{ 1.416 + GtSource *gt; 1.417 + 1.418 + if (!(gt = gt_source_new ())) 1.419 + return NULL; 1.420 + 1.421 + if (!parse_new_url (url, gt)) 1.422 + { 1.423 + gt_source_free (gt); 1.424 + return NULL; 1.425 + } 1.426 + 1.427 + return gt; 1.428 +} 1.429 + 1.430 +GtSource *gt_source_unserialize (const char *url) 1.431 +{ 1.432 + char *t_url; 1.433 + GtSource *src = NULL; 1.434 + 1.435 + if (!url) 1.436 + return NULL; 1.437 + 1.438 + if (!(t_url = STRDUP (url))) 1.439 + return NULL; 1.440 + 1.441 + /* 1.442 + * Determine whether this is the new format URL (beginning with 1.443 + * "Gnutella:?") or the old-style (starts with "Gnutella://") 1.444 + */ 1.445 + if (strncmp (t_url, "Gnutella://", sizeof ("Gnutella://") - 1) == 0) 1.446 + { 1.447 + src = handle_old_url (t_url); 1.448 + } 1.449 + else if (strncmp (t_url, "Gnutella:?", sizeof ("Gnutella:?") - 1) == 0) 1.450 + { 1.451 + src = handle_new_url (t_url); 1.452 + } 1.453 + else 1.454 + { 1.455 + /* do nothing */ 1.456 + } 1.457 + 1.458 + FREE (t_url); 1.459 + 1.460 + return src; 1.461 +} 1.462 + 1.463 +/* use the old format serialization for now */ 1.464 +#if 0 1.465 +static void unknown_opt (ds_data_t *key, ds_data_t *value, void *udata) 1.466 +{ 1.467 + String *str = udata; 1.468 + string_appendf (str, "%s=%s&", key->data, value->data); 1.469 +} 1.470 + 1.471 +char *gt_source_serialize (GtSource *gt) 1.472 +{ 1.473 + struct url_option *opt; 1.474 + char *url; 1.475 + size_t len; 1.476 + String str; 1.477 + 1.478 + string_init (&str); 1.479 + string_appendf (&str, "%s:?", GT->name); 1.480 + 1.481 + for (opt = gt_source_url_options; opt->key != NULL; opt++) 1.482 + { 1.483 + if (opt->serialize (gt, &str)) 1.484 + { 1.485 + /* append separator for next argument */ 1.486 + string_appendc (&str, '&'); 1.487 + } 1.488 + } 1.489 + 1.490 + /* copy unknown options to the URL */ 1.491 + dataset_foreach (gt->extra, unknown_opt, &str); 1.492 + 1.493 + len = str.len; 1.494 + assert (len > 0); 1.495 + 1.496 + url = string_finish_keep (&str); 1.497 + 1.498 + /* remove trailing separator (may not be there if source is empty) */ 1.499 + if (url[len - 1] == '&') 1.500 + url[len - 1] = 0; 1.501 + 1.502 + return url; 1.503 +} 1.504 +#endif 1.505 + 1.506 +/* serialize to the old format for now */ 1.507 +char *gt_source_serialize (GtSource *gt) 1.508 +{ 1.509 + String *str; 1.510 + 1.511 + if (!(str = string_new (NULL, 0, 0, TRUE))) 1.512 + return FALSE; 1.513 + 1.514 + string_appendf (str, "Gnutella://%s:%hu", net_ip_str (gt->user_ip), 1.515 + gt->user_port); 1.516 + 1.517 + string_appendf (str, "@%s:%hu", net_ip_str (gt->server_ip), 1.518 + gt->server_port); 1.519 + 1.520 + string_appendc (str, '['); 1.521 + 1.522 + if (gt->firewalled) 1.523 + string_append (str, "FW"); 1.524 + 1.525 + string_appendc (str, ']'); 1.526 + 1.527 + string_appendf (str, ":%s/%lu", 1.528 + STRING_NOTNULL (gt_guid_str (gt->guid)), (long)gt->index); 1.529 + string_appendf (str, "/%s", 1.530 + STRING_NOTNULL (gt->filename)); /* already encoded */ 1.531 + 1.532 + return string_free_keep (str); 1.533 +} 1.534 + 1.535 +/*****************************************************************************/ 1.536 + 1.537 +/* 1.538 + * This is called by the search result code in order to produce 1.539 + * source URLs. 1.540 + * 1.541 + * TODO: This is just wrong -- interface is not very extensible. The search 1.542 + * result code should probably use GtSource and call gt_source_serialize(). 1.543 + */ 1.544 +char *gt_source_url_new (const char *filename, uint32_t index, 1.545 + in_addr_t user_ip, uint16_t user_port, 1.546 + in_addr_t server_ip, uint16_t server_port, 1.547 + BOOL firewalled, const gt_guid_t *client_id) 1.548 +{ 1.549 + GtSource *src; 1.550 + char *url; 1.551 + 1.552 + if (!(src = gt_source_new ())) 1.553 + return NULL; 1.554 + 1.555 + gt_source_set_ip (src, user_ip); 1.556 + gt_source_set_port (src, user_port); 1.557 + gt_source_set_index (src, index); 1.558 + gt_source_set_server_ip (src, server_ip); 1.559 + gt_source_set_server_port (src, server_port); 1.560 + gt_source_set_firewalled (src, firewalled); 1.561 + 1.562 + if (!gt_source_set_guid (src, client_id) || 1.563 + !gt_source_set_filename (src, filename)) 1.564 + { 1.565 + gt_source_free (src); 1.566 + return NULL; 1.567 + } 1.568 + 1.569 + url = gt_source_serialize (src); 1.570 + gt_source_free (src); 1.571 + 1.572 + return url; 1.573 +} 1.574 + 1.575 +/*****************************************************************************/ 1.576 + 1.577 +GtSource *gt_source_new (void) 1.578 +{ 1.579 + GtSource *src; 1.580 + 1.581 + if (!(src = NEW (GtSource))) 1.582 + return NULL; 1.583 + 1.584 + /* special case: port is 6346 if not specified */ 1.585 + src->user_port = 6346; 1.586 + src->server_port = 6346; 1.587 + 1.588 + return src; 1.589 +} 1.590 + 1.591 +void gt_source_free (GtSource *gt) 1.592 +{ 1.593 + if (!gt) 1.594 + return; 1.595 + 1.596 + free (gt->guid); 1.597 + free (gt->filename); 1.598 + free (gt->status_txt); 1.599 + 1.600 + FREE (gt); 1.601 +} 1.602 + 1.603 +/*****************************************************************************/ 1.604 + 1.605 +void gt_source_set_ip (GtSource *src, in_addr_t ip) 1.606 +{ 1.607 + src->user_ip = ip; 1.608 +} 1.609 + 1.610 +void gt_source_set_port (GtSource *src, in_port_t port) 1.611 +{ 1.612 + src->user_port = port; 1.613 +} 1.614 + 1.615 +void gt_source_set_index (GtSource *src, uint32_t index) 1.616 +{ 1.617 + src->index = index; 1.618 +} 1.619 + 1.620 +void gt_source_set_server_ip (GtSource *src, in_addr_t server_ip) 1.621 +{ 1.622 + src->server_ip = server_ip; 1.623 +} 1.624 + 1.625 +void gt_source_set_server_port (GtSource *src, in_port_t server_port) 1.626 +{ 1.627 + src->server_port = server_port; 1.628 +} 1.629 + 1.630 +void gt_source_set_firewalled (GtSource *src, BOOL fw) 1.631 +{ 1.632 + src->firewalled = fw; 1.633 +} 1.634 + 1.635 +BOOL gt_source_set_filename (GtSource *src, const char *filename) 1.636 +{ 1.637 + char *encoded; 1.638 + 1.639 + /* special case for no filename */ 1.640 + if (!filename) 1.641 + { 1.642 + free (src->filename); 1.643 + src->filename = NULL; 1.644 + return TRUE; 1.645 + } 1.646 + 1.647 + if (!(encoded = gt_url_encode (filename))) 1.648 + return FALSE; 1.649 + 1.650 + src->filename = encoded; 1.651 + return TRUE; 1.652 +} 1.653 + 1.654 +BOOL gt_source_set_guid (GtSource *src, const gt_guid_t *guid) 1.655 +{ 1.656 + gt_guid_t *dup; 1.657 + 1.658 + if (!(dup = gt_guid_dup (guid))) 1.659 + return FALSE; 1.660 + 1.661 + src->guid = dup; 1.662 + return TRUE; 1.663 +} 1.664 + 1.665 +/*****************************************************************************/ 1.666 + 1.667 +int gnutella_source_cmp (Protocol *p, Source *a, Source *b) 1.668 +{ 1.669 + GtSource *gt_a = NULL; 1.670 + GtSource *gt_b = NULL; 1.671 + int ret = 0; 1.672 + 1.673 + if (!(gt_a = gt_source_unserialize (a->url)) || 1.674 + !(gt_b = gt_source_unserialize (b->url))) 1.675 + { 1.676 + gt_source_free (gt_a); 1.677 + gt_source_free (gt_b); 1.678 + return -1; 1.679 + } 1.680 + 1.681 + if (gt_a->user_ip > gt_b->user_ip) 1.682 + ret = 1; 1.683 + else if (gt_a->user_ip < gt_b->user_ip) 1.684 + ret = -1; 1.685 + 1.686 + /* 1.687 + * Having two sources with the same IP on the same transfer can trigger a 1.688 + * bug in most versions of giftd. At least as of giftd < 0.11.9, this 1.689 + * causes sources to be reactivated after download completion due to a bug 1.690 + * in handle_next_queued called from download_complete. To avoid that, we 1.691 + * pretend that multiple sources with the same hash from the same user_ip 1.692 + * are the same here, by ignoring the port. If the sources compare 1.693 + * equally, then one will replace the other when added, and there won't be 1.694 + * any dead sources with the same IP available to reactivate when the 1.695 + * download completes. 1.696 + * 1.697 + * It's ok for the client guid to be different, even if the IPs are the 1.698 + * same, since in that case the guid gets reflected in the user string, so 1.699 + * the bug in handle_next_queue() won't trigger. 1.700 + * 1.701 + * Also, Transfers that don't have hashes are ok since they can only ever 1.702 + * have one user. So, if either source doesn't have a hash the bug won't 1.703 + * trigger. 1.704 + */ 1.705 +#if 0 1.706 + if (gt_a->user_port > gt_b->user_port) 1.707 + ret = 1; 1.708 + else if (gt_a->user_port < gt_b->user_port) 1.709 + ret = -1; 1.710 +#endif 1.711 + 1.712 + /* if both IPs are private match by the guid */ 1.713 + if (gt_is_local_ip (gt_a->user_ip, gt_a->server_ip) && 1.714 + gt_is_local_ip (gt_b->user_ip, gt_b->server_ip)) 1.715 + { 1.716 + ret = gt_guid_cmp (gt_a->guid, gt_b->guid); 1.717 + } 1.718 + 1.719 + if (ret == 0) 1.720 + { 1.721 + /* if the hashes match consider them equal */ 1.722 + if (a->hash || b->hash) 1.723 + ret = gift_strcmp (a->hash, b->hash); 1.724 + else 1.725 + ret = gift_strcmp (gt_a->filename, gt_b->filename); 1.726 + } 1.727 + 1.728 + gt_source_free (gt_a); 1.729 + gt_source_free (gt_b); 1.730 + 1.731 + return ret; 1.732 +} 1.733 + 1.734 +int gnutella_source_add (Protocol *p, Transfer *transfer, Source *source) 1.735 +{ 1.736 + GtSource *src; 1.737 + 1.738 + assert (source->udata == NULL); 1.739 + 1.740 + if (!(src = gt_source_unserialize (source->url))) 1.741 + return FALSE; 1.742 + 1.743 + source->udata = src; 1.744 + 1.745 + /* track this download */ 1.746 + gt_download_add (transfer, source); 1.747 + 1.748 + return TRUE; 1.749 +} 1.750 + 1.751 +void gnutella_source_remove (Protocol *p, Transfer *transfer, Source *source) 1.752 +{ 1.753 + gt_download_remove (transfer, source); 1.754 + 1.755 + assert (source->udata != NULL); 1.756 + gt_source_free (source->udata); 1.757 +}