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