1 (* Copyright 2001, 2002 b52_simon :), b8_bavard, b8_fee_carabine, INRIA *)
3 This file is part of mldonkey.
5 mldonkey is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 mldonkey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with mldonkey; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 open CommonInteractive
43 let send_client c m
= send_client c
.client_sock m
45 let as_ft file
= as_file file
.ft_file
46 let ft_num file
= file_num
(as_ft file
)
47 let ft_size file
= file
.ft_file
.impl_file_size
48 let ft_state file
= file_state
(as_ft file
)
50 let as_file file
= as_file file
.file_file
51 let file_size file
= file
.file_file
.impl_file_size
52 let file_downloaded file
= file_downloaded (as_file file
)
53 let file_age file
= file
.file_file
.impl_file_age
54 let file_fd file
= file_fd (as_file file
)
55 let file_disk_name file
= file_disk_name (as_file file
)
56 let file_state file
= file_state (as_file file
)
57 let file_num file
= file_num (as_file file
)
58 let file_must_update file
= file_must_update (as_file file
)
61 let set_file_state file state
=
62 CommonFile.set_file_state (as_file file
) state
64 let as_client c
= as_client c
.client_client
65 let client_type c
= client_type (as_client c
)
67 let set_client_state client state
=
68 CommonClient.set_client_state (as_client client
) state
70 let set_client_disconnected client
=
71 CommonClient.set_client_disconnected (as_client client
)
73 let client_num c
= client_num (as_client c
)
76 let network = new_network
"BT" "BitTorrent"
83 let connection_manager = network.network_connection_manager
85 let (shared_ops
: file
CommonShared.shared_ops
) =
86 CommonShared.new_shared_ops
network
88 let (server_ops
: server
CommonServer.server_ops
) =
89 CommonServer.new_server_ops
network
91 let (room_ops
: server
CommonRoom.room_ops
) =
92 CommonRoom.new_room_ops
network
94 let (user_ops
: user
CommonUser.user_ops
) =
95 CommonUser.new_user_ops
network
97 let (file_ops
: file
CommonFile.file_ops
) =
98 CommonFile.new_file_ops
network
100 let (ft_ops
: ft
CommonFile.file_ops
) =
101 CommonFile.new_file_ops
network
103 let (client_ops
: client
CommonClient.client_ops
) =
104 CommonClient.new_client_ops
network
106 let must_share_file file codedname has_old_impl
=
107 match file
.file_shared
with
112 impl_shared_update
= 1;
113 impl_shared_fullname
= file_disk_name file
;
114 impl_shared_codedname
= codedname
;
115 impl_shared_size
= file_size file
;
116 impl_shared_id
= Md4.null
;
118 impl_shared_uploaded
= Int64.zero
;
119 impl_shared_ops
= shared_ops
;
120 impl_shared_val
= file
;
121 impl_shared_requests
= 0;
122 impl_shared_file
= Some
(as_file file
);
123 impl_shared_servers
= [];
125 file
.file_shared
<- Some
impl;
126 incr
CommonGlobals.nshared_files
;
127 CommonShared.shared_calculate_total_bytes
();
128 match has_old_impl
with
129 None
-> update_shared_num
impl
130 | Some old_impl
-> replace_shared old_impl
impl
133 let must_share_file file
= must_share_file file
(file_best_name
(as_file file
)) None
135 let unshare_file file
=
136 match file
.file_shared
with
140 file
.file_shared
<- None
;
141 decr
CommonGlobals.nshared_files
;
142 CommonShared.shared_calculate_total_bytes
()
145 module DO
= CommonOptions
147 let current_files = ref ([] : BTTypes.file list
)
149 let listen_sock = ref (None
: TcpServerSocket.t
option)
151 let bt_dht = ref (None
: BT_DHT.M.t
option)
153 let files_by_uid = Hashtbl.create
13
155 let max_range_len = Int64.of_int
(1 lsl 14)
156 let max_request_len = Int64.of_int
(1 lsl 16)
158 let bt_download_counter = ref Int64.zero
159 let bt_upload_counter = ref Int64.zero
161 let log_prefix = "[BT]"
163 let lprintf_nl ?exn fmt
=
164 lprintf_nl2 ?exn
log_prefix fmt
167 lprintf2
log_prefix fmt
170 let check_if_interesting file c
=
172 if not c
.client_alrd_sent_notinterested
then
173 let up = match c
.client_uploader
with
177 let swarmer = CommonSwarming.uploader_swarmer
up in
179 (* The client has nothing to propose to us *)
180 (not
(CommonSwarming.is_interesting
up )) &&
181 (* All the requested ranges are useless *)
182 (List.filter
(fun (_
,_
,r
) ->
183 let x,y
= CommonSwarming.range_range r
in
184 x < y
) c
.client_ranges_sent
= []) &&
185 (match c
.client_range_waiting
with
188 let x,y
= CommonSwarming.range_range r
in
190 (* The current blocks are also useless *)
191 (match c
.client_chunk
with
193 | Some
(chunk
, blocks
) ->
194 List.for_all
(fun b
->
195 let chunk_num = CommonSwarming.block_chunk_num
swarmer b
.up_block
in
196 let bitmap = CommonSwarming.chunks_verified_bitmap
swarmer in
197 VB.get
bitmap chunk_num <> VB.State_verified
) blocks
)
201 c
.client_interesting
<- false;
202 c
.client_alrd_sent_notinterested
<- true;
203 send_client c NotInterested
206 let add_torrent_infos file trackers
=
207 file
.file_trackers
<- trackers
@ file
.file_trackers
209 let create_temp_file file_temp file_files
file_state =
210 if !verbose
then lprintf_nl "create_temp_file %s - %s" file_temp
(string_of_state
file_state);
212 if file_state = FileShared
then
218 if file_files
<> [] then
219 Unix32.create_multifile file_temp
writable file_files
221 Unix32.create_diskfile file_temp
writable
223 if Unix32.destroyed
file_fd then
226 "create_temp_file: Unix32.create returned a destroyed FD for %s\n"
230 let make_tracker_url url
=
231 if String2.check_prefix
(String.lowercase url
) "http://" then
232 `Http url
(* do not change the case of the url *)
234 try Scanf.sscanf
(String.lowercase url
) "udp://%s@:%d" (fun host port
-> `Udp
(host
,port
))
237 (** invariant: [make_tracker_url (show_tracker_url url) = url] *)
238 let show_tracker_url : tracker_url
-> string = function
239 | `Http url
| `Other url
-> url
240 | `Udp
(host
,port
) -> Printf.sprintf
"udp://%s:%d" host port
242 let can_handle_tracker = function
247 let set_trackers file file_trackers
=
248 List.iter
(fun url
->
249 let url = make_tracker_url url in
250 if not
(List.exists
(fun tracker
-> tracker
.tracker_url
= url) file
.file_trackers
) then
253 tracker_interval
= 600;
254 tracker_min_interval
= 600;
255 tracker_last_conn
= 0;
256 tracker_last_clients_num
= 0;
257 tracker_torrent_downloaded
= 0;
258 tracker_torrent_complete
= 0;
259 tracker_torrent_incomplete
= 0;
260 tracker_torrent_total_clients_count
= 0;
261 tracker_torrent_last_dl_req
= 0;
264 tracker_status
= if can_handle_tracker url then Enabled
265 else Disabled_mld
(intern
"Tracker type not supported")
267 file
.file_trackers
<- t :: file
.file_trackers
)
270 let new_file ?
(metadata
=false) file_id
t torrent_diskname file_temp
file_state user group
=
272 Hashtbl.find
files_by_uid file_id
274 let file_fd = create_temp_file file_temp
t.torrent_files
file_state in
276 file_tracker_connected
= false;
277 file_file
= file_impl
;
278 file_piece_size
= t.torrent_piece_size
;
280 file_name
= t.torrent_name
;
281 file_comment
= t.torrent_comment
;
282 file_created_by
= t.torrent_created_by
;
283 file_creation_date
= t.torrent_creation_date
;
284 file_modified_by
= t.torrent_modified_by
;
285 file_encoding
= t.torrent_encoding
;
286 file_clients_num
= 0;
287 file_clients
= Hashtbl.create
113;
290 file_chunks
= t.torrent_pieces
;
291 file_files
= (List.map
(fun (file,size
) -> (file,size
,None
)) t.torrent_files
);
292 file_blocks_downloaded
= [];
293 file_uploaded
= Int64.zero
;
294 file_torrent_diskname
= torrent_diskname
;
295 file_completed_hook
= (fun _
-> ());
297 file_session_uploaded
= Int64.zero
;
298 file_session_downloaded
= Int64.zero
;
299 file_last_dht_announce
= 0;
300 file_metadata_size
= 0L;
301 file_metadata_piece
= 0L;
302 file_metadata_downloading
= metadata
;
303 file_metadata_chunks
= Array.make
20 "";
304 file_private
= t.torrent_private
;
306 (dummy_file_impl
()) with
307 impl_file_owner
= user
;
308 impl_file_group
= group
;
309 impl_file_fd
= Some
file_fd;
310 impl_file_size
= t.torrent_length
;
311 impl_file_downloaded
= Int64.zero
;
312 impl_file_val
= file;
313 impl_file_ops
= file_ops
;
314 impl_file_age
= last_time
();
315 impl_file_best_name
= t.torrent_name
;
318 if t.torrent_announce_list
<> [] then
319 set_trackers file t.torrent_announce_list
321 set_trackers file [t.torrent_announce
];
322 if file_state <> FileShared
then begin
323 let kernel = CommonSwarming.create_swarmer file_temp
(file_size file) in
324 let swarmer = CommonSwarming.create
kernel (as_file file)
325 file.file_piece_size
in
326 file.file_swarmer
<- Some
swarmer;
327 CommonSwarming.set_verified
swarmer (fun _ num
->
328 file.file_blocks_downloaded
<- (num
) ::
329 file.file_blocks_downloaded
;
330 file_must_update file;
331 (*Automatically send Have to ALL clients once a piece is verified
332 NB : will probably have to check if client can be interested*)
333 Hashtbl.iter
(fun _ c
->
335 if c
.client_registered_bitfield
then
337 match c
.client_bitmap
with
340 if not
(Bitv.get
bitmap num
) then
341 send_client c
(Have
(Int64.of_int num
));
342 check_if_interesting file c
347 CommonSwarming.set_verifier
swarmer (Verification
348 (Array.map
(fun sha1
-> Sha1 sha1
) file.file_chunks
));
350 current_files := file :: !current_files;
351 Hashtbl.add
files_by_uid file_id
file;
352 file_add file_impl
file_state;
353 must_share_file file;
356 let new_download ?
(metadata
=false) file_id
t torrent_diskname user
=
357 let file_temp = Filename.concat
!!DO.temp_directory
358 (Printf.sprintf
"BT-%s" (Sha1.to_string file_id
)) in
359 new_file ~metadata file_id
t torrent_diskname
file_temp FileDownloading user
361 let ft_by_num = Hashtbl.create
13
362 let ft_counter = ref 0
364 let new_ft file_name user
=
369 ft_filename
= file_name
;
370 ft_retry
= (fun _
-> ());
372 (dummy_file_impl
()) with
373 impl_file_owner
= user
;
374 impl_file_group
= user
.user_default_group
;
376 impl_file_size
= zero
;
377 impl_file_downloaded
= Int64.zero
;
379 impl_file_ops
= ft_ops
;
380 impl_file_age
= last_time
();
381 impl_file_best_name
= file_name
;
384 Hashtbl.add
ft_by_num !ft_counter ft;
385 file_add file_impl FileDownloading
;
388 let _dot_string s h
=
389 let len = String.length s
in
391 let ic = int_of_char c
in
392 if ic >= 65 && ic <= 70 then
393 string_of_int
(ic - 55)
395 if ic >= 97 && ic <= 102 then
396 string_of_int
(ic - 87)
398 Printf.sprintf
"%c" c
402 if i
< len then begin
403 if h
then Buffer.add_string b
(char2hex s
.[i
])
404 else Buffer.add_char b s
.[i
];
405 if i
< len-1 then Buffer.add_char b '
.'
;
409 Buffer.contents
(iter 0 (Buffer.create
(len*2)))
417 let dot_string_of_list s l
=
418 let buf = Buffer.create
(List.length l
) in
419 List.iter (fun i
-> Buffer.add_char
buf s
.[i
]) l
;
420 dot_string (Buffer.contents
buf)
422 let dot_string_of_string s
=
423 let buf = Buffer.create
20 in
424 let found_non_int = ref false in
425 String.iter (fun s
->
428 if !found_non_int then Buffer.add_char
buf '
.'
;
429 found_non_int := false;
430 Buffer.add_char
buf s
431 | _
-> found_non_int := true
435 (* check string s for char c (dec) at position l (list) *)
436 let check_all s c l
=
437 let ch = char_of_int c
in
438 List.for_all
(fun i
-> s
.[i
] = ch) l
442 ignore
(int_of_string
(String.sub s p
1));
446 let strip_leading_zeroes s
=
447 let l = String.length s
in
450 else if s
.[i
] <> '
0'
then String.sub s i
(l - i
)
454 (* from azureus/gpl *)
455 let decode_az_style s
=
456 if check_all s
45 [0;7] then begin
457 let s_id = (String.sub s
1 2) in
460 | "AR" -> Brand_arctic
461 | "AZ" -> Brand_azureus
462 | "BB" -> Brand_bitbuddy
463 | "BC" -> Brand_bitcomet
464 | "BR" -> Brand_bitrocket
465 | "BS" -> Brand_btslave
466 | "BX" -> Brand_bittorrentx
467 | "CT" (* ctorrent *)
468 | "CD" -> Brand_ctorrent
469 | "lt" (* libtorrent *)
470 | "LT" -> Brand_libtorrent
471 | "MT" -> Brand_moonlighttorrent
472 | "SB" -> Brand_swiftbit
473 | "SN" -> Brand_sharenet
474 | "SS" -> Brand_swarmscope
475 | "SZ" (* shareaza *)
476 | "S~" -> Brand_shareaza
477 | "TN" -> Brand_torrentdotnet
478 | "TS" -> Brand_torrentstorm
479 | "XT" -> Brand_xantorrent
480 | "ZT" -> Brand_ziptorrent
481 | "bk" -> Brand_bitkitten
482 | "MP" -> Brand_moopolice
483 | "UM" -> Brand_utorrent_mac
484 | "UT" -> Brand_utorrent
485 | "KT" -> Brand_ktorrent
486 | "LP" -> Brand_lphant
487 | "TR" -> Brand_transmission
488 | "HN" -> Brand_hydranode
489 | "RT" -> Brand_retriever
490 | "PC" -> Brand_cachelogic
491 | "ES" -> Brand_electricsheep
492 | "qB" -> Brand_qbittorrent
494 | "UL" -> Brand_uleecher
495 | "XX" -> Brand_xtorrent
498 | "AX" -> Brand_bitpump
499 | "DE" -> Brand_deluge
500 | "TT" -> Brand_tuotu
501 | "SD" (* Thunder (aka XùnLéi) *)
502 | "XL" -> Brand_xunlei
503 | "FT" -> Brand_foxtorrent
504 | "BF" -> Brand_bitflu
505 | "OS" -> Brand_oneswarm
506 | "LW" -> Brand_limewire
507 | "HL" -> Brand_halite
509 | "PD" -> Brand_pando
512 if brand = Brand_unknown
then None
else
517 | Brand_bitcomet
-> (String.sub s
4 1) ^
"." ^
(String.sub s
5 2)
522 | Brand_utorrent
-> (String.sub s
3 1) ^
"." ^
(String.sub s
4 1) ^
"." ^
(String.sub s
5 1)
524 | Brand_transmission
-> (String.sub s
3 1) ^
"." ^
(String.sub s
4 2)
526 | Brand_ctorrent
-> (strip_leading_zeroes (String.sub s
3 2)) ^
"." ^
(strip_leading_zeroes(String.sub s
5 2))
527 (* 3.4.5->[R=RC.6|D=Dev|''] *)
529 let x = match s
.[5] with
530 | 'R'
-> " RC" ^
(String.sub s
6 1)
534 (String.sub s
3 1) ^
"." ^
(String.sub s
4 1) ^
x
536 | Brand_bitrocket
-> (String.sub s
3 1) ^
"." ^
(String.sub s
4 1) ^
"(" ^
(String.sub s
5 2) ^
")"
538 | Brand_xtorrent
-> "v" ^
(strip_leading_zeroes (String.sub s
3 4))
539 (* BitFlu is too complicated YMDD (Y+M -> HEX) eg. 7224 is 2007.02.24 *)
542 | _
-> (dot_string (String.sub s
3 4))
544 Some
(brand, version)
548 let decode_tornado_style s
=
549 if s
.[5] = '
-'
then begin
552 | 'T'
-> Brand_bittornado
553 | 'S'
-> Brand_shadow
556 | 'O'
-> Brand_osprey
557 | 'R'
-> Brand_tribler
562 if s
.[5] ='
-'
&& s
.[6] ='
-'
&& s
.[7] ='
-'
then begin
563 let brand = check_brand s
.[0] in
564 if not
(brand = Brand_unknown
) then
565 bv := Some
(brand, (dot_string_h (String.sub s
1 3)));
567 else if s
.[6] = (char_of_int
48) then begin
568 let brand = check_brand s
.[0] in
569 if not
(brand = Brand_unknown
) then
570 bv := Some
(brand, ("LM " ^
dot_string_h (String.sub s
1 3)));
576 let decode_mainline_style s
=
577 if check_all s
45 [2;7] && check_int s
1 then begin
581 | 'M'
-> Brand_mainline
584 if brand = Brand_unknown
then None
585 else Some
(brand, (dot_string_of_string (String.sub s
1 6)))
589 let decode_simple_style s
=
590 let simple_list = ref
591 [ (0, "martini", Brand_martiniman
, "");
592 (0, "oernu", Brand_btugaxp
, "");
593 (0, "BTDWV-", Brand_deadmanwalking
, "");
594 (0, "PRC.P---", Brand_btplus
, "II");
595 (0, "P87.P---", Brand_btplus
, "");
596 (0, "S587Plus", Brand_btplus
, "");
597 (5, "Azureus", Brand_azureus
, "2.0.3.2");
598 (0, "-G3", Brand_g3torrent
, "");
599 (0, "-AR", Brand_arctic
, "");
600 (4, "btfans", Brand_simplebt
, "");
601 (0, "btuga", Brand_btugaxp
, "");
602 (0, "BTuga", Brand_btugaxp
, "");
603 (0, "DansClient", Brand_xantorrent
, "");
604 (0, "Deadman Walking-", Brand_deadmanwalking
, "");
605 (0, "346-", Brand_torrenttopia
, "");
606 (0, "271-", Brand_greedbt
, "2.7.1");
607 (10, "BG", Brand_btgetit
, "");
608 (0, "a00---0", Brand_swarmy
, "");
609 (0, "a02---0", Brand_swarmy
, "");
610 (0, "10-------", Brand_jvtorrent
, "");
611 (0, "T00---0", Brand_teeweety
, "");
612 (0, "LIME", Brand_limewire
, "");
613 (0, "AZ2500BT", Brand_btyrant
, "");
614 (0, "Mbrst", Brand_burst
, (dot_string_of_list s
[5;7;9]));
615 (0, "Plus", Brand_plus
, (dot_string_of_list s
[4;5;6]));
616 (0, "OP", Brand_opera
, (dot_string(String.sub s
2 4)));
617 (0, "eX", Brand_exeem
, (String.sub s
2 18));
618 (0, "turbobt", Brand_turbobt
, (String.sub s
7 5));
619 (0, "btpd", Brand_btpd
, (dot_string(String.sub s
5 3)));
620 (0, "XBT", Brand_xbt
, (dot_string(String.sub s
3 3)));
621 (0, "-FG", Brand_flashget
, (dot_string(String.sub s
4 3)));
622 (0, "-SP", Brand_bitspirit
, (dot_string(String.sub s
3 3)));
625 let len = List.length
!simple_list in
627 if pos
>= len then None
629 let (x,y
,z
,v
) = List.nth
!simple_list pos
in
630 if (String.sub s
x (String.length y
)) = y
then Some
(z
,v
)
637 let minor = Char.code s
.[1] in
638 Printf.sprintf
"%d.%d.%d" (Char.code s
.[0]) (minor / 10) (minor mod 10) in
639 if "RS" = String.sub s
2 2 then
640 Some
(Brand_rufus
, release s
)
644 if "BOW" = String.sub s
0 3 ||
645 (check_all s
45 [0;7] && "BOW" = String.sub s
1 3) then
646 Some
(Brand_bitsonwheels
, (String.sub s
4 3))
650 if ("BTM" = String.sub s
0 3) && ("BTuga" = String.sub s
5 5) then
651 Some
(Brand_btuga
, dot_string(String.sub s
3 2))
654 let decode_shadow s
=
655 if "S" = String.sub s
0 1 then begin
657 if check_all s
45 [6;7;8] then begin
658 let i1 = int_of_string
("0x" ^
String.sub s
1 1) in
659 let i2 = int_of_string
("0x" ^
String.sub s
2 1) in
660 let i3 = int_of_string
("0x" ^
String.sub s
3 1) in
661 bv := Some
(Brand_shadow
, (Printf.sprintf
"%d.%d.%d" i1 i2 i3))
664 if s
.[8] = (char_of_int
0) then begin
665 let i1 = int_of_char s
.[1] in
666 let i2 = int_of_char s
.[2] in
667 let i3 = int_of_char s
.[3] in
668 bv := Some
(Brand_shadow
, (Printf.sprintf
"%d.%d.%d" i1 i2 i3))
674 let decode_bitspirit s
=
675 if "BS" = String.sub s
2 2 then begin
677 if s
.[1] = (char_of_int
0) then bv := Some
(Brand_bitspirit
, "v1");
678 if s
.[1] = (char_of_int
2) then bv := Some
(Brand_bitspirit
, "v2");
679 if s
.[1] = (char_of_int
3) then bv := Some
(Brand_bitspirit
, "v3");
685 if 'U'
= s
.[0] && s
.[8] = '
-'
then
686 Some
(Brand_upnp
, (dot_string (String.sub s
1 3)))
689 let decode_old_bitcomet s
=
690 let bitcomet = String.sub s
0 4 in
691 if "exbc" = bitcomet || "FUTB" = bitcomet || "xUTB" = bitcomet then
692 let brand = if "LORD" = String.sub s
6 4 then
693 Brand_bitlord
else Brand_bitcomet
695 let versionMajorNumber = int_of_char s
.[4] in
696 let versionMinorNubmer =
697 match versionMajorNumber with
698 0 -> (int_of_char s
.[5])
699 | _
-> ((int_of_char s
.[5]) mod 10)
701 let version = Printf.sprintf
"%d.%d"
702 versionMajorNumber versionMinorNubmer in
703 Some
(brand, version)
706 let decode_shareaza s
=
707 let rec not_zeros pos
=
708 if pos
> 15 then true else
709 if s
.[pos
] = (char_of_int
0) then false else not_zeros (pos
+1)
711 let rec weird_crap pos
=
712 if pos
> 19 then true else
713 let i1 = (int_of_char s
.[pos
]) in
714 let i2 = (int_of_char s
.[(pos
mod 16)]) in
715 let i3 = (int_of_char s
.[(15 - (pos
mod 16))]) in
716 if not
(i1 = (i2 lxor i3)) then false else weird_crap (pos
+1)
718 if (not_zeros 0) && (weird_crap 16) then Some
(Brand_shareaza
, "") else None
720 let decode_non_zero s
=
721 let max_pos = ((String.length s
) - 1) in
722 let zero = char_of_int
0 in
723 let rec find_non_zero pos
=
724 if pos
> max_pos then max_pos else
725 if not
(s
.[pos
] = zero) then pos
else
726 find_non_zero (pos
+1)
729 (match find_non_zero 0 with
730 8 -> (if "UDP0" = String.sub s
16 4 then
731 bv := Some
(Brand_bitcomet
, "UDP");
732 if "HTTPBT" = String.sub s
14 6 then
733 bv := Some
(Brand_bitcomet
, "HTTP"));
734 | 9 -> if check_all s
3 [9;10;11] then
735 bv := Some
(Brand_snark
, "");
736 | 12 -> if check_all s
97 [12;13] then
737 bv := Some
(Brand_experimental
, "3.2.1b2")
739 if check_all s
0 [12;13] then
740 bv := Some
(Brand_experimental
, "3.1")
742 bv := Some
(Brand_mainline
, "")
748 (* format is : "-ML" ^ version ( of unknown length) ^ "-" ^ random bytes ( of unknown length) *)
749 let decode_mldonkey_style s
=
750 if '
-'
= s
.[0] then begin
751 let s_id = String.sub s
1 2 in
754 | "ML" -> Brand_mldonkey
757 if brand = Brand_unknown
then None
else
759 (try String.index_from s
3 '
-'
762 let version = String.sub s
3 len in
763 Some
(brand, version)
769 decode_tornado_style;
770 decode_mainline_style;
779 decode_mldonkey_style;
784 let parse_software s
=
785 let default = (Brand_unknown
, "") in
786 let rec iter = function
788 if !verbose_msg_clienttags
then lprintf_nl "BTUC: %S" s
;
793 | Some
(brand, version as bv) ->
794 if !verbose_msg_clienttags
then
795 lprintf_nl "BTKC: %S; ID: %S; version: %S" s
(brand_to_string
brand) version;
798 if Sha1.direct_of_string s
= Sha1.null
then
801 try iter decoder_list
804 let check_client_country_code c
=
805 if Geoip.active
() then
806 match c
.client_country_code
with
808 c
.client_country_code
<-
809 Geoip.get_country_code_option
(fst c
.client_host
)
812 let new_client file peer_id kind cc
=
814 let c = Hashtbl.find
file.file_clients kind
in
815 let old_ip = fst
c.client_host
in
816 c.client_host
<- kind
;
817 if old_ip <> Ip.null
&& old_ip <> fst
c.client_host
then
819 c.client_country_code
<- None
;
820 check_client_country_code c
824 let brand, release = parse_software (Sha1.direct_to_string peer_id
) in
826 client_client
= impl;
827 client_sock
= NoConnection
;
828 client_upload_requests
= [];
829 client_connection_control
= new_connection_control
(());
832 client_country_code
= cc
;
833 client_choked
= true;
834 client_received_peer_id
= false;
835 client_sent_choke
= false;
836 client_interested
= false;
837 client_uploader
= None
;
839 client_ranges_sent
= [];
840 client_range_waiting
= None
;
842 client_uid
= peer_id
;
843 client_brand
= brand;
844 client_release
= release;
845 client_bitmap
= None
;
846 client_allowed_to_write
= zero;
847 client_total_uploaded
= zero;
848 client_total_downloaded
= zero;
849 client_session_uploaded
= zero;
850 client_session_downloaded
= zero;
851 client_upload_rate
= Rate.new_rate
();
852 client_downloaded_rate
= Rate.new_rate
();
853 client_connect_time
= last_time
();
854 client_blocks_sent
= [];
855 client_new_chunks
= [];
858 client_alrd_sent_interested
= false;
859 client_alrd_sent_notinterested
= false;
860 client_interesting
= false;
861 client_incoming
= false;
862 client_registered_bitfield
= false;
863 client_last_optimist
= 0;
865 client_cache_extension
= false;
866 client_fast_extension
= false;
867 client_utorrent_extension
= false;
868 client_ut_metadata_msg
= -1L;
869 client_azureus_messaging_protocol
= false;
871 dummy_client_impl
with
873 impl_client_ops
= client_ops
;
874 impl_client_upload
= None
;
876 c.client_connection_control
.control_min_reask
<- 120;
877 check_client_country_code c;
879 Hashtbl.add
file.file_clients kind
c;
880 file.file_clients_num
<- file.file_clients_num
+ 1;
881 file_add_source
(as_file file) (as_client c);
884 let remove_file file =
885 Hashtbl.remove
files_by_uid file.file_id
;
886 current_files := List2.removeq
file !current_files
888 let remove_client c =
889 Hashtbl.remove
c.client_file
.file_clients
c.client_host
;
890 c.client_file
.file_clients_num
<- c.client_file
.file_clients_num
- 1;
891 file_remove_source
(as_file c.client_file
) (as_client c)
893 let remove_tracker url file =
894 if !verbose_msg_servers
then
895 List.iter (fun tracker
->
896 lprintf_nl "Old tracker list: %s" (show_tracker_url tracker
.tracker_url
)
897 ) file.file_trackers
;
898 List.iter (fun bad_tracker
->
899 if bad_tracker
.tracker_url
= url then
900 file.file_trackers
<- List2.remove_first bad_tracker
file.file_trackers
;
901 ) file.file_trackers
;
902 if !verbose_msg_servers
then
903 List.iter (fun tracker
->
904 lprintf_nl "New tracker list: %s" (show_tracker_url tracker
.tracker_url
)
907 let tracker_is_enabled t =
908 match t.tracker_status
with
910 | Disabled_failure
(i
,_
) ->
911 if !!tracker_retries
= 0 || i
< !!tracker_retries
then true else false
914 let torrents_directory = "torrents"
915 let new_torrents_directory = Filename.concat
torrents_directory "incoming"
916 let downloads_directory = Filename.concat
torrents_directory "downloads"
917 let tracked_directory = Filename.concat
torrents_directory "tracked"
918 let seeded_directory = Filename.concat
torrents_directory "seeded"
919 let old_directory = Filename.concat
torrents_directory "old"
921 (*************************************************************
923 Define a function to be called when the "mem_stats" command
924 is used to display information on structure footprint.
926 **************************************************************)
929 Heap.add_memstat
"BittorrentGlobals" (fun level
buf ->
930 Printf.bprintf
buf "Number of old files: %d\n" (List.length
!!old_files
);
931 let downloads = ref 0 in
932 let tracked = ref 0 in
933 let seeded = ref 0 in
934 Unix2.iter_directory
(fun file -> incr
downloads ) downloads_directory;
935 Unix2.iter_directory
(fun file -> incr
tracked ) tracked_directory;
936 Unix2.iter_directory
(fun file -> incr
seeded ) seeded_directory;
937 Printf.bprintf
buf "Files in downloads directory: %d\n" ! downloads;
938 Printf.bprintf
buf "Files in tracked directory: %d\n" ! tracked;
939 Printf.bprintf
buf "Files in seeded directory: %d\n" ! seeded;
940 Printf.bprintf
buf "files_by_uid: %d\n" (Hashtbl.length
files_by_uid);
941 Printf.bprintf
buf "ft_by_num: %d\n" (Hashtbl.length
ft_by_num);
947 Heap.add_memstat
"BittorrentDHT" (fun _level
buf ->
951 let (buckets
,nodes
,keys
,peers
) = stat dht
in
952 Printf.bprintf
buf "Routing : %d nodes in %d buckets\n" nodes buckets
;
953 Printf.bprintf
buf "Storage : %d keys with %d peers\n" keys peers
;
954 List.iter (fun s
-> Printf.bprintf
buf "%s\n" s
) (rpc_stats dht
);
955 let queries = ["PING",`Ping
;"FIND_NODE",`FindNode
;"GET_PEERS",`GetPeers
;"ANNOUNCE",`Announce
] in
956 Printf.bprintf
buf "Outgoing queries : ok/error/timeout\n";
957 List.iter begin fun (name
,qt
) ->
958 let get k
= try Hashtbl.find dht
.M.stats
(qt
,`Out k
) with Not_found
-> 0 in
959 Printf.bprintf
buf "%s: %d/%d/%d\n" name
(get `Answer
) (get `Error
) (get `Timeout
);
961 Printf.bprintf
buf "Incoming queries\n";
962 List.iter begin fun (name
,qt
) ->
963 let get () = try Hashtbl.find dht
.M.stats
(qt
,`In
) with Not_found
-> 0 in
964 Printf.bprintf
buf "%s: %d\n" name
(get ())