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 = >_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 (>->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, >->user_ip, >->user_port,
|
paulo@0
|
393 >->server_ip, >->server_port,
|
paulo@0
|
394 >->firewalled, &guid_ascii, >->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 }
|