annotate 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
rev   line source
paulo@0 1 /*
paulo@0 2 * $Id: source.c,v 1.12 2005/01/04 14:31:44 mkern Exp $
paulo@0 3 *
paulo@0 4 * Copyright (C) 2002-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 #include "gt_share_file.h"
paulo@0 19
paulo@0 20 #include "encoding/url.h"
paulo@0 21
paulo@0 22 #include "transfer/source.h"
paulo@0 23 #include "transfer/download.h"
paulo@0 24
paulo@0 25 /*****************************************************************************/
paulo@0 26
paulo@0 27 /*
paulo@0 28 * Most of the goop in this file is for specifying each parameter for the giFT
paulo@0 29 * source URL. The source URL is supposed to encode all the information
paulo@0 30 * necessary for contacting a source.
paulo@0 31 */
paulo@0 32
paulo@0 33 /*****************************************************************************/
paulo@0 34
paulo@0 35 typedef BOOL (*UnserializeFunc) (GtSource *gt, const char *key,
paulo@0 36 const char *value);
paulo@0 37 typedef BOOL (*SerializeFunc) (GtSource *gt, String *s);
paulo@0 38
paulo@0 39 #define URL_OPT_SERIALIZE(name) \
paulo@0 40 BOOL gt_src_spew_##name (GtSource *gt, String *s)
paulo@0 41
paulo@0 42 #define URL_OPT_UNSERIALIZE(name) \
paulo@0 43 BOOL gt_src_parse_##name (GtSource *gt, const char *key, const char *value)
paulo@0 44
paulo@0 45 #define DECLARE_URL_OPT(name) \
paulo@0 46 static URL_OPT_SERIALIZE(name); \
paulo@0 47 static URL_OPT_UNSERIALIZE(name)
paulo@0 48
paulo@0 49 /*****************************************************************************/
paulo@0 50
paulo@0 51 DECLARE_URL_OPT(ip);
paulo@0 52 DECLARE_URL_OPT(port);
paulo@0 53 DECLARE_URL_OPT(server_ip);
paulo@0 54 DECLARE_URL_OPT(server_port);
paulo@0 55 DECLARE_URL_OPT(guid);
paulo@0 56 DECLARE_URL_OPT(fw);
paulo@0 57 DECLARE_URL_OPT(index);
paulo@0 58 DECLARE_URL_OPT(name);
paulo@0 59
paulo@0 60 /*
paulo@0 61 * Options that can go in our source URL format.
paulo@0 62 */
paulo@0 63 static struct url_option
paulo@0 64 {
paulo@0 65 const char *key; /* key in url (i.e. "port" in "port=6346") */
paulo@0 66 SerializeFunc serialize;
paulo@0 67 UnserializeFunc unserialize;
paulo@0 68 } gt_source_url_options[] =
paulo@0 69 {
paulo@0 70 { "ip", gt_src_spew_ip, gt_src_parse_ip },
paulo@0 71 { "port", gt_src_spew_port, gt_src_parse_port },
paulo@0 72 { "sip", gt_src_spew_server_ip, gt_src_parse_server_ip },
paulo@0 73 { "sport", gt_src_spew_server_port, gt_src_parse_server_port },
paulo@0 74 { "fw", gt_src_spew_fw, gt_src_parse_fw },
paulo@0 75 { "guid", gt_src_spew_guid, gt_src_parse_guid },
paulo@0 76 { "index", gt_src_spew_index, gt_src_parse_index },
paulo@0 77 { "name", gt_src_spew_name, gt_src_parse_name },
paulo@0 78 { NULL, NULL, NULL }
paulo@0 79 };
paulo@0 80
paulo@0 81 /*****************************************************************************/
paulo@0 82
paulo@0 83 /*
paulo@0 84 * These functions return TRUE if they produced some output.
paulo@0 85 */
paulo@0 86
paulo@0 87 static URL_OPT_SERIALIZE(ip)
paulo@0 88 {
paulo@0 89 if (!gt->user_ip)
paulo@0 90 return FALSE;
paulo@0 91
paulo@0 92 string_appendf (s, "ip=%s", net_ip_str (gt->user_ip));
paulo@0 93 return TRUE;
paulo@0 94 }
paulo@0 95
paulo@0 96 static URL_OPT_SERIALIZE(port)
paulo@0 97 {
paulo@0 98 if (gt->user_port == 6346)
paulo@0 99 return FALSE;
paulo@0 100
paulo@0 101 string_appendf (s, "port=%hu", gt->user_port);
paulo@0 102 return TRUE;
paulo@0 103 }
paulo@0 104
paulo@0 105 static URL_OPT_SERIALIZE(name)
paulo@0 106 {
paulo@0 107 if (!gt->filename)
paulo@0 108 return FALSE;
paulo@0 109
paulo@0 110 string_appendf (s, "name=%s", gt->filename);
paulo@0 111 return TRUE;
paulo@0 112 }
paulo@0 113
paulo@0 114 static URL_OPT_SERIALIZE(guid)
paulo@0 115 {
paulo@0 116 if (gt_guid_is_empty (gt->guid))
paulo@0 117 return FALSE;
paulo@0 118
paulo@0 119 string_appendf (s, "guid=%s", gt_guid_str (gt->guid));
paulo@0 120 return TRUE;
paulo@0 121 }
paulo@0 122
paulo@0 123 static URL_OPT_SERIALIZE(server_ip)
paulo@0 124 {
paulo@0 125 if (!gt->server_ip)
paulo@0 126 return FALSE;
paulo@0 127
paulo@0 128 string_appendf (s, "sip=%s", net_ip_str (gt->server_ip));
paulo@0 129 return TRUE;
paulo@0 130 }
paulo@0 131
paulo@0 132 static URL_OPT_SERIALIZE(server_port)
paulo@0 133 {
paulo@0 134 if (gt->server_port == 6346)
paulo@0 135 return FALSE;
paulo@0 136
paulo@0 137 string_appendf (s, "sport=%hu", gt->server_port);
paulo@0 138 return TRUE;
paulo@0 139 }
paulo@0 140
paulo@0 141 static URL_OPT_SERIALIZE(fw)
paulo@0 142 {
paulo@0 143 if (!gt->firewalled)
paulo@0 144 return FALSE;
paulo@0 145
paulo@0 146 string_append (s, "fw=1");
paulo@0 147 return TRUE;
paulo@0 148 }
paulo@0 149
paulo@0 150 static URL_OPT_SERIALIZE(index)
paulo@0 151 {
paulo@0 152 if (!gt->index)
paulo@0 153 return FALSE;
paulo@0 154
paulo@0 155 string_appendf (s, "index=%u", gt->index);
paulo@0 156 return TRUE;
paulo@0 157 }
paulo@0 158
paulo@0 159 /*****************************************************************************/
paulo@0 160
paulo@0 161 /*
paulo@0 162 * These functions return TRUE if they were successful.
paulo@0 163 */
paulo@0 164
paulo@0 165 static URL_OPT_UNSERIALIZE(ip)
paulo@0 166 {
paulo@0 167 in_addr_t ip;
paulo@0 168
paulo@0 169 ip = net_ip (value);
paulo@0 170
paulo@0 171 if (ip == 0 || ip == INADDR_NONE)
paulo@0 172 return FALSE;
paulo@0 173
paulo@0 174 gt->user_ip = ip;
paulo@0 175 return TRUE;
paulo@0 176 }
paulo@0 177
paulo@0 178 static URL_OPT_UNSERIALIZE(port)
paulo@0 179 {
paulo@0 180 unsigned long port;
paulo@0 181
paulo@0 182 port = gift_strtoul (value);
paulo@0 183
paulo@0 184 if (port == ULONG_MAX || port >= 65536)
paulo@0 185 return FALSE;
paulo@0 186
paulo@0 187 gt->user_port = port;
paulo@0 188 return TRUE;
paulo@0 189 }
paulo@0 190
paulo@0 191 static URL_OPT_UNSERIALIZE(name)
paulo@0 192 {
paulo@0 193 char *name;
paulo@0 194
paulo@0 195 if (!(name = STRDUP (value)))
paulo@0 196 return FALSE;
paulo@0 197
paulo@0 198 gt->filename = name;
paulo@0 199 return TRUE;
paulo@0 200 }
paulo@0 201
paulo@0 202 static URL_OPT_UNSERIALIZE(guid)
paulo@0 203 {
paulo@0 204 gt_guid_t *guid;
paulo@0 205
paulo@0 206 if (!(guid = gt_guid_bin (value)))
paulo@0 207 return FALSE;
paulo@0 208
paulo@0 209 free (gt->guid);
paulo@0 210 gt->guid = guid;
paulo@0 211
paulo@0 212 return TRUE;
paulo@0 213 }
paulo@0 214
paulo@0 215 static URL_OPT_UNSERIALIZE(server_ip)
paulo@0 216 {
paulo@0 217 in_addr_t ip;
paulo@0 218
paulo@0 219 ip = net_ip (value);
paulo@0 220
paulo@0 221 if (ip == 0 || ip == INADDR_NONE)
paulo@0 222 return FALSE;
paulo@0 223
paulo@0 224 gt->server_ip = ip;
paulo@0 225 return TRUE;
paulo@0 226 }
paulo@0 227
paulo@0 228 static URL_OPT_UNSERIALIZE(server_port)
paulo@0 229 {
paulo@0 230 unsigned long port;
paulo@0 231
paulo@0 232 port = gift_strtoul (value);
paulo@0 233
paulo@0 234 if (port == ULONG_MAX || port >= 65536)
paulo@0 235 return FALSE;
paulo@0 236
paulo@0 237 gt->server_port = port;
paulo@0 238 return TRUE;
paulo@0 239 }
paulo@0 240
paulo@0 241 static URL_OPT_UNSERIALIZE(fw)
paulo@0 242 {
paulo@0 243 unsigned long fw;
paulo@0 244
paulo@0 245 fw = gift_strtoul (value);
paulo@0 246
paulo@0 247 if (fw != 0 && fw != 1)
paulo@0 248 return FALSE;
paulo@0 249
paulo@0 250 if (fw)
paulo@0 251 gt->firewalled = TRUE;
paulo@0 252 else
paulo@0 253 gt->firewalled = FALSE;
paulo@0 254
paulo@0 255 return TRUE;
paulo@0 256 }
paulo@0 257
paulo@0 258 static URL_OPT_UNSERIALIZE(index)
paulo@0 259 {
paulo@0 260 unsigned long index;
paulo@0 261
paulo@0 262 index = gift_strtoul (value);
paulo@0 263
paulo@0 264 if (index == ULONG_MAX)
paulo@0 265 return FALSE;
paulo@0 266
paulo@0 267 gt->index = (uint32_t)index;
paulo@0 268 return TRUE;
paulo@0 269 }
paulo@0 270
paulo@0 271 /*****************************************************************************/
paulo@0 272
paulo@0 273 /*
paulo@0 274 * Old Gnutella URL format:
paulo@0 275 *
paulo@0 276 * Gnutella://<u-ip>:<u-port>@<s-ip>:<s-port>[[FW]]:<client-guid>/<index>/<name>
paulo@0 277 *
paulo@0 278 * server_port is the server's gnutella port. This should probably pass
paulo@0 279 * back both the gnutella port instead and the peer's connecting port, to
paulo@0 280 * help in disambiguating different users behind the same firewall.
paulo@0 281 */
paulo@0 282 static BOOL parse_old_url (char *url,
paulo@0 283 uint32_t *r_user_ip, uint16_t *r_user_port,
paulo@0 284 uint32_t *r_server_ip, uint16_t *r_server_port,
paulo@0 285 BOOL *firewalled, char **r_pushid,
paulo@0 286 uint32_t *r_index, char **r_fname)
paulo@0 287 {
paulo@0 288 char *port_and_flags;
paulo@0 289 char *flag;
paulo@0 290
paulo@0 291 string_sep (&url, "://");
paulo@0 292
paulo@0 293 /* TODO: check for more errors */
paulo@0 294
paulo@0 295 *r_user_ip = net_ip (string_sep (&url, ":"));
paulo@0 296 *r_user_port = gift_strtoul (string_sep (&url, "@"));
paulo@0 297 *r_server_ip = net_ip (string_sep (&url, ":"));
paulo@0 298
paulo@0 299 /* handle bracketed flags after port. ugh, this is so ugly */
paulo@0 300 port_and_flags = string_sep (&url, ":");
paulo@0 301 *r_server_port = gift_strtoul (string_sep (&port_and_flags, "["));
paulo@0 302
paulo@0 303 if (!string_isempty (port_and_flags))
paulo@0 304 {
paulo@0 305 /* grab any flags inside the brackets */
paulo@0 306 while ((flag = string_sep_set (&port_and_flags, ",]")))
paulo@0 307 {
paulo@0 308 if (!STRCMP (flag, "FW"))
paulo@0 309 *firewalled = TRUE;
paulo@0 310 }
paulo@0 311 }
paulo@0 312
paulo@0 313 *r_pushid = string_sep (&url, "/");
paulo@0 314 *r_index = gift_strtoul (string_sep (&url, "/"));
paulo@0 315 *r_fname = url;
paulo@0 316
paulo@0 317 return TRUE;
paulo@0 318 }
paulo@0 319
paulo@0 320 static struct url_option *lookup_url_option (const char *key)
paulo@0 321 {
paulo@0 322 struct url_option *url_opt;
paulo@0 323
paulo@0 324 url_opt = &gt_source_url_options[0];
paulo@0 325
paulo@0 326 while (url_opt->key != NULL)
paulo@0 327 {
paulo@0 328 if (strcmp (url_opt->key, key) == 0)
paulo@0 329 return url_opt;
paulo@0 330
paulo@0 331 url_opt++;
paulo@0 332 }
paulo@0 333
paulo@0 334 return NULL;
paulo@0 335 }
paulo@0 336
paulo@0 337 /*
paulo@0 338 * New parameter-based URL format:
paulo@0 339 *
paulo@0 340 * Gnutella:?ip=<u-ip>&port=<u-port>&sip=<s-ip>&sport=<s-port>[&fw=<FW>]...
paulo@0 341 *
paulo@0 342 * Parameters we don't understand are placed in gt_src->extra Dataset, so we
paulo@0 343 * should be forwards and backwards compatible when adding new parameters.
paulo@0 344 */
paulo@0 345 static BOOL parse_new_url (char *url, GtSource *gt)
paulo@0 346 {
paulo@0 347 char *option;
paulo@0 348 char *key;
paulo@0 349 char *value;
paulo@0 350
paulo@0 351 /* skip prefix */
paulo@0 352 string_sep (&url, ":?");
paulo@0 353
paulo@0 354 while ((option = string_sep (&url, "&")))
paulo@0 355 {
paulo@0 356 struct url_option *url_opt;
paulo@0 357
paulo@0 358 value = option;
paulo@0 359 key = string_sep (&value, "=");
paulo@0 360
paulo@0 361 if (string_isempty (key) || string_isempty (value))
paulo@0 362 continue;
paulo@0 363
paulo@0 364 /* look up the key in our list of possible options */
paulo@0 365 if ((url_opt = lookup_url_option (key)))
paulo@0 366 {
paulo@0 367 /* unserialize the specified key */
paulo@0 368 if (url_opt->unserialize (gt, key, value))
paulo@0 369 continue;
paulo@0 370
paulo@0 371 /* fail through on failure to store failed keys */
paulo@0 372 }
paulo@0 373
paulo@0 374 /* store the unfound keys in the extra parameter dataset */
paulo@0 375 dataset_insertstr (&gt->extra, key, value);
paulo@0 376 }
paulo@0 377
paulo@0 378 return TRUE;
paulo@0 379 }
paulo@0 380
paulo@0 381 /*****************************************************************************/
paulo@0 382
paulo@0 383 static GtSource *handle_old_url (char *url)
paulo@0 384 {
paulo@0 385 GtSource *gt;
paulo@0 386 char *fname = NULL;
paulo@0 387 char *guid_ascii = NULL;
paulo@0 388
paulo@0 389 if (!(gt = gt_source_new ()))
paulo@0 390 return NULL;
paulo@0 391
paulo@0 392 if (!parse_old_url (url, &gt->user_ip, &gt->user_port,
paulo@0 393 &gt->server_ip, &gt->server_port,
paulo@0 394 &gt->firewalled, &guid_ascii, &gt->index, &fname))
paulo@0 395 {
paulo@0 396 gt_source_free (gt);
paulo@0 397 return NULL;
paulo@0 398 }
paulo@0 399
paulo@0 400 gt->filename = NULL;
paulo@0 401 if (!string_isempty (fname))
paulo@0 402 gt->filename = STRDUP (fname);
paulo@0 403
paulo@0 404 gt->guid = NULL;
paulo@0 405 if (!string_isempty (guid_ascii))
paulo@0 406 gt->guid = gt_guid_bin (guid_ascii);
paulo@0 407
paulo@0 408 return gt;
paulo@0 409 }
paulo@0 410
paulo@0 411 static GtSource *handle_new_url (char *url)
paulo@0 412 {
paulo@0 413 GtSource *gt;
paulo@0 414
paulo@0 415 if (!(gt = gt_source_new ()))
paulo@0 416 return NULL;
paulo@0 417
paulo@0 418 if (!parse_new_url (url, gt))
paulo@0 419 {
paulo@0 420 gt_source_free (gt);
paulo@0 421 return NULL;
paulo@0 422 }
paulo@0 423
paulo@0 424 return gt;
paulo@0 425 }
paulo@0 426
paulo@0 427 GtSource *gt_source_unserialize (const char *url)
paulo@0 428 {
paulo@0 429 char *t_url;
paulo@0 430 GtSource *src = NULL;
paulo@0 431
paulo@0 432 if (!url)
paulo@0 433 return NULL;
paulo@0 434
paulo@0 435 if (!(t_url = STRDUP (url)))
paulo@0 436 return NULL;
paulo@0 437
paulo@0 438 /*
paulo@0 439 * Determine whether this is the new format URL (beginning with
paulo@0 440 * "Gnutella:?") or the old-style (starts with "Gnutella://")
paulo@0 441 */
paulo@0 442 if (strncmp (t_url, "Gnutella://", sizeof ("Gnutella://") - 1) == 0)
paulo@0 443 {
paulo@0 444 src = handle_old_url (t_url);
paulo@0 445 }
paulo@0 446 else if (strncmp (t_url, "Gnutella:?", sizeof ("Gnutella:?") - 1) == 0)
paulo@0 447 {
paulo@0 448 src = handle_new_url (t_url);
paulo@0 449 }
paulo@0 450 else
paulo@0 451 {
paulo@0 452 /* do nothing */
paulo@0 453 }
paulo@0 454
paulo@0 455 FREE (t_url);
paulo@0 456
paulo@0 457 return src;
paulo@0 458 }
paulo@0 459
paulo@0 460 /* use the old format serialization for now */
paulo@0 461 #if 0
paulo@0 462 static void unknown_opt (ds_data_t *key, ds_data_t *value, void *udata)
paulo@0 463 {
paulo@0 464 String *str = udata;
paulo@0 465 string_appendf (str, "%s=%s&", key->data, value->data);
paulo@0 466 }
paulo@0 467
paulo@0 468 char *gt_source_serialize (GtSource *gt)
paulo@0 469 {
paulo@0 470 struct url_option *opt;
paulo@0 471 char *url;
paulo@0 472 size_t len;
paulo@0 473 String str;
paulo@0 474
paulo@0 475 string_init (&str);
paulo@0 476 string_appendf (&str, "%s:?", GT->name);
paulo@0 477
paulo@0 478 for (opt = gt_source_url_options; opt->key != NULL; opt++)
paulo@0 479 {
paulo@0 480 if (opt->serialize (gt, &str))
paulo@0 481 {
paulo@0 482 /* append separator for next argument */
paulo@0 483 string_appendc (&str, '&');
paulo@0 484 }
paulo@0 485 }
paulo@0 486
paulo@0 487 /* copy unknown options to the URL */
paulo@0 488 dataset_foreach (gt->extra, unknown_opt, &str);
paulo@0 489
paulo@0 490 len = str.len;
paulo@0 491 assert (len > 0);
paulo@0 492
paulo@0 493 url = string_finish_keep (&str);
paulo@0 494
paulo@0 495 /* remove trailing separator (may not be there if source is empty) */
paulo@0 496 if (url[len - 1] == '&')
paulo@0 497 url[len - 1] = 0;
paulo@0 498
paulo@0 499 return url;
paulo@0 500 }
paulo@0 501 #endif
paulo@0 502
paulo@0 503 /* serialize to the old format for now */
paulo@0 504 char *gt_source_serialize (GtSource *gt)
paulo@0 505 {
paulo@0 506 String *str;
paulo@0 507
paulo@0 508 if (!(str = string_new (NULL, 0, 0, TRUE)))
paulo@0 509 return FALSE;
paulo@0 510
paulo@0 511 string_appendf (str, "Gnutella://%s:%hu", net_ip_str (gt->user_ip),
paulo@0 512 gt->user_port);
paulo@0 513
paulo@0 514 string_appendf (str, "@%s:%hu", net_ip_str (gt->server_ip),
paulo@0 515 gt->server_port);
paulo@0 516
paulo@0 517 string_appendc (str, '[');
paulo@0 518
paulo@0 519 if (gt->firewalled)
paulo@0 520 string_append (str, "FW");
paulo@0 521
paulo@0 522 string_appendc (str, ']');
paulo@0 523
paulo@0 524 string_appendf (str, ":%s/%lu",
paulo@0 525 STRING_NOTNULL (gt_guid_str (gt->guid)), (long)gt->index);
paulo@0 526 string_appendf (str, "/%s",
paulo@0 527 STRING_NOTNULL (gt->filename)); /* already encoded */
paulo@0 528
paulo@0 529 return string_free_keep (str);
paulo@0 530 }
paulo@0 531
paulo@0 532 /*****************************************************************************/
paulo@0 533
paulo@0 534 /*
paulo@0 535 * This is called by the search result code in order to produce
paulo@0 536 * source URLs.
paulo@0 537 *
paulo@0 538 * TODO: This is just wrong -- interface is not very extensible. The search
paulo@0 539 * result code should probably use GtSource and call gt_source_serialize().
paulo@0 540 */
paulo@0 541 char *gt_source_url_new (const char *filename, uint32_t index,
paulo@0 542 in_addr_t user_ip, uint16_t user_port,
paulo@0 543 in_addr_t server_ip, uint16_t server_port,
paulo@0 544 BOOL firewalled, const gt_guid_t *client_id)
paulo@0 545 {
paulo@0 546 GtSource *src;
paulo@0 547 char *url;
paulo@0 548
paulo@0 549 if (!(src = gt_source_new ()))
paulo@0 550 return NULL;
paulo@0 551
paulo@0 552 gt_source_set_ip (src, user_ip);
paulo@0 553 gt_source_set_port (src, user_port);
paulo@0 554 gt_source_set_index (src, index);
paulo@0 555 gt_source_set_server_ip (src, server_ip);
paulo@0 556 gt_source_set_server_port (src, server_port);
paulo@0 557 gt_source_set_firewalled (src, firewalled);
paulo@0 558
paulo@0 559 if (!gt_source_set_guid (src, client_id) ||
paulo@0 560 !gt_source_set_filename (src, filename))
paulo@0 561 {
paulo@0 562 gt_source_free (src);
paulo@0 563 return NULL;
paulo@0 564 }
paulo@0 565
paulo@0 566 url = gt_source_serialize (src);
paulo@0 567 gt_source_free (src);
paulo@0 568
paulo@0 569 return url;
paulo@0 570 }
paulo@0 571
paulo@0 572 /*****************************************************************************/
paulo@0 573
paulo@0 574 GtSource *gt_source_new (void)
paulo@0 575 {
paulo@0 576 GtSource *src;
paulo@0 577
paulo@0 578 if (!(src = NEW (GtSource)))
paulo@0 579 return NULL;
paulo@0 580
paulo@0 581 /* special case: port is 6346 if not specified */
paulo@0 582 src->user_port = 6346;
paulo@0 583 src->server_port = 6346;
paulo@0 584
paulo@0 585 return src;
paulo@0 586 }
paulo@0 587
paulo@0 588 void gt_source_free (GtSource *gt)
paulo@0 589 {
paulo@0 590 if (!gt)
paulo@0 591 return;
paulo@0 592
paulo@0 593 free (gt->guid);
paulo@0 594 free (gt->filename);
paulo@0 595 free (gt->status_txt);
paulo@0 596
paulo@0 597 FREE (gt);
paulo@0 598 }
paulo@0 599
paulo@0 600 /*****************************************************************************/
paulo@0 601
paulo@0 602 void gt_source_set_ip (GtSource *src, in_addr_t ip)
paulo@0 603 {
paulo@0 604 src->user_ip = ip;
paulo@0 605 }
paulo@0 606
paulo@0 607 void gt_source_set_port (GtSource *src, in_port_t port)
paulo@0 608 {
paulo@0 609 src->user_port = port;
paulo@0 610 }
paulo@0 611
paulo@0 612 void gt_source_set_index (GtSource *src, uint32_t index)
paulo@0 613 {
paulo@0 614 src->index = index;
paulo@0 615 }
paulo@0 616
paulo@0 617 void gt_source_set_server_ip (GtSource *src, in_addr_t server_ip)
paulo@0 618 {
paulo@0 619 src->server_ip = server_ip;
paulo@0 620 }
paulo@0 621
paulo@0 622 void gt_source_set_server_port (GtSource *src, in_port_t server_port)
paulo@0 623 {
paulo@0 624 src->server_port = server_port;
paulo@0 625 }
paulo@0 626
paulo@0 627 void gt_source_set_firewalled (GtSource *src, BOOL fw)
paulo@0 628 {
paulo@0 629 src->firewalled = fw;
paulo@0 630 }
paulo@0 631
paulo@0 632 BOOL gt_source_set_filename (GtSource *src, const char *filename)
paulo@0 633 {
paulo@0 634 char *encoded;
paulo@0 635
paulo@0 636 /* special case for no filename */
paulo@0 637 if (!filename)
paulo@0 638 {
paulo@0 639 free (src->filename);
paulo@0 640 src->filename = NULL;
paulo@0 641 return TRUE;
paulo@0 642 }
paulo@0 643
paulo@0 644 if (!(encoded = gt_url_encode (filename)))
paulo@0 645 return FALSE;
paulo@0 646
paulo@0 647 src->filename = encoded;
paulo@0 648 return TRUE;
paulo@0 649 }
paulo@0 650
paulo@0 651 BOOL gt_source_set_guid (GtSource *src, const gt_guid_t *guid)
paulo@0 652 {
paulo@0 653 gt_guid_t *dup;
paulo@0 654
paulo@0 655 if (!(dup = gt_guid_dup (guid)))
paulo@0 656 return FALSE;
paulo@0 657
paulo@0 658 src->guid = dup;
paulo@0 659 return TRUE;
paulo@0 660 }
paulo@0 661
paulo@0 662 /*****************************************************************************/
paulo@0 663
paulo@0 664 int gnutella_source_cmp (Protocol *p, Source *a, Source *b)
paulo@0 665 {
paulo@0 666 GtSource *gt_a = NULL;
paulo@0 667 GtSource *gt_b = NULL;
paulo@0 668 int ret = 0;
paulo@0 669
paulo@0 670 if (!(gt_a = gt_source_unserialize (a->url)) ||
paulo@0 671 !(gt_b = gt_source_unserialize (b->url)))
paulo@0 672 {
paulo@0 673 gt_source_free (gt_a);
paulo@0 674 gt_source_free (gt_b);
paulo@0 675 return -1;
paulo@0 676 }
paulo@0 677
paulo@0 678 if (gt_a->user_ip > gt_b->user_ip)
paulo@0 679 ret = 1;
paulo@0 680 else if (gt_a->user_ip < gt_b->user_ip)
paulo@0 681 ret = -1;
paulo@0 682
paulo@0 683 /*
paulo@0 684 * Having two sources with the same IP on the same transfer can trigger a
paulo@0 685 * bug in most versions of giftd. At least as of giftd < 0.11.9, this
paulo@0 686 * causes sources to be reactivated after download completion due to a bug
paulo@0 687 * in handle_next_queued called from download_complete. To avoid that, we
paulo@0 688 * pretend that multiple sources with the same hash from the same user_ip
paulo@0 689 * are the same here, by ignoring the port. If the sources compare
paulo@0 690 * equally, then one will replace the other when added, and there won't be
paulo@0 691 * any dead sources with the same IP available to reactivate when the
paulo@0 692 * download completes.
paulo@0 693 *
paulo@0 694 * It's ok for the client guid to be different, even if the IPs are the
paulo@0 695 * same, since in that case the guid gets reflected in the user string, so
paulo@0 696 * the bug in handle_next_queue() won't trigger.
paulo@0 697 *
paulo@0 698 * Also, Transfers that don't have hashes are ok since they can only ever
paulo@0 699 * have one user. So, if either source doesn't have a hash the bug won't
paulo@0 700 * trigger.
paulo@0 701 */
paulo@0 702 #if 0
paulo@0 703 if (gt_a->user_port > gt_b->user_port)
paulo@0 704 ret = 1;
paulo@0 705 else if (gt_a->user_port < gt_b->user_port)
paulo@0 706 ret = -1;
paulo@0 707 #endif
paulo@0 708
paulo@0 709 /* if both IPs are private match by the guid */
paulo@0 710 if (gt_is_local_ip (gt_a->user_ip, gt_a->server_ip) &&
paulo@0 711 gt_is_local_ip (gt_b->user_ip, gt_b->server_ip))
paulo@0 712 {
paulo@0 713 ret = gt_guid_cmp (gt_a->guid, gt_b->guid);
paulo@0 714 }
paulo@0 715
paulo@0 716 if (ret == 0)
paulo@0 717 {
paulo@0 718 /* if the hashes match consider them equal */
paulo@0 719 if (a->hash || b->hash)
paulo@0 720 ret = gift_strcmp (a->hash, b->hash);
paulo@0 721 else
paulo@0 722 ret = gift_strcmp (gt_a->filename, gt_b->filename);
paulo@0 723 }
paulo@0 724
paulo@0 725 gt_source_free (gt_a);
paulo@0 726 gt_source_free (gt_b);
paulo@0 727
paulo@0 728 return ret;
paulo@0 729 }
paulo@0 730
paulo@0 731 int gnutella_source_add (Protocol *p, Transfer *transfer, Source *source)
paulo@0 732 {
paulo@0 733 GtSource *src;
paulo@0 734
paulo@0 735 assert (source->udata == NULL);
paulo@0 736
paulo@0 737 if (!(src = gt_source_unserialize (source->url)))
paulo@0 738 return FALSE;
paulo@0 739
paulo@0 740 source->udata = src;
paulo@0 741
paulo@0 742 /* track this download */
paulo@0 743 gt_download_add (transfer, source);
paulo@0 744
paulo@0 745 return TRUE;
paulo@0 746 }
paulo@0 747
paulo@0 748 void gnutella_source_remove (Protocol *p, Transfer *transfer, Source *source)
paulo@0 749 {
paulo@0 750 gt_download_remove (transfer, source);
paulo@0 751
paulo@0 752 assert (source->udata != NULL);
paulo@0 753 gt_source_free (source->udata);
paulo@0 754 }