paulo@0: /* paulo@0: * $Id: source.c,v 1.12 2005/01/04 14:31:44 mkern Exp $ paulo@0: * paulo@0: * Copyright (C) 2002-2003 giFT project (gift.sourceforge.net) paulo@0: * paulo@0: * This program is free software; you can redistribute it and/or modify it paulo@0: * under the terms of the GNU General Public License as published by the paulo@0: * Free Software Foundation; either version 2, or (at your option) any paulo@0: * later version. paulo@0: * paulo@0: * This program is distributed in the hope that it will be useful, but paulo@0: * WITHOUT ANY WARRANTY; without even the implied warranty of paulo@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU paulo@0: * General Public License for more details. paulo@0: */ paulo@0: paulo@0: #include "gt_gnutella.h" paulo@0: #include "gt_share_file.h" paulo@0: paulo@0: #include "encoding/url.h" paulo@0: paulo@0: #include "transfer/source.h" paulo@0: #include "transfer/download.h" paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* paulo@0: * Most of the goop in this file is for specifying each parameter for the giFT paulo@0: * source URL. The source URL is supposed to encode all the information paulo@0: * necessary for contacting a source. paulo@0: */ paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: typedef BOOL (*UnserializeFunc) (GtSource *gt, const char *key, paulo@0: const char *value); paulo@0: typedef BOOL (*SerializeFunc) (GtSource *gt, String *s); paulo@0: paulo@0: #define URL_OPT_SERIALIZE(name) \ paulo@0: BOOL gt_src_spew_##name (GtSource *gt, String *s) paulo@0: paulo@0: #define URL_OPT_UNSERIALIZE(name) \ paulo@0: BOOL gt_src_parse_##name (GtSource *gt, const char *key, const char *value) paulo@0: paulo@0: #define DECLARE_URL_OPT(name) \ paulo@0: static URL_OPT_SERIALIZE(name); \ paulo@0: static URL_OPT_UNSERIALIZE(name) paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: DECLARE_URL_OPT(ip); paulo@0: DECLARE_URL_OPT(port); paulo@0: DECLARE_URL_OPT(server_ip); paulo@0: DECLARE_URL_OPT(server_port); paulo@0: DECLARE_URL_OPT(guid); paulo@0: DECLARE_URL_OPT(fw); paulo@0: DECLARE_URL_OPT(index); paulo@0: DECLARE_URL_OPT(name); paulo@0: paulo@0: /* paulo@0: * Options that can go in our source URL format. paulo@0: */ paulo@0: static struct url_option paulo@0: { paulo@0: const char *key; /* key in url (i.e. "port" in "port=6346") */ paulo@0: SerializeFunc serialize; paulo@0: UnserializeFunc unserialize; paulo@0: } gt_source_url_options[] = paulo@0: { paulo@0: { "ip", gt_src_spew_ip, gt_src_parse_ip }, paulo@0: { "port", gt_src_spew_port, gt_src_parse_port }, paulo@0: { "sip", gt_src_spew_server_ip, gt_src_parse_server_ip }, paulo@0: { "sport", gt_src_spew_server_port, gt_src_parse_server_port }, paulo@0: { "fw", gt_src_spew_fw, gt_src_parse_fw }, paulo@0: { "guid", gt_src_spew_guid, gt_src_parse_guid }, paulo@0: { "index", gt_src_spew_index, gt_src_parse_index }, paulo@0: { "name", gt_src_spew_name, gt_src_parse_name }, paulo@0: { NULL, NULL, NULL } paulo@0: }; paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* paulo@0: * These functions return TRUE if they produced some output. paulo@0: */ paulo@0: paulo@0: static URL_OPT_SERIALIZE(ip) paulo@0: { paulo@0: if (!gt->user_ip) paulo@0: return FALSE; paulo@0: paulo@0: string_appendf (s, "ip=%s", net_ip_str (gt->user_ip)); paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_SERIALIZE(port) paulo@0: { paulo@0: if (gt->user_port == 6346) paulo@0: return FALSE; paulo@0: paulo@0: string_appendf (s, "port=%hu", gt->user_port); paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_SERIALIZE(name) paulo@0: { paulo@0: if (!gt->filename) paulo@0: return FALSE; paulo@0: paulo@0: string_appendf (s, "name=%s", gt->filename); paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_SERIALIZE(guid) paulo@0: { paulo@0: if (gt_guid_is_empty (gt->guid)) paulo@0: return FALSE; paulo@0: paulo@0: string_appendf (s, "guid=%s", gt_guid_str (gt->guid)); paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_SERIALIZE(server_ip) paulo@0: { paulo@0: if (!gt->server_ip) paulo@0: return FALSE; paulo@0: paulo@0: string_appendf (s, "sip=%s", net_ip_str (gt->server_ip)); paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_SERIALIZE(server_port) paulo@0: { paulo@0: if (gt->server_port == 6346) paulo@0: return FALSE; paulo@0: paulo@0: string_appendf (s, "sport=%hu", gt->server_port); paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_SERIALIZE(fw) paulo@0: { paulo@0: if (!gt->firewalled) paulo@0: return FALSE; paulo@0: paulo@0: string_append (s, "fw=1"); paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_SERIALIZE(index) paulo@0: { paulo@0: if (!gt->index) paulo@0: return FALSE; paulo@0: paulo@0: string_appendf (s, "index=%u", gt->index); paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* paulo@0: * These functions return TRUE if they were successful. paulo@0: */ paulo@0: paulo@0: static URL_OPT_UNSERIALIZE(ip) paulo@0: { paulo@0: in_addr_t ip; paulo@0: paulo@0: ip = net_ip (value); paulo@0: paulo@0: if (ip == 0 || ip == INADDR_NONE) paulo@0: return FALSE; paulo@0: paulo@0: gt->user_ip = ip; paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_UNSERIALIZE(port) paulo@0: { paulo@0: unsigned long port; paulo@0: paulo@0: port = gift_strtoul (value); paulo@0: paulo@0: if (port == ULONG_MAX || port >= 65536) paulo@0: return FALSE; paulo@0: paulo@0: gt->user_port = port; paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_UNSERIALIZE(name) paulo@0: { paulo@0: char *name; paulo@0: paulo@0: if (!(name = STRDUP (value))) paulo@0: return FALSE; paulo@0: paulo@0: gt->filename = name; paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_UNSERIALIZE(guid) paulo@0: { paulo@0: gt_guid_t *guid; paulo@0: paulo@0: if (!(guid = gt_guid_bin (value))) paulo@0: return FALSE; paulo@0: paulo@0: free (gt->guid); paulo@0: gt->guid = guid; paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_UNSERIALIZE(server_ip) paulo@0: { paulo@0: in_addr_t ip; paulo@0: paulo@0: ip = net_ip (value); paulo@0: paulo@0: if (ip == 0 || ip == INADDR_NONE) paulo@0: return FALSE; paulo@0: paulo@0: gt->server_ip = ip; paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_UNSERIALIZE(server_port) paulo@0: { paulo@0: unsigned long port; paulo@0: paulo@0: port = gift_strtoul (value); paulo@0: paulo@0: if (port == ULONG_MAX || port >= 65536) paulo@0: return FALSE; paulo@0: paulo@0: gt->server_port = port; paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_UNSERIALIZE(fw) paulo@0: { paulo@0: unsigned long fw; paulo@0: paulo@0: fw = gift_strtoul (value); paulo@0: paulo@0: if (fw != 0 && fw != 1) paulo@0: return FALSE; paulo@0: paulo@0: if (fw) paulo@0: gt->firewalled = TRUE; paulo@0: else paulo@0: gt->firewalled = FALSE; paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static URL_OPT_UNSERIALIZE(index) paulo@0: { paulo@0: unsigned long index; paulo@0: paulo@0: index = gift_strtoul (value); paulo@0: paulo@0: if (index == ULONG_MAX) paulo@0: return FALSE; paulo@0: paulo@0: gt->index = (uint32_t)index; paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* paulo@0: * Old Gnutella URL format: paulo@0: * paulo@0: * Gnutella://:@:[[FW]]:// paulo@0: * paulo@0: * server_port is the server's gnutella port. This should probably pass paulo@0: * back both the gnutella port instead and the peer's connecting port, to paulo@0: * help in disambiguating different users behind the same firewall. paulo@0: */ paulo@0: static BOOL parse_old_url (char *url, paulo@0: uint32_t *r_user_ip, uint16_t *r_user_port, paulo@0: uint32_t *r_server_ip, uint16_t *r_server_port, paulo@0: BOOL *firewalled, char **r_pushid, paulo@0: uint32_t *r_index, char **r_fname) paulo@0: { paulo@0: char *port_and_flags; paulo@0: char *flag; paulo@0: paulo@0: string_sep (&url, "://"); paulo@0: paulo@0: /* TODO: check for more errors */ paulo@0: paulo@0: *r_user_ip = net_ip (string_sep (&url, ":")); paulo@0: *r_user_port = gift_strtoul (string_sep (&url, "@")); paulo@0: *r_server_ip = net_ip (string_sep (&url, ":")); paulo@0: paulo@0: /* handle bracketed flags after port. ugh, this is so ugly */ paulo@0: port_and_flags = string_sep (&url, ":"); paulo@0: *r_server_port = gift_strtoul (string_sep (&port_and_flags, "[")); paulo@0: paulo@0: if (!string_isempty (port_and_flags)) paulo@0: { paulo@0: /* grab any flags inside the brackets */ paulo@0: while ((flag = string_sep_set (&port_and_flags, ",]"))) paulo@0: { paulo@0: if (!STRCMP (flag, "FW")) paulo@0: *firewalled = TRUE; paulo@0: } paulo@0: } paulo@0: paulo@0: *r_pushid = string_sep (&url, "/"); paulo@0: *r_index = gift_strtoul (string_sep (&url, "/")); paulo@0: *r_fname = url; paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static struct url_option *lookup_url_option (const char *key) paulo@0: { paulo@0: struct url_option *url_opt; paulo@0: paulo@0: url_opt = >_source_url_options[0]; paulo@0: paulo@0: while (url_opt->key != NULL) paulo@0: { paulo@0: if (strcmp (url_opt->key, key) == 0) paulo@0: return url_opt; paulo@0: paulo@0: url_opt++; paulo@0: } paulo@0: paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: /* paulo@0: * New parameter-based URL format: paulo@0: * paulo@0: * Gnutella:?ip=&port=&sip=&sport=[&fw=]... paulo@0: * paulo@0: * Parameters we don't understand are placed in gt_src->extra Dataset, so we paulo@0: * should be forwards and backwards compatible when adding new parameters. paulo@0: */ paulo@0: static BOOL parse_new_url (char *url, GtSource *gt) paulo@0: { paulo@0: char *option; paulo@0: char *key; paulo@0: char *value; paulo@0: paulo@0: /* skip prefix */ paulo@0: string_sep (&url, ":?"); paulo@0: paulo@0: while ((option = string_sep (&url, "&"))) paulo@0: { paulo@0: struct url_option *url_opt; paulo@0: paulo@0: value = option; paulo@0: key = string_sep (&value, "="); paulo@0: paulo@0: if (string_isempty (key) || string_isempty (value)) paulo@0: continue; paulo@0: paulo@0: /* look up the key in our list of possible options */ paulo@0: if ((url_opt = lookup_url_option (key))) paulo@0: { paulo@0: /* unserialize the specified key */ paulo@0: if (url_opt->unserialize (gt, key, value)) paulo@0: continue; paulo@0: paulo@0: /* fail through on failure to store failed keys */ paulo@0: } paulo@0: paulo@0: /* store the unfound keys in the extra parameter dataset */ paulo@0: dataset_insertstr (>->extra, key, value); paulo@0: } paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static GtSource *handle_old_url (char *url) paulo@0: { paulo@0: GtSource *gt; paulo@0: char *fname = NULL; paulo@0: char *guid_ascii = NULL; paulo@0: paulo@0: if (!(gt = gt_source_new ())) paulo@0: return NULL; paulo@0: paulo@0: if (!parse_old_url (url, >->user_ip, >->user_port, paulo@0: >->server_ip, >->server_port, paulo@0: >->firewalled, &guid_ascii, >->index, &fname)) paulo@0: { paulo@0: gt_source_free (gt); paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: gt->filename = NULL; paulo@0: if (!string_isempty (fname)) paulo@0: gt->filename = STRDUP (fname); paulo@0: paulo@0: gt->guid = NULL; paulo@0: if (!string_isempty (guid_ascii)) paulo@0: gt->guid = gt_guid_bin (guid_ascii); paulo@0: paulo@0: return gt; paulo@0: } paulo@0: paulo@0: static GtSource *handle_new_url (char *url) paulo@0: { paulo@0: GtSource *gt; paulo@0: paulo@0: if (!(gt = gt_source_new ())) paulo@0: return NULL; paulo@0: paulo@0: if (!parse_new_url (url, gt)) paulo@0: { paulo@0: gt_source_free (gt); paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: return gt; paulo@0: } paulo@0: paulo@0: GtSource *gt_source_unserialize (const char *url) paulo@0: { paulo@0: char *t_url; paulo@0: GtSource *src = NULL; paulo@0: paulo@0: if (!url) paulo@0: return NULL; paulo@0: paulo@0: if (!(t_url = STRDUP (url))) paulo@0: return NULL; paulo@0: paulo@0: /* paulo@0: * Determine whether this is the new format URL (beginning with paulo@0: * "Gnutella:?") or the old-style (starts with "Gnutella://") paulo@0: */ paulo@0: if (strncmp (t_url, "Gnutella://", sizeof ("Gnutella://") - 1) == 0) paulo@0: { paulo@0: src = handle_old_url (t_url); paulo@0: } paulo@0: else if (strncmp (t_url, "Gnutella:?", sizeof ("Gnutella:?") - 1) == 0) paulo@0: { paulo@0: src = handle_new_url (t_url); paulo@0: } paulo@0: else paulo@0: { paulo@0: /* do nothing */ paulo@0: } paulo@0: paulo@0: FREE (t_url); paulo@0: paulo@0: return src; paulo@0: } paulo@0: paulo@0: /* use the old format serialization for now */ paulo@0: #if 0 paulo@0: static void unknown_opt (ds_data_t *key, ds_data_t *value, void *udata) paulo@0: { paulo@0: String *str = udata; paulo@0: string_appendf (str, "%s=%s&", key->data, value->data); paulo@0: } paulo@0: paulo@0: char *gt_source_serialize (GtSource *gt) paulo@0: { paulo@0: struct url_option *opt; paulo@0: char *url; paulo@0: size_t len; paulo@0: String str; paulo@0: paulo@0: string_init (&str); paulo@0: string_appendf (&str, "%s:?", GT->name); paulo@0: paulo@0: for (opt = gt_source_url_options; opt->key != NULL; opt++) paulo@0: { paulo@0: if (opt->serialize (gt, &str)) paulo@0: { paulo@0: /* append separator for next argument */ paulo@0: string_appendc (&str, '&'); paulo@0: } paulo@0: } paulo@0: paulo@0: /* copy unknown options to the URL */ paulo@0: dataset_foreach (gt->extra, unknown_opt, &str); paulo@0: paulo@0: len = str.len; paulo@0: assert (len > 0); paulo@0: paulo@0: url = string_finish_keep (&str); paulo@0: paulo@0: /* remove trailing separator (may not be there if source is empty) */ paulo@0: if (url[len - 1] == '&') paulo@0: url[len - 1] = 0; paulo@0: paulo@0: return url; paulo@0: } paulo@0: #endif paulo@0: paulo@0: /* serialize to the old format for now */ paulo@0: char *gt_source_serialize (GtSource *gt) paulo@0: { paulo@0: String *str; paulo@0: paulo@0: if (!(str = string_new (NULL, 0, 0, TRUE))) paulo@0: return FALSE; paulo@0: paulo@0: string_appendf (str, "Gnutella://%s:%hu", net_ip_str (gt->user_ip), paulo@0: gt->user_port); paulo@0: paulo@0: string_appendf (str, "@%s:%hu", net_ip_str (gt->server_ip), paulo@0: gt->server_port); paulo@0: paulo@0: string_appendc (str, '['); paulo@0: paulo@0: if (gt->firewalled) paulo@0: string_append (str, "FW"); paulo@0: paulo@0: string_appendc (str, ']'); paulo@0: paulo@0: string_appendf (str, ":%s/%lu", paulo@0: STRING_NOTNULL (gt_guid_str (gt->guid)), (long)gt->index); paulo@0: string_appendf (str, "/%s", paulo@0: STRING_NOTNULL (gt->filename)); /* already encoded */ paulo@0: paulo@0: return string_free_keep (str); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* paulo@0: * This is called by the search result code in order to produce paulo@0: * source URLs. paulo@0: * paulo@0: * TODO: This is just wrong -- interface is not very extensible. The search paulo@0: * result code should probably use GtSource and call gt_source_serialize(). paulo@0: */ paulo@0: char *gt_source_url_new (const char *filename, uint32_t index, paulo@0: in_addr_t user_ip, uint16_t user_port, paulo@0: in_addr_t server_ip, uint16_t server_port, paulo@0: BOOL firewalled, const gt_guid_t *client_id) paulo@0: { paulo@0: GtSource *src; paulo@0: char *url; paulo@0: paulo@0: if (!(src = gt_source_new ())) paulo@0: return NULL; paulo@0: paulo@0: gt_source_set_ip (src, user_ip); paulo@0: gt_source_set_port (src, user_port); paulo@0: gt_source_set_index (src, index); paulo@0: gt_source_set_server_ip (src, server_ip); paulo@0: gt_source_set_server_port (src, server_port); paulo@0: gt_source_set_firewalled (src, firewalled); paulo@0: paulo@0: if (!gt_source_set_guid (src, client_id) || paulo@0: !gt_source_set_filename (src, filename)) paulo@0: { paulo@0: gt_source_free (src); paulo@0: return NULL; paulo@0: } paulo@0: paulo@0: url = gt_source_serialize (src); paulo@0: gt_source_free (src); paulo@0: paulo@0: return url; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: GtSource *gt_source_new (void) paulo@0: { paulo@0: GtSource *src; paulo@0: paulo@0: if (!(src = NEW (GtSource))) paulo@0: return NULL; paulo@0: paulo@0: /* special case: port is 6346 if not specified */ paulo@0: src->user_port = 6346; paulo@0: src->server_port = 6346; paulo@0: paulo@0: return src; paulo@0: } paulo@0: paulo@0: void gt_source_free (GtSource *gt) paulo@0: { paulo@0: if (!gt) paulo@0: return; paulo@0: paulo@0: free (gt->guid); paulo@0: free (gt->filename); paulo@0: free (gt->status_txt); paulo@0: paulo@0: FREE (gt); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: void gt_source_set_ip (GtSource *src, in_addr_t ip) paulo@0: { paulo@0: src->user_ip = ip; paulo@0: } paulo@0: paulo@0: void gt_source_set_port (GtSource *src, in_port_t port) paulo@0: { paulo@0: src->user_port = port; paulo@0: } paulo@0: paulo@0: void gt_source_set_index (GtSource *src, uint32_t index) paulo@0: { paulo@0: src->index = index; paulo@0: } paulo@0: paulo@0: void gt_source_set_server_ip (GtSource *src, in_addr_t server_ip) paulo@0: { paulo@0: src->server_ip = server_ip; paulo@0: } paulo@0: paulo@0: void gt_source_set_server_port (GtSource *src, in_port_t server_port) paulo@0: { paulo@0: src->server_port = server_port; paulo@0: } paulo@0: paulo@0: void gt_source_set_firewalled (GtSource *src, BOOL fw) paulo@0: { paulo@0: src->firewalled = fw; paulo@0: } paulo@0: paulo@0: BOOL gt_source_set_filename (GtSource *src, const char *filename) paulo@0: { paulo@0: char *encoded; paulo@0: paulo@0: /* special case for no filename */ paulo@0: if (!filename) paulo@0: { paulo@0: free (src->filename); paulo@0: src->filename = NULL; paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: if (!(encoded = gt_url_encode (filename))) paulo@0: return FALSE; paulo@0: paulo@0: src->filename = encoded; paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: BOOL gt_source_set_guid (GtSource *src, const gt_guid_t *guid) paulo@0: { paulo@0: gt_guid_t *dup; paulo@0: paulo@0: if (!(dup = gt_guid_dup (guid))) paulo@0: return FALSE; paulo@0: paulo@0: src->guid = dup; paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: int gnutella_source_cmp (Protocol *p, Source *a, Source *b) paulo@0: { paulo@0: GtSource *gt_a = NULL; paulo@0: GtSource *gt_b = NULL; paulo@0: int ret = 0; paulo@0: paulo@0: if (!(gt_a = gt_source_unserialize (a->url)) || paulo@0: !(gt_b = gt_source_unserialize (b->url))) paulo@0: { paulo@0: gt_source_free (gt_a); paulo@0: gt_source_free (gt_b); paulo@0: return -1; paulo@0: } paulo@0: paulo@0: if (gt_a->user_ip > gt_b->user_ip) paulo@0: ret = 1; paulo@0: else if (gt_a->user_ip < gt_b->user_ip) paulo@0: ret = -1; paulo@0: paulo@0: /* paulo@0: * Having two sources with the same IP on the same transfer can trigger a paulo@0: * bug in most versions of giftd. At least as of giftd < 0.11.9, this paulo@0: * causes sources to be reactivated after download completion due to a bug paulo@0: * in handle_next_queued called from download_complete. To avoid that, we paulo@0: * pretend that multiple sources with the same hash from the same user_ip paulo@0: * are the same here, by ignoring the port. If the sources compare paulo@0: * equally, then one will replace the other when added, and there won't be paulo@0: * any dead sources with the same IP available to reactivate when the paulo@0: * download completes. paulo@0: * paulo@0: * It's ok for the client guid to be different, even if the IPs are the paulo@0: * same, since in that case the guid gets reflected in the user string, so paulo@0: * the bug in handle_next_queue() won't trigger. paulo@0: * paulo@0: * Also, Transfers that don't have hashes are ok since they can only ever paulo@0: * have one user. So, if either source doesn't have a hash the bug won't paulo@0: * trigger. paulo@0: */ paulo@0: #if 0 paulo@0: if (gt_a->user_port > gt_b->user_port) paulo@0: ret = 1; paulo@0: else if (gt_a->user_port < gt_b->user_port) paulo@0: ret = -1; paulo@0: #endif paulo@0: paulo@0: /* if both IPs are private match by the guid */ paulo@0: if (gt_is_local_ip (gt_a->user_ip, gt_a->server_ip) && paulo@0: gt_is_local_ip (gt_b->user_ip, gt_b->server_ip)) paulo@0: { paulo@0: ret = gt_guid_cmp (gt_a->guid, gt_b->guid); paulo@0: } paulo@0: paulo@0: if (ret == 0) paulo@0: { paulo@0: /* if the hashes match consider them equal */ paulo@0: if (a->hash || b->hash) paulo@0: ret = gift_strcmp (a->hash, b->hash); paulo@0: else paulo@0: ret = gift_strcmp (gt_a->filename, gt_b->filename); paulo@0: } paulo@0: paulo@0: gt_source_free (gt_a); paulo@0: gt_source_free (gt_b); paulo@0: paulo@0: return ret; paulo@0: } paulo@0: paulo@0: int gnutella_source_add (Protocol *p, Transfer *transfer, Source *source) paulo@0: { paulo@0: GtSource *src; paulo@0: paulo@0: assert (source->udata == NULL); paulo@0: paulo@0: if (!(src = gt_source_unserialize (source->url))) paulo@0: return FALSE; paulo@0: paulo@0: source->udata = src; paulo@0: paulo@0: /* track this download */ paulo@0: gt_download_add (transfer, source); paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: void gnutella_source_remove (Protocol *p, Transfer *transfer, Source *source) paulo@0: { paulo@0: gt_download_remove (transfer, source); paulo@0: paulo@0: assert (source->udata != NULL); paulo@0: gt_source_free (source->udata); paulo@0: }