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 = &gt_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 (&gt->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, &gt->user_ip, &gt->user_port,
   1.396 +	                    &gt->server_ip, &gt->server_port,
   1.397 +	                    &gt->firewalled, &guid_ascii, &gt->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 +}