patch #7541
[mldonkey.git] / src / networks / bittorrent / bTInteractive.ml
blob1f74cbfdc7e207654be4ab1b91e0936c01c31306
1 (* Copyright 2001, 2002 b52_simon :), b8_bavard, b8_fee_carabine, INRIA *)
2 (*
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
20 open Int64ops
21 open Options
22 open Printf2
23 open Md4
24 open BasicSocket
26 open CommonSearch
27 open CommonGlobals
28 open CommonUser
29 open CommonClient
30 open CommonOptions
31 open CommonServer
32 open CommonResult
33 open CommonTypes
34 open CommonComplexOptions
35 open CommonFile
36 open CommonDownloads
37 open CommonShared
38 open CommonInteractive
39 open Autoconf
41 open BTTypes
42 open BTOptions
43 open BTGlobals
44 open BTComplexOptions
45 open BTProtocol
47 open Bencode
49 open Gettext
50 let _s x = _s "BTInteractive" x
51 let _b x = _b "BTInteractive" x
53 module VB = VerificationBitmap
55 let porttest_result = ref PorttestNotStarted
57 let interpret_azureus_porttest s =
58 let failure_message fmt =
59 Printf.sprintf ("Port test failure, " ^^ fmt) in
60 try
61 let value = decode s in
62 match value with
63 | Dictionary alist ->
64 (try
65 match List.assoc "result" alist with
66 | Int 1L -> "Port test OK!"
67 | Int 0L ->
68 (try
69 match List.assoc "reason" alist with
70 | String reason -> failure_message "%s" reason
71 | _ -> raise Not_found
72 with Not_found ->
73 failure_message "%s" "no reason given")
74 | Int status ->
75 failure_message "unknown status code (%Ld)" status
76 | _ -> raise Not_found
77 with Not_found ->
78 failure_message "%s" "no status given")
79 | _ ->
80 failure_message "unexpected value type %s" (Bencode.print value)
81 with _ ->
82 failure_message "%s" "broken bencoded value"
84 let interpret_utorrent_porttest s =
85 if String2.contains s "<div class=\"status-image\">OK!</div>" then
86 "Port test OK!"
87 else
88 "Port is not accessible"
90 let perform_porttests tests =
91 match tests with
92 | [] -> porttest_result := PorttestResult (last_time(), "No tests available")
93 | _ ->
94 let module H = Http_client in
95 porttest_result := PorttestInProgress (last_time ());
96 let rec loop = function
97 | [] -> ()
98 | (url,interpret)::other ->
99 let r = {
100 H.basic_request with
101 H.req_url = Url.of_string url;
102 H.req_user_agent = get_user_agent ();
103 (* no sense in using proxy anyway *)
104 (* H.req_proxy = !CommonOptions.http_proxy; *)
105 H.req_max_total_time = 45.;
106 } in
107 H.wget_string r
108 (fun s -> porttest_result := PorttestResult (last_time (), interpret s))
109 ~ferr:(fun code ->
110 porttest_result := PorttestResult (last_time (), Printf.sprintf "Remote service error (%d)" code);
111 loop other)
112 (fun _ _ -> ())
114 loop tests
116 let op_file_all_sources file =
117 let list = ref [] in
118 Hashtbl.iter (fun _ c ->
119 list := (as_client c) :: !list
120 ) file.file_clients;
121 !list
123 let op_file_active_sources file =
124 let list = ref [] in
125 Hashtbl.iter (fun _ c ->
126 let as_c = as_client c in
127 match client_state as_c with
128 Connected_downloading _ -> list := as_c :: !list
129 | _ -> ()
130 ) file.file_clients;
131 !list
133 let op_file_files file impl =
134 match file.file_swarmer with
135 None -> [CommonFile.as_file impl]
136 | Some swarmer ->
137 CommonSwarming.subfiles swarmer
139 let op_file_debug file =
140 let buf = Buffer.create 100 in
141 (* CommonSwarming.debug_print buf file.file_swarmer; *)
142 Hashtbl.iter (fun _ c ->
143 Printf.bprintf buf "Client %d: %s\n" (client_num c)
144 (match c.client_sock with
145 NoConnection -> "No Connection"
146 | Connection _ -> "Connected"
147 | ConnectionWaiting _ -> "Waiting for Connection"
149 ) file.file_clients;
150 Buffer.contents buf
152 let op_file_commit file new_name =
153 CommonSwarming.remove_swarmer file.file_swarmer;
154 file.file_swarmer <- None;
155 if file_state file <> FileShared then
156 begin
157 if not (List.mem (file.file_name, file_size file) !!old_files) then
158 old_files =:= (file.file_name, file_size file) :: !!old_files;
159 set_file_state file FileShared;
161 if Unix32.destroyed (file_fd file) then
162 if !verbose then lprintf_file_nl (as_file file) "op_file_commit: FD is destroyed... repairing";
164 (* During the commit operation, for security, the file_fd is destroyed. So
165 we create it again to be able to share this file again. *)
166 set_file_fd
167 (as_file file)
168 (create_temp_file new_name (List.map (fun (file,size,_) -> (file,size)) file.file_files) (file_state file));
170 if Unix32.destroyed (file_fd file) then
171 lprintf_file_nl (as_file file) "op_file_commit: FD is destroyed... could not repair!";
173 let new_torrent_diskname =
174 Filename.concat seeded_directory
175 (Filename.basename file.file_torrent_diskname)
177 (try
178 Unix2.rename file.file_torrent_diskname new_torrent_diskname;
179 with _ ->
180 (lprintf_file_nl (as_file file) "op_file_commit: failed to rename %s to %s"
181 file.file_torrent_diskname new_torrent_diskname));
182 file.file_torrent_diskname <- new_torrent_diskname;
184 (* update file_shared with new path to committed file *)
185 match file.file_shared with
186 | None -> ()
187 | Some old_impl ->
188 begin
189 let impl = {
190 impl_shared_update = 1;
191 impl_shared_fullname = file_disk_name file;
192 impl_shared_codedname = old_impl.impl_shared_codedname;
193 impl_shared_size = file_size file;
194 impl_shared_id = Md4.null;
195 impl_shared_num = 0;
196 impl_shared_uploaded = old_impl.impl_shared_uploaded;
197 impl_shared_ops = shared_ops;
198 impl_shared_val = file;
199 impl_shared_requests = old_impl.impl_shared_requests;
200 impl_shared_file = Some (as_file file);
201 impl_shared_servers = [];
202 } in
203 file.file_shared <- Some impl;
204 replace_shared old_impl impl;
206 end
208 let auto_links =
209 let re = Str.regexp_case_fold "\\(https?://[a-zA-Z0-9_.!~*'();/?:@&=+$,%-]+\\)" in
210 fun s -> Str.global_replace re "\\<a href=\\\"\\1\\\"\\>\\1\\</a\\>" s
212 (** Get swarming info for subfiles (priorities and progress)
213 @return empty list if no swarmer *)
214 let get_subfiles file =
215 match try Some (CommonSwarming.file_swarmer (as_file file)) with _ -> None with
216 | None -> []
217 | Some swarmer ->
218 match CommonSwarming.get_swarmer_priorities_intervals swarmer with
219 | [] -> []
220 | ((_,prio)::_ as l) ->
221 let intervals = ref l in
222 let prio = ref prio in
223 let rec count_intervals_till bytes =
224 let rec loop acc_prio = function
225 | (i_start,i_prio) :: tail when i_start < bytes ->
226 prio := i_prio;
227 loop (min acc_prio i_prio) tail
228 | ((i_start,i_prio) :: _ as l) when i_start = bytes ->
229 prio := i_prio;
230 intervals := l;
231 acc_prio
232 | l -> intervals := l; acc_prio
234 loop !prio !intervals
236 let downloaded = CommonSwarming.get_swarmer_block_verified swarmer in
237 let r = ref [] in
238 Unix32.subfile_tree_map (file_fd file)
239 begin fun fname start length current_length ->
240 let prio = count_intervals_till (start ++ length) in
241 (* let (blockstart,blockend) = CommonSwarming.blocks_of_ *)
242 let stop = if length <> 0L then (start ++ length -- 1L) else start in
243 let blockstart = try CommonSwarming.compute_block_num swarmer start with _ -> 0 in
244 let blockend = try CommonSwarming.compute_block_num swarmer stop with _ -> 0 in
245 let ok = ref 0 in
246 for i = blockstart to blockend do
247 if VB.State_verified = VB.get downloaded i then incr ok;
248 done;
249 let progress = float !ok /. float (blockend - blockstart + 1) in
250 r := (fname, length, prio, progress) :: !r
251 end;
252 List.rev !r
255 let op_file_print file o =
257 let subfiles =
258 let subfiles = ref (get_subfiles file) in
259 List.map begin fun (name,size,magic) ->
260 let magic = match magic with None -> "" | Some m -> Printf.sprintf " / %s" m in
261 match !subfiles with
262 | [] -> (name,size,magic,"",None)
263 | (i_name,i_size,i_prio,progress)::t ->
265 lprintf_nl "%S = %S %Ld = %Ld | priority %d" name i_name size i_size i_prio;
267 subfiles := t;
268 let progress = Printf.sprintf ", %.0f%%" (100. *. progress) in
269 if name = i_name && size = i_size then (* sanity check *)
270 (name,size,magic,progress,Some i_prio)
271 else
272 (name,size,magic,progress,None)
273 end file.file_files
275 let buf = o.conn_buf in
276 if use_html_mods o then
277 begin
278 let emit text ?(desc=text) value =
279 Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ());
280 html_mods_td buf [
281 (desc, "sr br", text);
282 ("", "sr", value)
286 emit (_s"Filename") file.file_name;
287 emit (_s"Hash") ~desc:(_s"Torrent metadata hash") (Sha1.to_hexa file.file_id);
288 emit (_s"Torrent search") ~desc:(_s"Search for similar torrent files") (Printf.sprintf
289 "\\<a target=\\\"_blank\\\" href=\\\"http://isohunt.com/%s\\\"\\>IsoHunt\\</a\\>" file.file_name);
291 let tracker_header_printed = ref false in
292 List.iter (fun tracker ->
293 let tracker_url = show_tracker_url tracker.tracker_url in
294 let tracker_text =
295 if not !!use_trackers then
296 Printf.sprintf "disabled: %s" tracker_url
297 else
298 match tracker.tracker_status with
299 | Disabled s | Disabled_mld s ->
300 Printf.sprintf "\\<font color=\\\"red\\\"\\>disabled: %s\\<br\\>\\--error: %s\\</font\\>"
301 tracker_url s
302 | Disabled_failure (i,s) ->
303 Printf.sprintf "\\<font color=\\\"red\\\"\\>disabled: %s\\<br\\>\\--error: %s (try %d)\\</font\\>"
304 tracker_url s i
305 | _ ->
306 Printf.sprintf "enabled: %s" tracker_url
308 let text = if not !tracker_header_printed then _s"Tracker(s)" else "" in
309 emit text tracker_text;
310 tracker_header_printed := true;
311 ) file.file_trackers;
313 emit (_s"Torrent filename") file.file_torrent_diskname;
314 emit (_s"Comment") (match file.file_comment with "" -> "-" | s -> auto_links s);
315 emit (_s"Created by") (match file.file_created_by with "" -> "-" | s -> auto_links s);
316 emit (_s"Creation date") (Date.to_string (Int64.to_float file.file_creation_date));
317 emit (_s"Modified by") (match file.file_modified_by with "" -> "-" | s -> auto_links s);
318 emit (_s"Encoding") (match file.file_encoding with "" -> "-" | s -> s);
319 emit (_s"Piece size") (Int64.to_string file.file_piece_size);
320 emit (_s"Private") ~desc:(_s"Private torrents get peers only via trackers")
321 (if file.file_private then _s "yes" else _s "no");
322 if !bt_dht <> None then
323 emit (_s"Last DHT announce") ~desc:(_s"Last time this torrent was announced in DHT")
324 (string_of_date file.file_last_dht_announce);
326 let rec print_first_tracker l =
327 match l with
328 | [] -> ()
329 | t :: q ->
330 if not (tracker_is_enabled t) then
331 print_first_tracker q
332 else begin
333 emit (_s"Last announce") ~desc:(_s"Last time this torrent was announced to the tracker")
334 (string_of_date t.tracker_last_conn);
336 if t.tracker_last_conn > 1 then
337 emit (_s"Next announce") ~desc:(_s"Time of the next announce to the tracker (planned)")
338 (string_of_date (t.tracker_last_conn + t.tracker_interval));
340 emit (_s"Announce interval") ~desc:(_s"Tracker announce interval")
341 (Printf.sprintf "%d seconds" t.tracker_interval);
343 emit (_s"Min announce interval") ~desc:(_s"Minimum tracker announce interval")
344 (Printf.sprintf "%d seconds" t.tracker_min_interval);
346 (* show only interesting answers*)
347 if t.tracker_torrent_downloaded > 0 then
348 emit (_s"Downloaded") (string_of_int t.tracker_torrent_downloaded);
350 if t.tracker_torrent_complete > 0 then
351 emit (_s"Seeders") ~desc:(_s"Peers that have complete download")
352 (string_of_int t.tracker_torrent_complete);
354 if t.tracker_torrent_incomplete > 0 then
355 emit (_s"Leechers") ~desc:(_s"Peers that have incomplete download")
356 (string_of_int t.tracker_torrent_incomplete);
358 if t.tracker_torrent_total_clients_count > 0 then
359 emit (_s"Peers") ~desc:(_s"Total clients count")
360 (string_of_int t.tracker_torrent_total_clients_count);
362 if t.tracker_torrent_last_dl_req > 0 then
363 emit (_s"Latest request") (Printf.sprintf "%ds" t.tracker_torrent_last_dl_req);
365 if String.length t.tracker_id > 0 then
366 emit (_s"Tracker id") t.tracker_id;
368 if String.length t.tracker_key > 0 then
369 emit (_s"Tracker key") t.tracker_key;
372 print_first_tracker file.file_trackers;
374 (* This is bad. Magic info should be automatically filled in when
375 the corresponding chunks complete. (see CommonSwarming)
377 This code only fills in the magic info for subfiles when a user
378 manually performs a "vd #". (interfaces out of sync)
380 Magic info for shared files with subfiles is missing as well?
382 if !Autoconf.magic_works then begin
383 let check_magic file =
384 match Magic.M.magic_fileinfo file false with
385 None -> None
386 | Some s -> Some (intern s)
388 let fdn = file_disk_name file in
389 let new_file_files = ref [] in
391 List.iter (fun (f, s, m) ->
392 let subfile = Filename.concat fdn f in
393 new_file_files := (f,s, check_magic subfile) :: !new_file_files;
394 ) file.file_files;
396 file.file_files <- List.rev !new_file_files;
397 file_must_update file; (* Send update to guis *)
399 end;
400 (* -- End bad -- *)
402 let extra =
403 match List.fold_left (fun acc subfile ->
404 match acc, subfile with
405 | (Some false|None),(_,_,_,_,Some prio) when prio > 0 -> Some true
406 | None,(_,_,_,_,Some 0) -> Some false
407 | None,(_,_,_,_,None) -> None
408 | acc,_ -> acc) None subfiles
409 with
410 | None -> ""
411 | Some dl ->
412 Printf.sprintf ", \\<a title=\\\"toggle all files\\\" href=\\\"submit?q=set_subfile_prio+%d+%d+%d+%d\\\"\\>%s\\</a\\>"
413 (file_num file) (if dl then 0 else 1) 0 (List.length subfiles - 1)
414 (if dl then "unselect all" else "select all")
416 emit (_s"Full path"^extra) ~desc:(_s"Full path to the download") (file_disk_name file);
418 let cntr = ref 0 in
419 List.iter (fun (filename, size, magic, progress, prio) ->
420 Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ());
421 let fs = Printf.sprintf (_b"File %d") !cntr in
422 let extra = match prio with
423 | None -> ""
424 | Some prio -> Printf.sprintf ", \\<a title=\\\"toggle file\\\" href=\\\"javascript:void(0)\\\" onclick=\\\"xhr_get('submit?api=set_subfile_prio+%d+%d+%d',toggle_priority(this,%d,%d))\\\"\\>priority %d\\</a\\>" (file_num file) (if prio = 0 then 1 else 0) !cntr (file_num file) !cntr prio
426 emit (fs^extra) ~desc:fs (Printf.sprintf "%s (%Ld bytes%s)%s" filename size progress magic);
427 incr cntr;
428 ) subfiles
429 end (* use_html_mods *)
430 else begin
432 Printf.bprintf buf "Trackers:\n";
433 List.iter (fun tracker ->
434 let tracker_url = show_tracker_url tracker.tracker_url in
435 match tracker.tracker_status with
436 | Disabled s | Disabled_mld s ->
437 Printf.bprintf buf "%s, disabled: %s\n" tracker_url s
438 | Disabled_failure (i,s) ->
439 Printf.bprintf buf "%s, disabled (try %d): %s\n" tracker_url i s
440 | _ -> Printf.bprintf buf "%s\n" tracker_url
441 ) file.file_trackers;
442 if file.file_torrent_diskname <> "" then
443 Printf.bprintf buf "Torrent diskname: %s\n" file.file_torrent_diskname;
444 if file.file_comment <> "" then Printf.bprintf buf "Comment: %s\n" file.file_comment;
445 if file.file_created_by <> "" then Printf.bprintf buf "Created by %s\n" file.file_created_by;
446 let s = Date.to_string (Int64.to_float file.file_creation_date) in
447 if s <> "" then Printf.bprintf buf "Creation date: %s\n" s;
448 if file.file_modified_by <> "" then Printf.bprintf buf "Modified by %s\n" file.file_modified_by;
449 if file.file_encoding <> "" then Printf.bprintf buf "Encoding: %s\n" file.file_encoding;
450 Printf.bprintf buf (_b"Full path: %s\n") (file_disk_name file);
451 if file.file_files <> [] then Printf.bprintf buf "Subfiles: %d\n" (List.length file.file_files);
452 let cntr = ref 0 in
453 List.iter (fun (filename, size, magic, progress, prio) ->
454 incr cntr;
455 let prio = match prio with Some n -> Printf.sprintf ", priority %d" n | None -> "" in
456 Printf.bprintf buf "File %d%s: %s (%Ld bytes%s)%s\n" !cntr prio filename size progress magic
457 ) subfiles
460 let op_file_print_sources file o =
461 let buf = o.conn_buf in
463 (* redefine functions for telnet output *)
464 let html_mods_td buf l =
465 if use_html_mods o then
466 html_mods_td buf l
467 else
468 (* List *)
469 List.iter (fun (t,c,d) ->
470 (* Title Class Value *)
471 Printf.bprintf buf "%s "
475 let html_mods_table_header buf n c l =
476 if use_html_mods o then
477 html_mods_table_header buf n c l
478 else
479 if List.length l > 0 then begin
480 Printf.bprintf buf "\n";
481 List.iter (fun (w,x,y,z) ->
482 (* Sort Class Title Value *)
483 Printf.bprintf buf "%s "
485 ) l;
486 Printf.bprintf buf "\n"
490 if Hashtbl.length file.file_clients > 0 then begin
492 let header_list = [
493 ( Num, "srh br ac", "Client number", "Num" ) ;
494 ( Str, "srh br", "Client UID", "UID" ) ;
495 ( Str, "srh br", "Client software", "Soft" ) ;
496 ( Str, "srh", "IP address", "IP address" ) ;
497 ( Num, "srh br ar", "Port", "Port" ) ;
498 ] @ (if Geoip.active () then [( Str, "srh br ar", "Country Code/Name", "CC" )] else []) @ [
499 ( Num, "srh ar", "Total UL bytes to this client for all files", "tUL" ) ;
500 ( Num, "srh ar br", "Total DL bytes from this client for all files", "tDL" ) ;
501 ( Num, "srh ar", "Session UL bytes to this client for all files", "sUL" ) ;
502 ( Num, "srh ar br", "Session DL bytes from this client for all files", "sDL" ) ;
503 ( Str, "srh ar", "Interested [T]rue, [F]alse", "I" ) ;
504 ( Str, "srh ar", "Choked [T]rue, [F]alse", "C" ) ;
505 ( Num, "srh br ar", "Allowed to write", "A" ) ;
506 ( Str, "srh ar", "Interesting [T]rue, [F]alse", "I" );
507 ( Str, "srh ar", "Already sent interested [T]rue, [F]alse", "A" );
508 ( Str, "srh br ar", "Already sent not interested [T]rue, [F]alse", "N" );
510 ( Str, "srh ar", "Good [T]rue, [F]alse", "G" );
511 ( Str, "srh ar", "Incoming [T]rue, [F]alse", "I" );
512 ( Str, "srh br ar", "Registered bitfield [T]rue, [F]alse", "B" );
514 ( Num, "srh ar", "Connection Time", "T" );
515 ( Str, "srh ar", "Last optimistic unchoke", "L.Opt" );
516 ( Num, "srh br ar", "Number of tries", "N" );
518 ( Str, "srh", "DHT [T]rue, [F]alse", "D" );
519 ( Str, "srh", "Cache extensions [T]rue, [F]alse", "C" );
520 ( Str, "srh", "Fast extensions [T]rue, [F]alse", "F" );
521 ( Str, "srh", "uTorrent extensions [T]rue, [F]alse", "U" );
522 ( Str, "srh br", "Azureus messaging protocol [T]rue, [F]alse", "A" );
524 ( Str, "srh", "Bitmap (absent|partial|present|verified)", (colored_chunks
525 (Array.init (String.length info.G.file_chunks)
526 (fun i -> ((int_of_char info.G.file_chunks.[i])-48)))) ) ;
528 ( Num, "srh ar", "Number of full chunks", (Printf.sprintf "%d"
529 (match file.file_swarmer with
530 | None -> 0
531 | Some swarmer ->
532 let bitmap =
533 CommonSwarming.chunks_verified_bitmap swarmer in
534 VB.fold_lefti (fun acc _ s ->
535 if s = VB.State_verified then acc + 1 else acc) 0 bitmap)))
536 ] in
538 html_mods_table_header buf "sourcesTable" "sources al" header_list;
540 Hashtbl.iter (fun _ c ->
541 let cinfo = client_info (as_client c) in
542 if use_html_mods o then
543 Printf.bprintf buf "\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr());
545 let btos b = if b then "T" else "F" in
546 let cc,cn = Geoip.get_country_code_name cinfo.GuiTypes.client_country_code in
547 let td_list = [
548 ("", "sr br ar", Printf.sprintf "%d" (client_num c));
549 ("", "sr br", (Sha1.to_string c.client_uid));
550 ("", "sr br", Printf.sprintf "%s %s" (brand_to_string c.client_brand) c.client_release);
551 ("", "sr", (Ip.to_string (fst c.client_host)));
552 ("", "sr br ar", Printf.sprintf "%d" (snd c.client_host));
553 ] @ (if Geoip.active () then
554 [( cn, "sr br", if use_html_mods o then CommonPictures.flag_html cc else cc)]
555 else []) @ [
556 ("", "sr ar", (size_of_int64 c.client_total_uploaded));
557 ("", "sr ar br", (size_of_int64 c.client_total_downloaded));
558 ("", "sr ar", (size_of_int64 c.client_session_uploaded));
559 ("", "sr ar br", (size_of_int64 c.client_session_downloaded));
560 ("", "sr", (btos c.client_interested));
561 ("", "sr", (btos c.client_choked));
562 ("", "sr br ar", (Int64.to_string c.client_allowed_to_write));
563 (* This is way too slow for 1000's of chunks on a page with 100's of sources
564 ("", "sr", (CommonFile.colored_chunks (Array.init (String.length c.client_bitmap)
565 (fun i -> (if c.client_bitmap.[i] = '1' then 2 else 0)) )) );
567 ("", "sr", (btos c.client_interesting));
568 ("", "sr", (btos c.client_alrd_sent_interested));
569 ("", "br sr", (btos c.client_alrd_sent_notinterested));
571 ("", "sr", (btos c.client_good));
572 ("", "sr", (btos c.client_incoming));
573 ("", "br sr", (btos c.client_registered_bitfield));
575 ("", "sr", Printf.sprintf "%d" ((last_time () - c.client_connect_time) / 60));
576 ("", "ar sr", string_of_date c.client_last_optimist);
577 ("", "br sr", Printf.sprintf "%d" c.client_num_try);
579 ("", "sr", (btos c.client_dht));
580 ("", "sr", (btos c.client_cache_extension));
581 ("", "sr", (btos c.client_fast_extension));
582 ("", "sr", (btos c.client_utorrent_extension));
583 ("", "br sr", (btos c.client_azureus_messaging_protocol));
585 ("", "sr ar", (let fc = ref 0 in
586 (match c.client_bitmap with
587 None -> ()
588 | Some bitmap ->
589 Bitv.iter (fun s -> if s then incr fc) bitmap);
590 (Printf.sprintf "%d" !fc) ) )
591 ] in
593 html_mods_td buf td_list;
594 if use_html_mods o then Printf.bprintf buf "\\</tr\\>"
595 else Printf.bprintf buf "\n";
597 ) file.file_clients;
599 if use_html_mods o then Printf.bprintf buf "\\</table\\>\\</div\\>\\<br\\>"
600 else Printf.bprintf buf "\n";
604 let op_file_check file =
605 lprintf_file_nl (as_file file) "Checking chunks of %s" file.file_name;
606 match file.file_swarmer with
607 None ->
608 lprintf_file_nl (as_file file) "verify_chunks: no swarmer to verify chunks"
609 | Some swarmer ->
610 CommonSwarming.verify_all_chunks_immediately swarmer
612 let remove_all_clients file =
613 Hashtbl.clear file.file_clients;
614 file.file_clients_num <- 0
616 let op_file_cancel file =
617 CommonSwarming.remove_swarmer file.file_swarmer;
618 file.file_swarmer <- None;
619 BTClients.file_stop file;
620 remove_file file;
621 BTClients.disconnect_clients file;
622 remove_all_clients file;
623 if Sys.file_exists file.file_torrent_diskname then Sys.remove file.file_torrent_diskname
625 let op_ft_cancel ft =
626 Hashtbl.remove ft_by_num ft.ft_id
628 let op_ft_commit ft newname =
629 Hashtbl.remove ft_by_num ft.ft_id
631 let op_file_info file =
633 let module P = GuiTypes in
635 let last_seen = match file.file_swarmer with
636 None -> [| last_time () |]
637 | Some swarmer -> CommonSwarming.compute_last_seen swarmer in
639 { (impl_file_info file.file_file) with
641 P.file_name = file.file_name;
642 P.file_network = network.network_num;
643 P.file_chunks = (match file.file_swarmer with
644 | None -> None
645 | Some swarmer -> Some (CommonSwarming.chunks_verified_bitmap swarmer));
646 P.file_chunk_size = (match file.file_swarmer with
647 | None -> None
648 | Some t -> Some (List.map (fun t -> t.CommonSwarming.t_chunk_size) t.CommonSwarming.t_s.CommonSwarming.s_networks));
649 P.file_availability =
650 [network.network_num,(match file.file_swarmer with
651 None -> "" | Some swarmer ->
652 CommonSwarming.chunks_availability swarmer)];
654 P.file_chunks_age = last_seen;
655 P.file_uids = [Uid.create (BTUrl file.file_id)];
656 P.file_sub_files = file.file_files;
657 P.file_active_sources = List.length (op_file_active_sources file);
658 P.file_all_sources = (Hashtbl.length file.file_clients);
659 P.file_comment = file.file_comment;
662 let op_ft_info ft =
664 let module P = GuiTypes in
667 P.file_fields = P.Fields_file_info.all;
669 P.file_comment = file_comment (as_ft ft);
670 P.file_name = ft.ft_filename;
671 P.file_num = ft_num ft;
672 P.file_network = network.network_num;
673 P.file_names = [ft.ft_filename];
674 P.file_md4 = Md4.null;
675 P.file_size = ft_size ft;
676 P.file_downloaded = zero;
677 P.file_all_sources = 0;
678 P.file_active_sources = 0;
679 P.file_state = ft_state ft;
680 P.file_sources = None;
681 P.file_download_rate = 0.;
682 P.file_chunks = None;
683 P.file_chunk_size = None;
684 P.file_availability = [network.network_num, ""];
685 P.file_format = FormatNotComputed 0;
686 P.file_chunks_age = [| last_time () |];
687 P.file_age = 0;
688 P.file_last_seen = BasicSocket.last_time ();
689 P.file_priority = 0;
690 P.file_uids = [];
691 P.file_sub_files = [];
692 P.file_magic = None;
693 P.file_comments = [];
694 P.file_user = "";
695 P.file_group = "";
696 P.file_release = file_release (as_ft ft);
701 let load_torrent_string s user group =
702 if !verbose then lprintf_nl "load_torrent_string";
703 let file_id, torrent = BTTorrent.decode_torrent s in
705 (* Save the torrent, because we later want to put
706 it in the seeded directory. *)
707 let torrent_diskname = CommonFile.concat_file downloads_directory (torrent.torrent_name ^ ".torrent") in
708 if Sys.file_exists torrent_diskname then
709 begin
710 if !verbose then lprintf_nl "load_torrent_string: %s already exists, ignoring" torrent_diskname;
711 raise (Torrent_already_exists torrent.torrent_name)
712 end;
713 File.from_string torrent_diskname s;
715 if !verbose then
716 lprintf_nl "Starting torrent download with diskname: %s"
717 torrent_diskname;
718 let file = new_download file_id torrent torrent_diskname user group in
719 BTClients.talk_to_tracker file true;
720 CommonInteractive.start_download (file_find (file_num file));
721 file
723 let load_torrent_file filename user group =
724 if !verbose then
725 lprintf_nl "load_torrent_file %s" filename;
726 let s = File.to_string filename in
727 (* Delete the torrent if it is in the downloads dir. because it gets saved
728 again under the torrent name and we don't want to clutter up this dir. .*)
729 if Sys.file_exists filename
730 && (Filename.dirname filename) = downloads_directory then
731 Sys.remove filename;
732 ignore (load_torrent_string s user group)
735 let parse_tracker_reply file t filename =
736 (*This is the function which will be called by the http client
737 for parsing the response*)
738 (* Interested only in interval*)
739 if !verbose_msg_servers then lprintf_file_nl (as_file file) "Filename %s" filename;
740 let tracker_reply =
742 File.to_string filename
743 with e -> lprintf_file_nl (as_file file) "Empty reply from tracker"; ""
745 let v =
746 match tracker_reply with
747 | "" ->
748 if !verbose_connect then
749 lprintf_file_nl (as_file file) "Empty reply from tracker";
750 Bencode.decode ""
751 | _ -> Bencode.decode tracker_reply
753 if !verbose_msg_servers then lprintf_file_nl (as_file file) "Received: %s" (Bencode.print v);
754 t.tracker_interval <- 600;
755 match v with
756 Dictionary list ->
757 List.iter (fun (key,value) ->
758 match (key, value) with
759 String "interval", Int n ->
760 t.tracker_interval <- Int64.to_int n;
761 if !verbose_msg_servers then lprintf_file_nl (as_file file) ".. interval %d .." t.tracker_interval
762 | String "failure reason", String failure ->
763 lprintf_file_nl (as_file file) "Failure from Tracker in file: %s Reason: %s" file.file_name failure
764 (*TODO: merge with f from get_sources_from_tracker and parse the rest of the answer, too.
765 also connect to the sources we receive or instruct tracker to send none, perhaps based
766 on an config option. firewalled people could activate the option and then seed torrents, too.*)
767 | _ -> ()
768 ) list;
769 | _ -> assert false
772 let try_share_file torrent_diskname =
773 if !verbose_share then lprintf_nl "try_share_file: %s" torrent_diskname;
774 let s = File.to_string torrent_diskname in
775 let file_id, torrent = BTTorrent.decode_torrent s in
778 let filename =
779 let rec iter list =
780 match list with
781 [] -> raise Not_found
782 | sh :: tail ->
783 let s = sharing_strategy sh.shdir_strategy in
784 if match torrent.torrent_files with
785 [] -> not s.sharing_directories
786 | _ -> s.sharing_directories then
787 let filename =
788 Filename.concat sh.shdir_dirname torrent.torrent_name
790 if !verbose_share then lprintf_nl "Checking for %s" filename;
791 if Sys.file_exists filename then filename else
792 iter tail
793 else
794 iter tail
796 iter (shared_directories_including_user_commit ())
799 let user = CommonUserDb.admin_user () in
800 let file = new_file file_id torrent torrent_diskname
801 filename FileShared user user.user_default_group in
803 if !verbose_share then
804 lprintf_file_nl (as_file file) "Sharing file %s" filename;
805 BTClients.talk_to_tracker file false;
806 `Ok torrent_diskname
807 with
808 | Not_found ->
809 (* if the torrent is still there while the file is gone, remove the torrent *)
810 if !verbose_share then lprintf_nl "Removing torrent for %s" s;
811 let new_torrent_diskname =
812 Filename.concat old_directory
813 (Filename.basename torrent_diskname)
815 begin try
816 Unix2.rename torrent_diskname new_torrent_diskname;
817 `Ok new_torrent_diskname
818 with _ ->
819 let msg = Printf.sprintf "Failed to rename %S to %S" torrent_diskname new_torrent_diskname in
820 lprintf_nl "%s" msg;
821 `Err msg
823 | e ->
824 let msg = Printf.sprintf "Cannot share %S - exn %s" torrent_diskname (Printexc2.to_string e) in
825 lprintf_nl "%s" msg;
826 `Err msg
828 (* Call one minute after start, and then every 20 minutes. Should
829 automatically contact the tracker. *)
830 let share_files _ =
831 if !verbose_share then lprintf_nl "share_files";
832 List.iter (fun file ->
833 ignore (try_share_file (Filename.concat seeded_directory file))
834 ) (Unix2.list_directory seeded_directory);
835 let shared_files_copy = !current_files in
836 (* if the torrent is gone while the file is still shared, remove the share *)
837 List.iter (fun file ->
838 (* if !verbose_share then lprintf_nl "Checking torrent share for %s" file.file_torrent_diskname; *)
839 if not (Sys.file_exists file.file_torrent_diskname) &&
840 file_state file = FileShared then
841 begin
842 if !verbose_share then lprintf_nl "Removing torrent share for %s" file.file_torrent_diskname;
843 BTClients.file_stop file;
844 remove_file file;
845 BTClients.disconnect_clients file;
846 remove_all_clients file;
848 ) shared_files_copy
850 (** talk_to_tracker maintains timers and will connect to trackers only when allowed by rules *)
851 let announce_shared_files () =
852 List.iter (fun file -> if file_state file = FileShared then BTClients.talk_to_tracker file false) !current_files
854 let scan_new_torrents_directory () =
855 let filenames = Unix2.list_directory new_torrents_directory in
856 List.iter (fun file ->
857 let file = Filename.concat new_torrents_directory file in
858 let file_basename = Filename.basename file in
859 if not (Unix2.is_directory file) then
861 let file_owner = fst (Unix32.owner file) in
862 let user =
864 CommonUserDb.user2_user_find file_owner
865 with Not_found -> CommonUserDb.admin_user ()
867 load_torrent_file file user user.user_default_group;
868 (try Sys.remove file with _ -> ())
869 with
870 | e ->
871 Unix2.rename file (Filename.concat old_directory file_basename);
872 lprintf_nl "Error %s in scan_new_torrents_directory for %s, moved to torrents/old ..."
873 (Printexc2.to_string e) file_basename
874 ) filenames
876 let retry_all_ft () =
877 Hashtbl.iter (fun _ ft ->
878 try ft.ft_retry ft with e ->
879 lprintf_nl "ft_retry: exception %s" (Printexc2.to_string e)
880 ) ft_by_num
882 let load_torrent_from_web r user group ft =
883 let module H = Http_client in
884 H.wget r (fun filename ->
885 if ft_state ft = FileDownloading then begin
886 load_torrent_file filename user group;
887 file_cancel (as_ft ft) (CommonUserDb.admin_user ())
888 end)
890 let valid_torrent_extension url =
891 let ext = String.lowercase (Filename2.last_extension url) in
892 ext = ".torrent" || ext = ".tor"
894 let get_regexp_string text r =
895 ignore (Str.search_forward r text 0);
896 let a = Str.group_beginning 1 in
897 let b = Str.group_end 1 in
898 String.sub text a (b - a)
900 let op_network_parse_url url user group =
901 let location_regexp = "Location: \\(.*\\)" in
903 let real_url = get_regexp_string url (Str.regexp location_regexp) in
904 if (valid_torrent_extension real_url)
905 || (String2.contains url "Content-Type: application/x-bittorrent")
906 then (
907 let u = Url.of_string real_url in
908 let module H = Http_client in
909 let r = {
910 H.basic_request with
911 H.req_url = u;
912 H.req_proxy = !CommonOptions.http_proxy;
913 H.req_user_agent = get_user_agent ();
914 H.req_referer = (
915 let (rule_search,rule_value) =
916 try (List.find(fun (rule_search,rule_value) ->
917 Str.string_match (Str.regexp rule_search) real_url 0
918 ) !!referers )
919 with Not_found -> ("",real_url) in
920 Some (Url.of_string rule_value) );
921 H.req_headers = (try
922 let cookies = List.assoc u.Url.server !!cookies in
923 [ ( "Cookie", List.fold_left (fun res (key, value) ->
924 if res = "" then
925 key ^ "=" ^ value
926 else
927 res ^ "; " ^ key ^ "=" ^ value
928 ) "" cookies
930 with Not_found -> []);
931 H.req_max_retry = 10;
932 } in
934 let file_diskname = Filename.basename u.Url.short_file in
935 let ft = new_ft file_diskname user in
936 ft.ft_retry <- (load_torrent_from_web r user group);
937 load_torrent_from_web r user group ft;
938 "started download", true
940 else
941 "", false
942 with
943 | Not_found ->
944 if (valid_torrent_extension url) then
946 if !verbose then lprintf_nl "Not_found and trying to load %s" url;
948 load_torrent_file url user group;
949 "", true
950 with
951 Torrent_already_exists _ -> "A torrent with this name is already in the download queue", false
952 with e ->
953 lprintf_nl "Exception %s while 2nd loading" (Printexc2.to_string e);
954 let s = Printf.sprintf "Can not load load torrent file: %s"
955 (Printexc2.to_string e) in
956 s, false
957 else
958 begin
959 if !verbose then lprintf_nl "Not_found and url has non valid torrent extension: %s" url;
960 "Not_found and url has non valid torrent extension", false
962 | e ->
963 lprintf_nl "Exception %s while loading" (Printexc2.to_string e);
964 let s = Printf.sprintf "Can not load load torrent file: %s"
965 (Printexc2.to_string e) in
966 s, false
968 let op_client_info c =
969 check_client_country_code c;
970 let module P = GuiTypes in
971 let (ip,port) = c.client_host in
972 { (impl_client_info c.client_client) with
974 P.client_network = network.network_num;
975 P.client_kind = Known_location (ip,port);
976 P.client_country_code = c.client_country_code;
977 P.client_state = client_state (as_client c);
978 P.client_type = client_type c;
979 P.client_name = (Printf.sprintf "%s:%d" (Ip.to_string ip) port);
980 P.client_software = (brand_to_string c.client_brand);
981 P.client_release = c.client_release;
982 P.client_total_downloaded = c.client_total_downloaded;
983 P.client_total_uploaded = c.client_total_uploaded;
984 P.client_session_downloaded = c.client_session_downloaded;
985 P.client_session_uploaded = c.client_session_uploaded;
986 P.client_upload = Some (c.client_file.file_name);
987 P.client_connect_time = c.client_connect_time;
991 let op_client_connect c =
992 BTClients.connect_client c
994 let op_client_disconnect c=
995 BTClients.disconnect_client c Closed_by_user
997 let op_client_bprint c buf =
998 let cc = as_client c in
999 let cinfo = client_info cc in
1000 Printf.bprintf buf "%s (%s)\n"
1001 cinfo.GuiTypes.client_name
1002 (Sha1.to_string c.client_uid)
1004 let op_client_dprint c o file =
1005 let info = file_info file in
1006 let buf = o.conn_buf in
1007 let cc = as_client c in
1008 client_print cc o;
1009 Printf.bprintf buf (_b "\n%18sDown : %-10s Uploaded: %-10s Ratio: %s%1.1f (%s)\n") ""
1010 (Int64.to_string c.client_total_downloaded)
1011 (Int64.to_string c.client_total_uploaded)
1012 (if c.client_total_downloaded > c.client_total_uploaded then "-" else "+")
1013 (if c.client_total_uploaded > Int64.zero then
1014 Int64.to_float (c.client_total_downloaded // c.client_total_uploaded)
1015 else 1.)
1016 ("BT");
1017 (Printf.bprintf buf (_b "%18sFile : %s\n") "" info.GuiTypes.file_name)
1019 let op_client_dprint_html c o file str =
1020 let info = file_info file in
1021 let buf = o.conn_buf in
1022 let ac = as_client c in
1023 let cinfo = client_info ac in
1024 Printf.bprintf buf " \\<tr onMouseOver=\\\"mOvr(this);\\\"
1025 onMouseOut=\\\"mOut(this);\\\" class=\\\"%s\\\"\\>" str;
1027 let show_emulemods_column = ref false in
1028 if Autoconf.donkey = "yes" then begin
1029 if !!emule_mods_count then
1030 show_emulemods_column := true
1031 end;
1033 let cc,cn = Geoip.get_country_code_name cinfo.GuiTypes.client_country_code in
1035 html_mods_td buf ([
1036 ("", "srb ar", Printf.sprintf "%d" (client_num c));
1037 ((string_of_connection_state (client_state ac)), "sr",
1038 (short_string_of_connection_state (client_state ac)));
1039 ((Sha1.to_string c.client_uid), "sr", cinfo.GuiTypes.client_name);
1040 ("", "sr", (brand_to_string c.client_brand)); (* cinfo.GuiTypes.client_software *)
1041 ("", "sr", c.client_release);
1043 (if !show_emulemods_column then [("", "sr", "")] else [])
1045 ("", "sr", "F");
1046 ("", "sr ar", Printf.sprintf "%d"
1047 (((last_time ()) - cinfo.GuiTypes.client_connect_time) / 60));
1048 ("", "sr", "D");
1049 ("", "sr", "N");
1050 ("", "sr", (Ip.to_string (fst c.client_host)));
1051 ] @ (if Geoip.active () then [(cn, "sr", CommonPictures.flag_html cc)] else []) @ [
1052 ("", "sr ar", (size_of_int64 c.client_total_uploaded));
1053 ("", "sr ar", (size_of_int64 c.client_total_downloaded));
1054 ("", "sr ar", (size_of_int64 c.client_session_uploaded));
1055 ("", "sr ar", (size_of_int64 c.client_session_downloaded));
1056 ("", "sr", info.GuiTypes.file_name); ]);
1057 true
1059 let op_network_connected _ = true
1061 let compute_torrent filename announce comment =
1062 let announce = if announce = "" then BTTracker.get_default_tracker () else announce in
1063 if !verbose then lprintf_nl "compute_torrent: [%s] [%s] [%s]"
1064 filename announce comment;
1065 let basename = Printf.sprintf "%s.torrent" (Filename.basename filename) in
1066 let torrent = Filename.concat seeded_directory basename in
1067 let is_private = false in
1068 let file_id = BTTorrent.generate_torrent announce torrent comment is_private filename in
1069 match try_share_file torrent with
1070 | `Err msg -> failwith msg
1071 | `Ok torrent_path ->
1072 Filename.concat (Sys.getcwd ()) torrent_path,
1073 try `Ok (BTTracker.track_torrent basename file_id) with exn -> `Err (Printexc2.to_string exn)
1075 (* let text fmt = Printf.ksprintf (fun s -> `Text s) fmt *)
1077 OCaml 3.08.3 compatibility (ksprintf not available)
1078 http://mldonkey.sourceforge.net/phpBB2/viewtopic.php?p=30453
1080 let text s = `Text s
1081 let link name url = `Link (name,url)
1083 let output buf typ elements =
1084 let f = match typ with
1085 | HTML | XHTML | XML ->
1086 begin function
1087 | `Text s -> Xml.buffer_escape buf s
1088 | `Link (name,url) ->
1089 Printf.bprintf buf "<a href=\"%s\">%s</a>"
1090 (Xml.escape url) (Xml.escape (match name with "" -> url | s -> s))
1091 | `Break -> Buffer.add_string buf "<br/>"
1093 | TEXT | ANSI ->
1094 begin function
1095 | `Text s -> Buffer.add_string buf s
1096 | `Link ("",url) -> Printf.bprintf buf "%s" url
1097 | `Link (name,url) -> Printf.bprintf buf "%s <%s>" name url
1098 | `Break -> Buffer.add_string buf "\n"
1101 List.iter f elements
1103 (* dirty hack *)
1104 let output o l =
1105 match o.conn_output with
1106 | ANSI | TEXT -> output o.conn_buf o.conn_output l
1107 | HTML | XHTML | XML ->
1108 let buf = Buffer.create 1024 in
1109 output buf o.conn_output l;
1110 let s = Buffer.contents buf in
1111 for i = 0 to String.length s - 1 do
1112 begin match s.[i] with
1113 | '<' | '>' | '\\' | '"' | '&' -> Buffer.add_char o.conn_buf '\\'
1114 | _ -> () end;
1115 Buffer.add_char o.conn_buf s.[i]
1116 done
1118 let commands =
1121 "compute_torrent", "Network/Bittorrent", Arg_multiple (fun args o ->
1122 output o
1123 begin try
1124 let filename = ref "" in
1125 let comment = ref "" in
1126 (match args with
1127 fname :: [comm] -> filename := fname; comment := comm
1128 | [fname] -> filename := fname
1129 | _ -> raise Not_found);
1131 let (path,url) = compute_torrent !filename "" !comment in
1133 text (Printf.sprintf "Torrent file generated : %s" path);
1134 `Break;
1135 (match url with
1136 | `Ok url -> link "Download" url
1137 | `Err s -> text (Printf.sprintf "Not tracked : %s" s));
1138 `Break
1140 with
1141 | Not_found -> [text "Not enough parameters"; `Break]
1142 | exn -> [text (Printf.sprintf "Error: %s" (Printexc2.to_string exn)); `Break]
1143 end;
1145 ), _s "<filename> [<comment>] :\tgenerate the corresponding <filename> .torrent file with <comment>.\n\t\t\t\t\t\tThe file is automatically tracked if tracker is enabled and seeded if located in incoming/";
1147 "torrents", "Network/Bittorrent", Arg_none (fun o ->
1148 output o
1149 begin try
1150 BTTracker.check_tracker ();
1151 let files_tracked = Unix2.list_directory tracked_directory in
1152 let files_downloading = Unix2.list_directory downloads_directory in
1153 let files_seeded = Unix2.list_directory seeded_directory in
1154 let files_old = Unix2.list_directory old_directory in
1155 let all_torrents_files = files_tracked @ files_downloading @ files_seeded @ files_old in
1157 let l = List.map (fun file -> [link file (BTTracker.tracker_url file); `Break]) all_torrents_files in
1159 (`Text (_s ".torrent files available:")) :: `Break :: List.flatten l
1160 with
1161 exn ->
1162 [`Text (Printexc2.to_string exn); `Break]
1163 end;
1164 _s ""
1165 ), _s ":\t\t\t\tprint all .torrent files on this server";
1167 "print_torrent", "Network/Bittorrent", Arg_one (fun arg o ->
1168 if CommonUserDb.user2_is_admin o.conn_user.ui_user then begin
1169 let file =
1171 Some (as_file_impl (file_find (int_of_string arg)))
1172 with _ -> None
1174 match file with
1175 | None -> Printf.sprintf "file %s not found" arg
1176 | Some file ->
1178 if use_html_mods o then begin
1179 html_mods_cntr_init ();
1180 html_mods_table_header o.conn_buf "sourcesInfo" "sourcesInfo" [
1181 ( Str, "srh br", "File Info", "Info" ) ;
1182 ( Str, "srh", "Value", "Value" ) ]
1183 end;
1184 op_file_print file.impl_file_val o;
1185 if use_html_mods o then begin
1186 Printf.bprintf o.conn_buf "\\</tr\\>\\</table\\>\\</div\\>";
1187 Printf.bprintf o.conn_buf "\\</td\\>\\</tr\\>\\</table\\>\\</div\\>\\<br\\>"
1191 end else
1192 begin print_command_result o "You are not allowed to use print_torrent";
1193 "" end
1194 ), _s "<num> :\t\t\tshow internal data of .torrent file";
1196 "seeded_torrents", "Network/Bittorrent", Arg_none (fun o ->
1197 if CommonUserDb.user2_is_admin o.conn_user.ui_user then begin
1198 List.iter (fun file ->
1199 if file_state file = FileShared then
1200 Printf.bprintf o.conn_buf "%s [U %Ld u/d %Ld/%Ld]\n"
1201 file.file_name file.file_uploaded file.file_session_uploaded file.file_session_downloaded
1202 ) !current_files;
1203 _s "done"
1204 end else
1205 begin print_command_result o "You are not allowed to use seeded_torrents";
1206 "" end
1207 ), _s ":\t\t\tprint all seeded .torrent files on this server (output: name, total upload, session upload, session download)";
1209 "reshare_torrents", "Network/Bittorrent", Arg_none (fun o ->
1210 share_files ();
1211 _s "done"
1212 ), _s ":\t\t\trecheck torrents/* directories for changes";
1214 "rm_old_torrents", "Network/Bittorrent", Arg_none (fun o ->
1215 let files_outdated = Unix2.list_directory old_directory in
1216 let buf = o.conn_buf in
1217 if o.conn_output = HTML then begin
1218 (* TODO: really htmlize it *)
1219 Printf.bprintf buf "Removing old torrents...";
1220 List.iter (fun file ->
1221 Printf.bprintf buf "%s "
1222 file;
1223 ) files_outdated
1225 else begin
1226 Printf.bprintf buf "Removing old torrents...\n";
1227 List.iter (fun file ->
1228 Printf.bprintf buf "%s\n"
1229 file
1230 ) files_outdated;
1231 end;
1232 List.iter (fun file ->
1233 Sys.remove (Filename.concat old_directory file)
1234 ) files_outdated;
1235 _s ""
1236 ), _s ":\t\t\tremove all old .torrent files";
1238 "startbt", "Network/Bittorrent", Arg_one (fun url o ->
1239 let buf = o.conn_buf in
1240 if Sys.file_exists url then
1241 begin
1242 load_torrent_file url o.conn_user.ui_user o.conn_user.ui_user.user_default_group;
1243 Printf.bprintf buf "loaded file %s\n" url
1245 else
1246 begin
1247 let url = "Location: " ^ url ^ "\nContent-Type: application/x-bittorrent" in
1248 let result = fst (op_network_parse_url url o.conn_user.ui_user o.conn_user.ui_user.user_default_group) in
1249 Printf.bprintf buf "%s\n" result
1250 end;
1251 _s ""
1252 ), "<url|file> :\t\t\tstart BT download";
1254 "stop_all_bt", "Network/Bittorrent", Arg_none (fun o ->
1255 List.iter (fun file -> BTClients.file_stop file ) !current_files;
1256 let buf = o.conn_buf in
1257 if o.conn_output = HTML then
1258 (* TODO: really htmlize it *)
1259 Printf.bprintf buf "started sending stops..."
1260 else
1261 Printf.bprintf buf "started sending stops...\n";
1262 _s ""
1263 ), _s ":\t\t\t\tstops all bittorrent downloads, use this if you want to make sure that the stop signal actually\n\t\t\t\t\tgets to the tracker when shutting mlnet down, but you have to wait till the stops get to the\n\t\t\t\t\ttracker and not wait too long, so mldonkey reconnects to the tracker :)";
1265 "tracker", "Network/Bittorrent", Arg_multiple (fun args o ->
1267 let num = ref "" in
1268 let urls = ref [] in
1269 (match args with
1270 | nums :: [] -> raise Not_found
1271 | nums :: rest -> num := nums; urls := rest
1272 | _ -> raise Not_found);
1274 let num = int_of_string !num in
1275 Hashtbl.iter (fun _ file ->
1276 if file_num file = num then begin
1277 if !verbose then
1278 lprintf_file_nl (as_file file) "adding trackers for file %i" num;
1279 set_trackers file !urls;
1280 raise Exit
1282 ) files_by_uid;
1283 let buf = o.conn_buf in
1284 if o.conn_output = HTML then
1285 html_mods_table_one_row buf "serversTable" "servers" [
1286 ("", "srh", "file not found"); ]
1287 else
1288 Printf.bprintf buf "file not found";
1289 _s ""
1290 with
1291 | Exit ->
1292 let buf = o.conn_buf in
1293 if o.conn_output = HTML then
1294 html_mods_table_one_row buf "serversTable" "servers" [
1295 ("", "srh", "tracker added"); ]
1296 else
1297 Printf.bprintf buf "tracker added";
1298 _s ""
1299 | _ ->
1300 if !verbose then
1301 lprintf_nl "Not enough or wrong parameters.";
1302 let buf = o.conn_buf in
1303 if o.conn_output = HTML then
1304 html_mods_table_one_row buf "serversTable" "servers" [
1305 ("", "srh", "Not enough or wrong parameters."); ]
1306 else
1307 Printf.bprintf buf "Not enough or wrong parameters.";
1308 _s ""
1309 ), "<num> <url> <url>... :\t\tadd URLs as trackers for num";
1311 (* TODO : add some code from make_torrent
1312 "print_torrent", Arg_one (fun filename o ->
1314 ".torrent file printed"
1315 ), "<filename.torrent> :\t\tprint the content of filename"
1320 open LittleEndian
1321 open GuiDecoding
1323 let op_gui_message s user =
1324 match get_int16 s 0 with
1325 0 ->
1326 let text = String.sub s 2 (String.length s - 2) in
1327 if !verbose then lprintf_nl "received torrent from gui...";
1328 (try
1329 let file = load_torrent_string text user user.user_default_group in
1330 raise (Torrent_started file.file_name)
1331 with e -> (match e with
1332 | Torrent_already_exists s -> lprintf_nl "Loading torrent from GUI: torrent %s is already in download queue" s
1333 | _ -> ());
1334 raise e)
1335 | 1 -> (* 34+ *)
1336 let n = get_int s 2 in
1337 let a, pos = get_string s 6 in
1338 let c, pos = get_string s pos in
1339 let sf = CommonShared.shared_find n in
1340 let f = shared_fullname sf in
1341 ignore (compute_torrent f a c)
1342 | opcode -> failwith (Printf.sprintf "[BT] Unknown message opcode %d" opcode)
1344 let _ =
1346 ft_ops.op_file_cancel <- op_ft_cancel;
1347 ft_ops.op_file_commit <- op_ft_commit;
1348 ft_ops.op_file_info <- op_ft_info;
1349 ft_ops.op_file_active_sources <- (fun _ -> []);
1350 ft_ops.op_file_all_sources <- (fun _ -> []);
1352 file_ops.op_file_all_sources <- op_file_all_sources;
1353 file_ops.op_file_files <- op_file_files;
1354 file_ops.op_file_active_sources <- op_file_active_sources;
1355 file_ops.op_file_debug <- op_file_debug;
1356 file_ops.op_file_commit <- op_file_commit;
1357 file_ops.op_file_print <- op_file_print;
1358 file_ops.op_file_print_sources <- op_file_print_sources;
1359 file_ops.op_file_check <- op_file_check;
1360 file_ops.op_file_cancel <- op_file_cancel;
1361 file_ops.op_file_info <- op_file_info;
1362 file_ops.op_file_save_as <- (fun file name -> ());
1363 file_ops.op_file_shared <- (fun file ->
1364 match file.file_shared with
1365 None -> None
1366 | Some sh -> Some (as_shared sh)
1368 file_ops.op_file_download_order <- (fun file strategy ->
1369 match file.file_swarmer with
1370 | None -> None
1371 | Some s ->
1372 (match strategy with
1373 (* return current strategy *)
1374 | None -> Some (CommonSwarming.get_strategy s)
1375 | Some strategy -> CommonSwarming.set_strategy s strategy;
1376 Some (CommonSwarming.get_strategy s))
1379 network.op_network_gui_message <- op_gui_message;
1380 network.op_network_connected <- op_network_connected;
1381 network.op_network_parse_url <- op_network_parse_url;
1382 network.op_network_share <- (fun fullname codedname size -> ());
1383 network.op_network_close_search <- (fun s -> ());
1384 network.op_network_forget_search <- (fun s -> ());
1385 network.op_network_connect_servers <- (fun s -> ());
1386 network.op_network_search <- (fun ss buf -> ());
1387 network.op_network_download <- (fun r user group -> dummy_file);
1388 network.op_network_recover_temp <- (fun s -> ());
1389 let clean_exit_started = ref false in
1390 network.op_network_clean_exit <- (fun s ->
1391 if not !clean_exit_started then
1392 begin
1393 List.iter (fun file -> BTClients.file_stop file) !current_files;
1394 clean_exit_started := true;
1395 end;
1396 List.for_all (fun file -> not file.file_tracker_connected) !current_files;
1398 network.op_network_reset <- (fun _ ->
1399 List.iter (fun file -> BTClients.file_stop file) !current_files);
1400 network.op_network_ports <- (fun _ ->
1402 !!client_port, "client_port TCP";
1403 !!BTTracker.tracker_port, "tracker_port TCP";
1404 ] @ (match !bt_dht with None -> [] | Some dht -> [dht.BT_DHT.M.dht_port,"dht_port UDP"]));
1405 network.op_network_porttest_result <- (fun _ -> !porttest_result);
1406 network.op_network_porttest_start <- (fun _ ->
1407 azureus_porttest_random := (Random.int 100000);
1408 let tests = [
1409 Printf.sprintf "http://www.utorrent.com/testport?port=%d" !!client_port, interpret_utorrent_porttest;
1410 Printf.sprintf "http://azureus.aelitis.com/natcheck.php?port=%d&check=azureus_rand_%d"
1411 !!client_port !azureus_porttest_random, interpret_azureus_porttest;
1412 ] in
1413 perform_porttests tests
1415 network.op_network_check_upload_slots <- (fun _ -> check_bt_uploaders ());
1416 client_ops.op_client_info <- op_client_info;
1417 client_ops.op_client_connect <- op_client_connect;
1418 client_ops.op_client_disconnect <- op_client_disconnect;
1419 client_ops.op_client_bprint <- op_client_bprint;
1420 client_ops.op_client_dprint <- op_client_dprint;
1421 client_ops.op_client_dprint_html <- op_client_dprint_html;
1422 client_ops.op_client_browse <- (fun _ _ -> ());
1423 client_ops.op_client_files <- (fun _ -> []);
1424 client_ops.op_client_clear_files <- (fun _ -> ());
1426 CommonNetwork.register_commands commands;
1428 shared_ops.op_shared_state <- (fun file o ->
1429 if o.conn_output = HTML then
1430 Printf.sprintf "\\<a href=\\\"submit?q=print_torrent+%d\\\"\\>Details\\</a\\>"
1431 (file_num file)
1432 else Printf.sprintf "Shared using %s" file.file_torrent_diskname
1434 shared_ops.op_shared_unshare <- (fun file ->
1435 (if !verbose_share then lprintf_file_nl (as_file file) "unshare file");
1436 BTGlobals.unshare_file file);
1437 shared_ops.op_shared_info <- (fun file ->
1438 let module T = GuiTypes in
1439 match file.file_shared with
1440 None -> assert false
1441 | Some impl ->
1442 { (impl_shared_info impl) with
1443 T.shared_network = network.network_num;
1444 T.shared_filename = file_best_name (as_file file);
1445 T.shared_uids = [Uid.create (Sha1 file.file_id)];
1446 T.shared_sub_files = file.file_files;