patch #7372
[mldonkey.git] / src / networks / donkey / donkeyComplexOptions.ml
blob36147930eace137e24bff9aa380bbdc2f9be0a01
1 (* Copyright 2002 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 Printf2
21 open BasicSocket
22 open Md4
23 open Options
25 open CommonDownloads
26 open CommonSwarming
27 open CommonClient
28 open CommonServer
29 open CommonComplexOptions
30 open CommonFile
31 open CommonTypes
32 open CommonOptions
33 open CommonGlobals
34 open CommonResult
36 open DonkeyTypes
37 open DonkeyOptions
38 open DonkeyGlobals
40 module VB = VerificationBitmap
42 let shared_files_ini = create_options_file "shared_files_new.ini"
44 let file_sources_ini = create_options_file "file_sources.ini"
46 let stats_ini = create_options_file "stats.ini"
48 let mod_stats_ini = create_options_file "stats_mod.ini"
50 let shared_section = file_section shared_files_ini [] ""
51 let stats_section = file_section stats_ini [] ""
52 let mod_stats_section = file_section mod_stats_ini [] ""
53 let file_sources_section = file_section file_sources_ini [] ""
57 (* emulate emule onlinesig.dat
59 <connected (0|1)> | <server name> | <ip> | <port#>
60 <downloadrate %.1f> | <uploadrate %.1f>| <queuesize int>
62 I know this is stupid, but "give the people what they want"..
66 let create_online_sig () =
68 let most_users = ref Int64.zero in
69 let server_name= ref "" in
70 let server_ip = ref "" in
71 let server_port = ref 0 in
72 List.iter (fun s ->
73 match s.server_nusers with
74 | Some v when v > !most_users ->
75 server_name := s.server_name;
76 server_ip := (Ip.to_string s.server_ip);
77 server_port := s.server_port;
78 most_users := (match s.server_nusers with None -> 0L | Some v -> v)
79 | _ -> ()
80 ) (connected_servers());
82 Unix2.tryopen_write "onlinesig.dat" (fun oc ->
84 if !most_users = Int64.zero then
85 output_string oc ("0\n")
86 else
87 output_string oc (Printf.sprintf "1|%s|%s|%d\n" !server_name !server_ip !server_port);
88 let dlkbs = (( (float_of_int !udp_download_rate) +. (float_of_int !control_download_rate)) /. 1024.0) in
89 let ulkbs = (( (float_of_int !udp_upload_rate) +. (float_of_int !control_upload_rate)) /. 1024.0) in
91 output_string oc (Printf.sprintf "%.1f|%.1f|%d\n" dlkbs ulkbs
92 (Intmap.length !CommonUploads.pending_slots_map)))
94 (************ COMPLEX OPTIONS *****************)
96 let value_to_addr v =
97 match v with
98 List [v1;v2] | SmallList [v1;v2] ->
99 (Ip.of_string (value_to_string v1), value_to_int v2)
100 | _ -> failwith "Options: Not an int32 pair"
102 let addr_to_value ip port =
103 SmallList [string_to_value (Ip.to_string ip); int_to_value port]
105 let value_to_md4 v =
106 Md4.of_string (value_to_string v)
108 let value_to_client is_friend assocs =
109 let get_value name conv = conv (List.assoc name assocs) in
110 let (ip,port) =
112 get_value "client_addr" (fun v ->
113 match v with
114 List [ip;port] | SmallList [ip;port] ->
115 let ip = Ip.of_string (value_to_string ip) in
116 let port = value_to_int port in
117 (ip, port)
118 | _ -> failwith "Options: Not an client option"
120 with _ ->
121 failwith "Source without address: removed"
124 let _ =
126 let last_conn =
127 (min (get_value "client_age" value_to_int)
128 (BasicSocket.last_time ()))
130 let last_conn = normalize_time last_conn in
131 last_conn
132 with _ -> 0 in
133 let cc = Geoip.get_country_code_option ip in
134 let l = DonkeyGlobals.new_client (Direct_address (ip,port)) cc in
136 let md4 = try
137 get_value "client_md4" value_to_md4
138 with _ -> Md4.null
140 let name = try
141 get_value "client_name" value_to_string with _ -> "" in
142 set_client_name l name md4;
144 if is_friend then friend_add l;
147 let value_to_donkey_client v =
148 match v with
149 List [ip;port] | SmallList [ip;port] ->
150 let ip = Ip.of_string (value_to_string ip) in
151 let port = value_to_int port in
152 let cc = Geoip.get_country_code_option ip in
153 DonkeyGlobals.new_client (Direct_address (ip, port)) cc
154 | Module assocs ->
155 value_to_client false assocs
156 | _ -> assert false
158 let client_to_value c =
159 let list = [
160 "client_md4", string_to_value (Md4.to_string c.client_md4);
161 "client_name", string_to_value c.client_name;
165 match c.client_kind with
166 Direct_address (ip, port) ->
167 ("client_addr", addr_to_value ip port) :: list
168 | _ -> raise Exit (* Don't save these options *)
170 let donkey_client_to_value c =
171 Options.Module (client_to_value c)
173 let client_option =
174 define_option_class "Client" value_to_donkey_client
175 donkey_client_to_value
177 let value_to_server assocs =
178 let get_value name conv = conv (List.assoc name assocs) in
179 let ip, port = get_value "server_addr" (fun v ->
180 match v with
181 List [ip;port] | SmallList [ip;port] ->
182 let ip = Ip.of_string (value_to_string ip) in
183 let port = value_to_int port in
184 ip, port
185 | _ -> failwith "Options: Not an server option"
186 ) in
187 let l = DonkeyGlobals.new_server ip port in
189 (try
190 l.server_name <- get_value "server_name" value_to_string
191 with _ -> ());
192 (try
193 l.server_preferred <- get_value "server_preferred" value_to_bool
194 with _ -> ());
195 (try
196 connection_set_last_conn l.server_connection_control
197 (normalize_time (min (get_value "server_age" value_to_int)
198 (BasicSocket.last_time ())));
199 with _ -> ());
200 as_server l.server_server
202 let server_to_value c =
203 let fields =
205 "server_addr", addr_to_value c.server_ip c.server_port;
206 "server_age", int_to_value (
207 connection_last_conn c.server_connection_control);
211 let fields =
212 if c.server_name <> "" then
213 ("server_name", string_to_value c.server_name) :: fields
214 else fields in
216 let fields =
217 if c.server_preferred then
218 ("server_preferred", bool_to_value true) :: fields else
219 fields in
220 List.rev fields
222 let value_to_int32pair v =
223 match v with
224 List [v1;v2] | SmallList [v1;v2] ->
225 (value_to_int64 v1, value_to_int64 v2)
226 | _ ->
227 failwith "Options: Not an int32 pair"
229 let value_to_file file_size file_state user group assocs =
230 let get_value name conv = conv (List.assoc name assocs) in
232 let file_md4 =
234 get_value "file_md4" value_to_string
235 with _ -> failwith "Bad file_md4"
237 let file_diskname, empty =
238 let filename =
240 get_value "file_diskname" value_to_string
241 with _ ->
242 let filename = Filename.concat !!temp_directory file_md4 in
243 lprintf_nl "getting file_diskname from ini failed, testing for ed2k-temp-file %s" filename;
244 if Sys.file_exists filename then
245 filename
246 else
247 begin
248 let filename =
249 Filename.concat
250 !!temp_directory
251 ( string_of_uid ( Ed2k (Md4.of_string file_md4) ) )
253 lprintf_nl "getting file_diskname from ini failed, testing for ed2k-temp-file %s"
254 filename;
255 if Sys.file_exists filename then
256 filename
257 else
258 Filename.concat
259 !!temp_directory
260 ( file_string_of_uid ( Ed2k (Md4.of_string file_md4) ) )
263 let file_exists filename = Sys.file_exists filename in
264 if not (file_exists filename) then
265 (* I think we should die here, to prevent any corruption. *)
266 lprintf_nl "Error: temp file %s not found, re-creating empty one" filename;
268 if !verbose && (not !CommonGlobals.is_startup_phase) then
269 lprintf_nl "ed2k-temp-file %s used." filename;
271 filename, not (file_exists filename)
274 let file = DonkeyGlobals.new_file file_diskname file_state
275 (Md4.of_string file_md4) file_size "" true user group in
277 (try
278 set_file_best_name (as_file file)
279 (get_value "file_filename" value_to_string) "" 0
280 with _ -> update_best_name file);
282 let md4s = try get_value "file_md4s" (value_to_array value_to_md4)
283 with _ -> [||] in
285 if md4s <> [||] then
286 begin
287 if md4_of_array md4s <> (Md4.of_string file_md4) ||
288 Array.length md4s <> file.file_nchunk_hashes then
289 lprintf_nl "discarding partial chunks hashes, computed hash is wrong for %s"
290 (file_best_name file)
291 else
292 file.file_computed_md4s <- md4s
293 end;
295 (match file.file_swarmer with
296 None -> ()
297 | Some swarmer ->
298 CommonSwarming.value_to_frontend swarmer assocs;
299 CommonSwarming.set_verifier swarmer (if md4s = [||] then
300 VerificationNotAvailable
301 else
302 Verification
303 (Array.map (fun md4 -> Ed2k md4) md4s));
304 if empty then
305 begin
306 lprintf_nl "re-created missing temp file of %s , resetting chunk status to missing"
307 (file_best_name file);
308 let ver_str = String.make (Array.length md4s) (VB.state_to_char VB.State_missing) in
309 CommonSwarming.set_chunks_verified_bitmap swarmer (VB.of_string ver_str);
312 as_file file
314 let file_to_value file =
315 let fields =
317 "file_md4", string_to_value (Md4.to_string file.file_md4);
318 "file_diskname", string_to_value file.file_diskname;
319 ("file_md4s",
320 array_to_value Md4.hash_to_value file.file_computed_md4s);
323 let fields =
324 match file.file_swarmer with
325 None -> fields
326 | Some swarmer ->
327 CommonSwarming.frontend_to_value swarmer fields
329 fields
331 module SharedFileOption = struct
333 let value_to_shinfo v =
334 match v with
335 Options.Module assocs ->
336 let sh_md4s = try
337 value_to_array (fun v ->
338 Md4.of_string (value_to_string v)) (List.assoc "md4s" assocs)
339 with _ -> failwith "Bad shared file md4"
341 let sh_size = try
342 value_to_int64 (List.assoc "size" assocs)
343 with _ -> failwith "Bad shared file size"
345 let sh_name = try
346 value_to_filename (List.assoc "name" assocs)
347 with _ -> failwith "Bad shared file name"
349 let sh_mtime = try
350 value_to_float (List.assoc "mtime" assocs)
351 with _ -> failwith "Bad shared file mtime"
353 { sh_name = sh_name; sh_mtime = sh_mtime;
354 sh_size = sh_size; sh_md4s = sh_md4s;
357 | _ -> failwith "Options: not a shared file info option"
359 let shinfo_to_value sh =
360 Options.Module [
361 "name", filename_to_value sh.sh_name;
362 "md4s", array_to_value Md4.hash_to_value sh.sh_md4s;
363 "mtime", float_to_value sh.sh_mtime;
364 "size", int64_to_value sh.sh_size;
368 let t = define_option_class "SharedFile" value_to_shinfo shinfo_to_value
371 module StatsOption = struct
373 let value_to_stat v =
374 match v with
375 Options.Module assocs ->
377 brand_seen = value_to_int (List.assoc "seen" assocs);
378 brand_banned = value_to_int (List.assoc "banned" assocs);
379 brand_filerequest = value_to_int (List.assoc "filereqs" assocs);
380 brand_download = value_to_int64 (List.assoc "download" assocs);
381 brand_upload = value_to_int64 (List.assoc "upload" assocs);
384 | _ -> failwith "Options: not a stat option"
386 let stat_to_value b =
387 Options.Module [
388 "seen", int_to_value b.brand_seen;
389 "banned", int_to_value b.brand_banned;
390 "filereqs", int_to_value b.brand_filerequest;
391 "download", int64_to_value b.brand_download;
392 "upload", int64_to_value b.brand_upload;
396 let t = define_option_class "Stat" value_to_stat stat_to_value
399 module ModStatsOption = struct
401 let value_to_mod_stat v =
402 match v with
403 Options.Module assocs ->
405 brand_seen = value_to_int (List.assoc "mseen" assocs);
406 brand_banned = value_to_int (List.assoc "mbanned" assocs);
407 brand_filerequest = value_to_int (List.assoc "mfilereqs" assocs);
408 brand_download = value_to_int64 (List.assoc "mdownload" assocs);
409 brand_upload = value_to_int64 (List.assoc "mupload" assocs);
412 | _ -> failwith "Options: not a mod_stat option"
414 let stat_to_mod_value b =
415 Options.Module [
416 "mseen", int_to_value b.brand_seen;
417 "mbanned", int_to_value b.brand_banned;
418 "mfilereqs", int_to_value b.brand_filerequest;
419 "mdownload", int64_to_value b.brand_download;
420 "mupload", int64_to_value b.brand_upload;
424 let t = define_option_class "ModStat" value_to_mod_stat stat_to_mod_value
427 let value_to_module f v =
428 match v with
429 Module list -> f list
430 | _ -> failwith "Option should be a module"
432 let save_time = define_header_option file_sources_ini
433 ["save_time"] "" int_option (last_time ())
435 let known_shared_files = define_option shared_section
436 ["shared_files"] ""
437 (list_option SharedFileOption.t) []
439 (************ UPDATE OPTIONS *************)
441 (* This function is used only with the "n" command and the
442 op_network_add_server method, which are the only ways for the user
443 to enter servers, bypassing the update_server_list variable. *)
445 let force_add_server ip port =
447 DonkeyGlobals.find_server ip port
448 with _ ->
449 let s = DonkeyGlobals.new_server ip port in
452 let check_add_server ip port =
453 if Ip.usable ip &&
454 not (is_black_address ip port None) && port <> 4662 then
455 force_add_server ip port
456 else raise Not_found
458 let safe_add_server ip port =
459 if Ip.usable ip &&
460 not (is_black_address ip port None) && port <> 4662 then
462 ignore (DonkeyGlobals.find_server ip port)
463 with _ ->
464 ignore (DonkeyGlobals.new_server ip port)
466 let config_files_loaded = ref false
468 let load _ =
469 if !verbose then lprintf_nl "Loading shared files";
470 (try
471 Options.load shared_files_ini;
472 Options.load stats_ini;
473 Options.load mod_stats_ini;
474 with Sys_error _ ->
475 Options.save_with_help shared_files_ini);
476 config_files_loaded := true
478 let guptime = define_option stats_section ["guptime"] "" int_option 0
480 let new_stats_array () =
481 Array.init brand_count (fun _ ->
482 { dummy_stats with brand_seen = 0 }
485 let new_mod_stats_array () =
486 Array.init brand_mod_count (fun _ ->
487 { dummy_stats with brand_seen = 0 }
490 let gstats_array = define_option stats_section ["stats"] ""
491 (array_option StatsOption.t) (new_stats_array ())
493 let gstats_mod_array = define_option mod_stats_section ["stats"] ""
494 (array_option ModStatsOption.t) (new_mod_stats_array ())
496 let _ =
497 option_hook gstats_array (fun _ ->
498 let old_stats = !!gstats_array in
499 let old_len = Array.length old_stats in
500 if old_len <> brand_count then
501 let t = new_stats_array () in
502 for i = 0 to old_len - 1 do
503 t.(i) <- old_stats.(i)
504 done;
505 gstats_array =:= t
508 let _ =
509 option_hook gstats_mod_array (fun _ ->
510 let old_mod_stats = !!gstats_mod_array in
511 let old_mod_len = Array.length old_mod_stats in
512 if old_mod_len <> brand_mod_count then
513 let t = new_mod_stats_array () in
514 for i = 0 to old_mod_len - 1 do
515 t.(i) <- old_mod_stats.(i)
516 done;
517 gstats_mod_array =:= t
520 let diff_time = ref 0
522 let sources_loaded = ref false (* added 2.5.24 *)
524 let save _ =
525 if !config_files_loaded then begin
526 Options.save_with_help shared_files_ini;
527 guptime =:= !!guptime + (last_time () - start_time) - !diff_time;
528 diff_time := (last_time () - start_time);
529 Options.save_with_help stats_ini;
530 Options.save_with_help mod_stats_ini;
531 create_online_sig ()
534 let save_sources _ =
535 if !sources_loaded && !!keep_sources then begin
536 save_time =:= last_time ();
537 let cleaner = DonkeySources.attach_sources_to_file file_sources_section in
538 Options.save_with_help file_sources_ini;
539 cleaner ()
542 let guptime () = !!guptime - !diff_time
544 let load_sources () =
545 if not !!keep_sources then () else
546 (try
547 let cleaner = DonkeySources.attach_sources_to_file file_sources_section in
548 cleaner ();
549 Options.load file_sources_ini;
550 cleaner ();
551 sources_loaded := true;
552 lprintf_nl "loading sources completed"
553 with _ -> ())
555 let check_result r tags =
556 if r.result_names = [] || r.result_size = Int64.zero then begin
557 if !verbose then begin
558 lprintf_n "Bad search result: ";
559 List.iter (fun tag ->
560 lprintf "[%s] = [%s] " (string_of_field tag.tag_name)
561 (string_of_tag_value tag.tag_value)
562 ) tags;
563 lprint_newline ();
564 end;
565 false
567 else true
569 (* Inserted in complexOptions to be able to access old_files (ugly) *)
570 let result_of_file md4 tags =
571 let rec r = { dummy_result with
572 result_uids = [Uid.create (Ed2k md4)];
573 result_done = (List.mem md4 !!old_files) || (Hashtbl.mem files_by_md4 md4);
574 result_source_network = network.network_num;
575 } in
576 List.iter (fun tag ->
577 match tag with
578 { tag_name = Field_Filename; tag_value = String s } ->
579 r.result_names <- s :: r.result_names
580 | { tag_name = Field_Size; tag_value = (Uint64 v| Fint64 v) } ->
581 r.result_size <- v;
582 | { tag_name = Field_Size; tag_value = (Uint16 v| Uint8 v) } ->
583 r.result_size <- Int64.of_int v;
584 | { tag_name = Field_Size_Hi; tag_value = Uint8 v } ->
585 r.result_size <- Int64.logor r.result_size (Int64.shift_left (Int64.of_int v) 32);
586 | { tag_name = Field_Format; tag_value = String s } ->
587 r.result_tags <- tag :: r.result_tags;
588 r.result_format <- s
589 | { tag_name = Field_Type; tag_value = String s } ->
590 r.result_tags <- tag :: r.result_tags;
591 r.result_type <- s
592 | _ ->
593 r.result_tags <- tag :: r.result_tags
594 ) tags;
595 if check_result r tags then
596 let rs = update_result_num r in
597 Some rs
598 else None
601 let _ =
602 network.op_network_file_of_option <- value_to_file;
603 file_ops.op_file_to_option <- file_to_value;
605 network.op_network_server_of_option <- value_to_server;
606 server_ops.op_server_to_option <- server_to_value;
608 network.op_network_client_of_option <- (fun is_friend l ->
609 as_client (value_to_client is_friend l));
610 client_ops.op_client_to_option <- client_to_value;
612 network.op_network_load_complex_options <- load;
613 network.op_network_save_complex_options <- save;
614 network.op_network_save_sources <- save_sources;
616 let cleaner = DonkeySources.attach_sources_to_file file_sources_section in
617 cleaner ()