1 (* Copyright 2001, 2002 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
28 open TcpBufferedSocket
32 open CommonComplexOptions
41 open CommonInteractive
47 let log_prefix = "[dcGlo]"
50 lprintf_nl2
log_prefix fmt
52 exception Found_client
of DcTypes.client
53 exception Found_server
of DcTypes.server
54 exception Found_file
of DcTypes.file
55 exception Found_user
of DcTypes.user
56 exception Wrong_file_size
of Int64.t
* Int64.t
59 let network = new_network
"DC" "Direct Connect"
68 let connection_manager = network.network_connection_manager
70 let (server_ops
: server
CommonServer.server_ops
) =
71 CommonServer.new_server_ops
network
73 let (user_ops
: user
CommonUser.user_ops
) =
74 CommonUser.new_user_ops
network
76 let (file_ops
: file
CommonFile.file_ops
) =
77 CommonFile.new_file_ops
network
79 let (client_ops
: client
CommonClient.client_ops
) =
80 CommonClient.new_client_ops
network
82 let (shared_ops
: CommonUploads.shared_file
CommonShared.shared_ops
) =
83 CommonShared.new_shared_ops
network
85 let once_create_filelist = ref false (* filelist creation one time on start *)
86 let once_connect_to_servers = ref false (* autoconnection done to servers on start *)
88 let file_disk_name file
= file_disk_name (as_file file
.file_file
)
89 let dc_tcp_listen_sock = ref (None
: TcpServerSocket.t
option)
90 let dc_udp_sock = ref (None
: UdpSocket.t
option)
91 (*let nservers = ref 0 *) (* connected servers count *)
92 let connected_servers = ref ([]: server list
) (* list of connected servers (servers that have sent $Hello to us)*)
93 let servers_by_ip : (string , server
) Hashtbl.t
= Hashtbl.create
10
94 let users_by_name = Hashtbl.create
113
95 let current_files = ref ([]: file list
)
96 let dc_files_by_unchecked_hash : (string , file
) Hashtbl.t
= Hashtbl.create
47
97 let dc_files_by_key = Hashtbl.create
47
98 let dc_tiger_computing = ref false
99 let dc_get_nchunks size
= Int64.to_int
(size
// CommonUploads.tiger_block_size
) + 1
100 let dc_shared_files_by_fullname : (string , dc_shared_file
) Hashtbl.t
= Hashtbl.create
30
101 let dc_shared_files_by_codedname : (string , dc_shared_file
) Hashtbl.t
= Hashtbl.create
30
102 let dc_shared_files_by_hash : (string , dc_shared_file
) Hashtbl.t
= Hashtbl.create
30
103 let dc_result_info : (int , dc_result
) Hashtbl.t
= Hashtbl.create
30
104 let dc_results_by_file : ((string * string * int64
), CommonTypes.result
) Hashtbl.t
= Hashtbl.create
30
105 let dc_files_to_hash = ref ([] : dc_shared_file list
)
106 let dc_last_manual_search = ref 0.
107 let dc_last_autosearch = ref (None
: CommonTypes.search
option)
108 let dc_last_autosearch_time = ref 0.
109 let dc_total_uploaded = ref Int64.zero
110 (*let current_open_slots = ref 0*)
111 (*let clients_by_name = Hashtbl.create 113*)
112 let clients_list = ref ([] : client list
)
113 let dc_hublist = ref ([] : dc_hub list
) (* list for DC servers *)
114 let temp_nick = ref "unknown"
115 let temp_nick_num = ref 0 (* this is used to name temporary users always with different name *)
116 let used_slots = ref 0
117 let dc_download_preread = ref 128 (* max (int64_kbyte-1) atm. Used to make simple check of file corruption *)
118 let dc_config_files_loaded = ref false
119 let char5 = char_of_int
5 (* /005 *)
120 let char13 = char_of_int
13 (* \r *)
121 let char32 = char_of_int
32 (* "space" *)
122 let char38 = char_of_int
38 (* & *)
123 let char39 = char_of_int
39 (* ' *)
124 let char42 = char_of_int
42 (* * *)
125 let char43 = char_of_int
43 (* + *)
126 let char58 = char_of_int
58 (* : *)
127 let char60 = char_of_int
60 (* < *)
128 let char62 = char_of_int
62 (* > *)
129 let char92 = char_of_int
92 (* slash \ *)
130 let char129 = char_of_int
129 (* extended ASCII *)
131 let char154 = char_of_int
154 (* extended ASCII *)
132 let char160 = char_of_int
160 (* extended ASCII *)
133 let char165 = char_of_int
165 (* extended ASCII *)
134 let empty_string = ""
135 let int64_kbyte = Int64.of_int
1024
136 let int64_mbyte = Int64.mul
int64_kbyte int64_kbyte
137 let int64_gbyte = Int64.mul
int64_mbyte int64_kbyte
138 let int64_64kbytes = Int64.mul
int64_kbyte (Int64.of_int
64)
139 let random_port = (Random.int 60000) + 1025
140 let mylist_ext = ".DcLst"
143 let mylistxmlbz2_ext = xml_ext ^
bz2_ext
144 let mylist = "MyList" ^
mylist_ext
145 let mylistxmlbz2 = "files" ^
mylistxmlbz2_ext
146 let directconnect_directory = "direct_connect"
147 let filelist_directory = Filename.concat
directconnect_directory "filelists"
148 let last_share_size = ref 0
150 (* These are the $Supports commands that MLDonkey understands at the moment *)
151 let mldonkey_dc_hub_supports = {
152 nogetinfo
= true; (* Hub doesn't need to receive a $GetINFO from a client to send out $MyINFO *)
153 nohello
= true; (* Client doesn't need either $Hello or $NickList to be sent *)
154 userip2
= false; (* Support for v2 of the $UserIP command *)
155 usercommand
= false; (* Support for $UserCommand, which is a standard way of adding hub-specific shortcuts to the client *)
163 let mldonkey_dc_client_supports = {
164 bzlist
= false; (* Support for a bzip2 compressed file list *)
165 minislots
= true; (* Support for the concept of a "mini-slot" *) (* off not supported *)
166 getzblock
= false; (* Instead of $Get and $Send, use $GetZBlock *)
167 xmlbzlist
= true; (* Support for UTF-8 XML file lists, includes also support for $UGetBlock *)
168 adcget
= true; (* Support for $ADCGET, a file retrieval command backported from the ADC draft *)
169 tthl
= false; (* Support for the "tthl" namespace for $ADCGET *)
170 tthf
= true; (* Support for the retrieving a file by its TTH through $ADCGET *)
171 zlig
= false; (* Support for compressing the stream of data sent by $ADCGET with the ZLib library *)
172 clientid
= false; (* Support for the $ClientID command *)
173 chunk
= false; (* Extension by Valknut that allows retrieval of sections of a file through a modified $Get syntax *)
174 gettestzblock
= false; (* Support for compressed transfers with commands $GetTestZBlock and $Sending *)
177 (* DC++ 0674 Supports to hubs: UserCommand NoGetINFO NoHello UserIP2 TTHSearch GetZBlock *)
178 (* DC++ 0674 Supports to clients: MiniSlots XmlBZList ADCGet TTHL TTHF GetZBlock ZLIG *)
179 (* Verlihub supports: OpPlus NoGetINFO NoHello UserIP2 *)
184 description
= empty_string;
185 client_brand
= empty_string;
186 version
= empty_string;
190 conn_speed
= empty_string;
191 open_upload_slot
= 0;
193 sharesize
= Int64.zero
;
194 email
= empty_string;
199 let set_server_state s state
=
200 set_server_state (as_server s
.server_server
) state
201 (*let set_room_state s state =
202 set_room_state (as_room s.server_room) state *)
203 let server_num s
= server_num (as_server s
.server_server
)
204 let file_num s
= file_num (as_file s
.file_file
)
205 let server_state s
= server_state (as_server s
.server_server
)
206 let file_state s
= file_state (as_file s
.file_file
)
207 let server_must_update s
= server_must_update (as_server s
.server_server
)
208 let file_must_update s
= file_must_update (as_file s
.file_file
)
210 let dc_new_shared_dir dirname
= {
211 shared_dirname
= dirname
;
216 let dc_shared_tree = dc_new_shared_dir ""
218 (* Copy from CommonUploads... *)
219 let rec dc_add_shared_file node dcsh dir_list
=
223 node
.shared_files
<- dcsh
:: node
.shared_files
224 | dirname
:: dir_tail
->
227 List.assoc dirname
node.shared_dirs
229 let new_node = dc_new_shared_dir dirname
in
230 node.shared_dirs
<- (dirname
, new_node) :: node.shared_dirs
;
233 dc_add_shared_file node dcsh dir_tail
235 let open_slots () = !!dc_open_slots
236 let current_slots () = open_slots () - !used_slots
238 let dc_remove_uploader () =
239 if !used_slots < 1 then begin
241 if !verbose_upload
then lprintf_nl "Slot internal counting error: already 0"
244 if !verbose_upload
then lprintf_nl "Decreased used slots to (%d)" !used_slots
247 let dc_insert_uploader () =
248 if !used_slots >= open_slots () then begin
249 used_slots := open_slots ();
250 if !verbose_upload
then lprintf_nl "Slot internal counting error: already at maximum"
253 if !verbose_upload
then lprintf_nl "Increased used slots to (%d)" !used_slots
256 let dc_can_upload () =
257 if !used_slots >= open_slots () then false else true
259 let counts_as_minislot size
= size
< int64_64kbytes
261 let is_even_to_hundreds x
= (x
> 0) && ((x
mod 100) = 0)
263 let is_even_to_tenths x
= (x
> 0) && ((x
mod 10) = 0)
265 let is_even_to_twos x
= (x
> 0) && ((x
mod 2) = 0)
267 let is_valid_tiger_hash hash
=
268 if String.length hash
= 39 then begin
269 if (String.contains hash
char32) || (String.contains hash
char92) ||
270 (String.contains hash '
/'
) then false
274 let find_sockets_client sock
=
277 (match c
.client_sock
with
278 | Connection csock
-> if csock
== sock
then raise
(Found_client c
)
283 | Found_client c
-> Some c
286 (* set our nick for hubs from .ini or global *)
288 if !!login
= "" then !!CommonOptions.global_login
else !!login
290 (* Shorten string to some maximum length *)
291 let shorten_string s length
=
292 if length
< String.length s
then
294 let n = Charset.utf8_nth s length
in
297 _
-> s
(* relies on bounds checking! FIXME? *)
300 (* Replace one string to another string from string *)
301 let dc_replace_str_to_str s find_str to_str
=
302 if find_str
= to_str
then failwith
"dc_replace_str_to_str find_str = to_str";
303 let flen = String.length find_str
in
310 index := String2.search_from s
0 find_str
312 | Not_found
-> index := -1 );
313 if (!index = -1) then begin
314 str := !str ^ s
; true
316 str := !str ^
String2.before s
!index ^ to_str
;
317 rest := String2.after s
(!index+flen);
321 if not
ok then replace !rest
326 (* Strip all unnecessary characters from string (CHECK not perfect) *)
327 let clean_string str =
328 (* DC++ static const char* badChars = "$|.[]()-_+"; *)
330 let batch = ref "" in
331 let last_was_space = ref false in
333 if (String.length
!batch) > 2 then begin
338 String.iter
(fun c
->
339 (match c
with (* TODO 1..9 *)
340 | c
when ((c
>= 'a'
) && (c
<= 'z'
)) ||
341 ((c
>= 'A'
) && (c
<= 'Z'
)) ||
342 ((c
>= char129) && (c
<= char154)) ||
343 ((c
>= char160) && (c
<= char165)) ||
344 ((c
>= '
0'
) && (c
<= '
9'
)) ->
345 last_was_space := false;
346 batch := !batch ^
String2.of_char c
347 | ' '
| '
.'
| '
-'
| '_'
->
348 if !last_was_space then ()
350 if add_to_s () then s := !s ^
String2.of_char
char32;
352 last_was_space := true;
356 ignore
(add_to_s ());
359 (* Create temporary nickname for client connection *)
360 let create_temp_nick () =
361 let s = "Unknown" ^
(string_of_int
!temp_nick_num) in
362 if !temp_nick_num == max_int
then temp_nick_num := 0
363 else temp_nick_num := succ
!temp_nick_num;
366 (* Add user to server and vice versa *)
367 let add_user_to_server u
s =
368 if not
(List.memq
s u
.user_servers
) then u
.user_servers
<- s :: u
.user_servers
; (* add server to users list *)
369 if not
(List.memq u
s.server_users
) then begin
370 s.server_users
<- u
:: s.server_users
; (* add user to servers list *)
371 server_new_user
(as_server
s.server_server
) (as_user u
.user_user
);
374 (* Add new user to hubs userlist *)
375 (* PROBLEM ? There can possibly be users from different servers with same names, *)
376 (* and atm. this is not checked in any way. So if on different servers *)
377 (* has users with same name, they are hereafter treated as one *)
378 let new_user server name
=
381 Hashtbl.find
users_by_name name
385 user_ip
= Ip.addr_of_ip
Ip.null
;
388 user_user
= user_impl
;
389 user_uploaded
= Int64.zero
;
390 user_downloaded
= Int64.zero
;
391 user_link
= empty_string;
392 user_myinfo
= init_myinfo;
395 user_state
= UserIdle
;
397 user_read_messages
= 0;
400 impl_user_ops
= user_ops
;
401 impl_user_val
= user;
403 Hashtbl.add
users_by_name name
user;
405 (*lprintf_nl "New user: %s" user.user_nick;*)
408 ignore
(match server
with
409 | Some
s -> add_user_to_server u s
413 (* Check if user has some of my nicks = is me *)
418 if first
.server_last_nick
= u.user_nick
then true
421 in iter !connected_servers
423 (* Find user by name *)
424 let search_user_by_name nick
=
426 Hashtbl.find
users_by_name nick
427 with _
-> raise Not_found
429 (* Remove server from users serverlist *)
430 let remove_server_from_user s u =
431 if (List.memq
s u.user_servers
) then begin
432 u.user_servers
<- List2.removeq
s u.user_servers
(* remove server from user *)
435 (* Remove user from servers userlist *)
436 let remove_user_from_server u s =
437 if (List.memq
u s.server_users
) then begin
438 s.server_users
<- List2.removeq
u s.server_users
(* remove user frim server *)
441 (* Remove user from servers userlist and if not any pending downloads, from Hashtbl userlist also *)
442 let remove_user s u =
443 remove_user_from_server u s;
444 remove_server_from_user s u;
445 if (List.length
u.user_servers
< 1) then begin
446 if u.user_clients
= [] then begin (* if user has no clients *)
447 Hashtbl.remove
users_by_name u.user_nick
;
448 u.user_messages
<- [];
449 u.user_read_messages
<- 0;
451 lprintf_nl "User (%s) has clients, not removed" u.user_nick
455 (* Is user active ? *)
457 u.user_myinfo
.mode
= 'A'
459 (* Check is filelist downloading from this user already on queue or loaded *)
460 let filelist_already_downloading u =
463 (match cl
.client_state
with
464 | DcDownloadListWaiting
| DcDownloadListConnecting _
| DcDownloadList _
-> raise BreakIter
470 (* true if user has new messages *)
471 let user_has_new_messages user = (List.length
user.user_messages
) > user.user_read_messages
473 (* file impl for uploading clients *)
474 let new_upfile dcsh fd
=
475 let filename,directory
=
478 Filename.basename dcsh
.dc_shared_fullname
, Filename.dirname dcsh
.dc_shared_fullname
480 let filename = Unix32.filename fd
in
481 Filename.basename
filename, Filename.dirname
filename )
485 file_unchecked_tiger_root
= "";
486 file_directory
= directory
;
487 file_name
= filename;
490 (*file_tiger_array = [||];*)
491 file_autosearch_count
= 0;
493 (dummy_file_impl
()) with
494 impl_file_fd
= Some fd
;
495 impl_file_size
= Unix32.getsize64 fd
;
496 impl_file_downloaded
= Int64.zero
;
497 impl_file_received
= Int64.zero
;
498 impl_file_val
= file;
499 impl_file_ops
= file_ops
;
500 impl_file_age
= last_time
();
501 impl_file_best_name
= filename;
505 (* Return existing file or create new one *)
506 let new_file tiger_root
(directory
:string) (filename:string) (file_size
:int64
) =
508 let f = Hashtbl.find
dc_files_by_unchecked_hash tiger_root
in
509 if !verbose_download
then
510 lprintf_nl "File with hash exists: (%s) (%s) (%s)" tiger_root
f.file_directory
f.file_name
;
513 let key = (directory
, filename, file_size
) in
515 let f = Hashtbl.find
dc_files_by_key key in (* Then try to find by key (dir,name,size) *)
516 if !verbose_download
then lprintf_nl "File exists: (%s) (%s)" f.file_directory
f.file_name
;
520 (match tiger_root
with
521 | "" -> Printf.sprintf
"DC_%s_%s" directory
filename
522 | _
-> Printf.sprintf
"DC_%s" tiger_root
)
524 let fullname = CommonFile.concat_file
!!temp_directory
temp_filename in
525 let temp_file = Unix32.create_rw
fullname in
528 Unix32.getsize
fullname
530 if !verbose_unexpected_messages
then
531 lprintf_nl "Exception (%s) in current_size of (%s)" (Printexc2.to_string e
) fullname;
536 file_unchecked_tiger_root
= tiger_root
;
537 file_directory
= directory
;
538 file_name
= filename;
541 (*file_tiger_array = [||];*)
542 file_autosearch_count
= 0;
544 (dummy_file_impl
()) with
545 impl_file_fd
= Some
temp_file;
546 impl_file_size
= file_size
;
547 impl_file_downloaded
= current_size;
548 impl_file_received
= current_size;
549 impl_file_val
= file;
550 impl_file_ops
= file_ops
;
551 impl_file_age
= last_time
();
552 impl_file_best_name
= filename;
554 file_add impl FileNew
; (* CommonInteractive.file_add *)
555 current_files := file :: !current_files;
556 if tiger_root
<> empty_string then Hashtbl.add
dc_files_by_unchecked_hash tiger_root
file;
557 Hashtbl.add
dc_files_by_key key file;
558 if !verbose_download
then
559 lprintf_nl "New File:(%s) (%s) (%s) (%Ld)" tiger_root
file.file_directory
file.file_name file_size
;
564 (* Some shortcuts to CommonFile... *)
565 let file_size file = file.file_file
.impl_file_size
566 let file_downloaded file = file_downloaded (as_file
file.file_file
)
567 let file_age file = file.file_file
.impl_file_age
568 let file_fd file = file_fd (as_file
file.file_file
)
570 (* Add new client, return client*)
573 client_client
= impl
;
574 client_sock
= NoConnection
;
577 client_supports
= None
;
579 client_file
= None
; (* (file, filename) *)
580 client_state
= DcIdle
;
581 client_error
= NoError
;
582 client_error_count
= 0;
583 client_preread_bytes_left
= 0;
584 client_pos
= Int64.zero
;
585 client_endpos
= Int64.zero
; (* atm. upload end position *)
586 client_receiving
= Int64.zero
;
588 client_connect_time
= last_time
();
589 client_connection_control
= new_connection_control
();
590 client_downloaded
= Int64.zero
;
591 client_uploaded
= Int64.zero
;
593 dummy_client_impl
with
595 impl_client_ops
= client_ops
;
596 impl_client_upload
= None
;
598 (*lprintf_nl "New client"; *)
599 CommonClient.new_client impl
;
600 clients_list := c :: !clients_list;
603 (* add client to file & vice versa *)
604 let add_client_to_file client
file = (* TODO we never empty files clients list so implement some kind of size control *)
605 if not
(List.memq client
file.file_clients
) then begin (* if client is not on file's contact list... *)
606 file.file_clients
<- client
:: file.file_clients
; (* then add this new client to file contact list *)
607 client
.client_file
<- Some
file;
608 (*file_add_source (as_file file.file_file) (as_client client.client_client)*) (* CommonFile.file_add_source *)
611 (* add client to user & vice versa *)
612 let add_client_to_user client
user =
613 if not
(List.memq client
user.user_clients
) then begin
614 user.user_clients
<- user.user_clients
@ [ client
]; (* add client to userlist *)
615 client
.client_user
<- Some
user
618 (* New client to user with file *)
619 let new_client_to_user_with_file u f =
620 let c = new_client () in
621 c.client_name
<- Some
u.user_nick
;
622 add_client_to_user c u;
623 add_client_to_file c f;
627 client_type (as_client
c.client_client
)
629 (* Find clients by name, return list of all matching clients *)
630 (*let find_clients_by_name name =
631 let result = ref [] in
633 (match c.client_user with
634 | Some u -> if u.user_nick = name then result := c :: !result; ()
639 (* Print client state to string *)
640 let client_state_to_string c =
641 let get_direction dir
=
643 | Upload i
-> Printf.sprintf
"Upload %d" i
644 | Download i
-> Printf.sprintf
"Download %d" i
)
646 (match c.client_state
with
648 | DcDownloadWaiting _
-> "DcDownloadWaiting"
649 | DcDownloadConnecting _
-> "DcDownloadConnecting"
650 | DcDownloadListWaiting
-> "DcDownloadListWaiting"
651 | DcDownloadListConnecting _
-> "DcDownloadListConnecting"
652 | DcConnectionStyle style
->
654 | ClientActive dir
-> Printf.sprintf
"DcConnectionStyle ClientActive %s" (get_direction dir
)
655 | MeActive dir
-> Printf.sprintf
"DcConnectionStyle MeActive %s" (get_direction dir
) )
656 | DcDownload _
-> "DcDownload"
657 | DcDownloadList _
-> "DcDownloadList"
658 | DcUpload _
-> "DcUpload"
659 | DcUploadStarting _
-> "DcUploadStarting"
660 | DcUploadList _
-> "DcUploadList"
661 | DcUploadListStarting _
-> "DcUploadListStarting"
662 | DcUploadDoneWaitingForMore
-> "DcUploadDoneWaitingForMore" )
664 (* Copy client data to another *)
665 let new_copy_client c =
668 (*client_sock = c.client_sock;*)
669 client_name
= c.client_name
;
670 client_addr
= c.client_addr
;
671 client_supports
= c.client_supports
;
672 client_lock
= c.client_lock
;
673 client_file
= c.client_file
;
674 (*client_state = c.client_state;*)
675 client_pos
= c.client_pos
;
676 client_receiving
= c.client_receiving
;
677 client_user
= c.client_user
;
678 client_error
= c.client_error
;
679 client_error_count
= c.client_error_count
;
680 client_endpos
= c.client_endpos
;
681 client_connect_time
= c.client_connect_time
;
682 client_downloaded
= c.client_downloaded
;
683 client_uploaded
= c.client_uploaded
;
686 (* Get clients username *)
687 let clients_username client
=
689 (match client
.client_user
with
690 | Some
user -> user.user_nick
694 (* Remove clients files and all references from files to this client *)
695 let remove_client_from_clients_file c =
696 (match c.client_file
with
698 f.file_clients
<- List2.removeq
c f.file_clients
;
699 c.client_file
<- None
702 (* Remove clients references from users *)
703 let remove_client c =
704 (match c.client_user
with
706 u.user_clients
<- List2.removeq
c u.user_clients
707 (*lprintf_nl "Removed one client from user %s clientlist" u.user_nick;*)
709 c.client_user
<- None
;
710 remove_client_from_clients_file c;
711 clients_list := List2.removeq
c !clients_list;
712 client_remove
(as_client
c.client_client
);
715 (* Remove file from current filelist *)
716 let remove_file_from_filelist file =
718 current_files := List2.removeq
file !current_files;
720 if !verbose_unexpected_messages
then
721 lprintf_nl "Could not remove file from !current_files - %s" file.file_name
)
723 (* Remove file from hashtbl dc_files_by_unchecked_hash *)
724 let remove_file_from_hashes file =
725 if file.file_unchecked_tiger_root
<> empty_string then begin
727 Hashtbl.remove
dc_files_by_unchecked_hash file.file_unchecked_tiger_root
;
729 if !verbose_unexpected_messages
then
730 lprintf_nl "Could not remove file from hashtable dc_files_unchecked_hash - %s" file.file_name
)
733 (* Remove file from hashtab dc_files_by_key *)
734 let remove_file_from_files file =
736 Hashtbl.remove
dc_files_by_key (file.file_directory
, file.file_name
, file.file_file
.impl_file_size
);
738 if !verbose_unexpected_messages
then
739 lprintf_nl "Could not remove file from hashtable dc_files_by_key - %s" file.file_name
)
741 (* remove all clients of file *)
742 let remove_files_clients file =
746 file.file_clients
<- []
748 (* remove file from file list *)
749 let remove_file_with_clients file =
750 remove_files_clients file;
751 remove_file_from_hashes file;
752 remove_file_from_files file;
753 remove_file_from_filelist file
755 (* remove file from file list *)
756 let remove_file_not_clients file =
758 c.client_file
<- None
;
760 file.file_clients
<- [];
761 remove_file_from_hashes file;
762 remove_file_from_files file;
763 remove_file_from_filelist file
765 let set_client_state c state
=
766 set_client_state (as_client
c.client_client
) state
768 let dc_set_client_disconnected c =
769 set_client_disconnected
(as_client
c.client_client
)
771 let set_clients_upload c sh
=
772 set_client_upload
(as_client
c.client_client
) sh
; (*(as_file c.client_file);*)
773 set_client_has_a_slot
(as_client
c.client_client
) NormalSlot
;
774 client_enter_upload_queue
(as_client
c.client_client
)
776 (* Print closing reason to string *)
777 let closing_reason_to_text reason
=
779 | Closed_for_error text
-> Printf.sprintf
"Error: Reason (%s)" text
780 | Closed_for_timeout
-> "Timeout"
781 | Closed_for_lifetime
-> "Lifetime"
782 | Closed_by_peer
-> "By peer"
783 | Closed_by_user
-> "By user (us - operation complete)"
784 | Closed_for_overflow
-> "Overflow"
785 | Closed_connect_failed
-> "Connect failed"
786 | Closed_for_exception _
-> "Exception" )
788 (* Can client start downloading *)
789 let is_client_waiting c =
790 (match c.client_state
with (* check user clients states *)
791 | DcIdle
| DcDownloadWaiting _
| DcDownloadListWaiting
-> true
792 | DcUpload _
| DcUploadStarting _
| DcUploadListStarting _
| DcUploadList _
793 | DcDownloadListConnecting _
| DcDownloadConnecting _
| DcDownload _
794 | DcDownloadList _
| DcConnectionStyle _
| DcUploadDoneWaitingForMore
-> false )
796 (* Can client start downloading clients file *)
797 let is_client_blocking_downloading c =
798 (match c.client_state
with
799 | DcIdle
| DcUpload _
| DcUploadStarting _
| DcUploadListStarting _
800 | DcUploadList _
| DcDownloadWaiting _
| DcDownloadListWaiting
801 | DcUploadDoneWaitingForMore
-> false
802 | DcDownloadListConnecting _
| DcDownloadConnecting _
| DcDownload _
803 | DcDownloadList _
| DcConnectionStyle _
-> true )
805 (* Check user, that has sent RevConnectToMe and we have sent ConnectToMe and we are waiting *)
806 let check_passive_user u =
807 (match u.user_state
with
808 | UserPassiveUserInitiating time
->
809 if (current_time
() -. time
) > float_of_int
!!client_timeout
then begin
810 if !verbose_msg_clients
then
811 lprintf_nl "Resetted passive user (%s) waiting state " u.user_nick
;
812 u.user_state
<- UserIdle
;
816 (* Check all clients, that have sent RevConnectToMe and we have sent ConnectToMe and we are waiting *)
817 let check_all_passive_users () =
818 Hashtbl.iter (fun _
u ->
822 (* Check that user has no downloads and is not in conversation state *)
823 let can_user_start_downloading u =
826 if (is_client_blocking_downloading c) then raise BreakIter
828 (match u.user_state
with (* check user state/timeouts *)
830 | UserPassiveUserInitiating time
-> (* passive users wait check *)
831 if (current_time
() -. time
) > float_of_int
!!client_timeout
then begin
832 if !verbose_msg_clients
then lprintf_nl "Resetted RevConnect Passive user waiting (%s)" u.user_nick
;
833 u.user_state
<- UserIdle
;
834 end else raise BreakIter
835 | _
-> raise BreakIter
);
839 (* Find a connected client by ip *)
840 (*let find_connected_client_by_ip ip port =
843 (match c.client_sock with
845 (match c.client_addr with
846 | None -> failwith "Client connected but no ip address"
847 | Some (cip , cport) -> begin
848 lprintf_nl "Client match found: checking real ips";
849 let rip = Ip.to_string (TcpBufferedSocket.peer_ip sock) in
850 let rport = TcpBufferedSocket.peer_port sock in
851 lprintf_nl " From socket: rip= %s rport= %d" rip rport;
852 lprintf_nl " From c.client_addr: cip= %s cport= %d" (Ip.to_string cip) cport;
853 lprintf_nl " From ConnectToMe : ip = %s port = %d" (Ip.to_string ip) port;
854 if (Ip.equal cip ip) then raise (Found_client c)
860 | Found_client c -> Some c
861 | Failure e -> lprintf_nl "In ( find_connected_client_by_ip): %s" e; None
862 | Not_found -> None ) *)
864 (* Find any client with known ip *)
865 (*let find_client_by_ip ip =
868 (match c.client_addr with
869 | None -> failwith "No ip on client!"
871 if (Ip.equal cip ip) then begin
872 (match c.client_user with
873 | Some u -> lprintf_nl " Found matching client from user %s with ip: %s" u.user_nick
874 (Ip.to_string cip); ()
875 | _ -> failwith "find_client_by_ip: No user in client !" );
876 raise (Found_client c)
881 | Found_client c -> Some c
882 | Failure e -> lprintf_nl "In (find_client_by_ip): %s" e; None
883 | Not_found -> None ) *)
885 (* Add needed dc-info fields to result by number *)
886 let add_info_to_result r
user tiger_root directory
=
890 directory
= directory
;
893 Hashtbl.find
dc_result_info r
.stored_result_num
(* if result number exists in hashtable result_sources *)
894 (* return existing result's info (user & directory) *)
896 Hashtbl.add
dc_result_info r
.stored_result_num
result_info; (* ...add the new result's info to hashtable *)
899 (* add new server/hub by address and port if not exist - return server/hub*)
900 let new_server addr ip port
=
901 let ips = Ip.to_string ip
in
903 Hashtbl.find
servers_by_ip ips
906 server_server
= server_impl
;
907 server_name
= "<unknown>";
911 server_supports
= None
;
912 server_connection_time
= nan
; (* Stands for ``not a number' *)
913 server_hub_state
= Waiting
;
914 server_connection_control
= new_connection_control
();
915 server_sock
= NoConnection
;
916 server_autoconnect
= false;
918 server_last_nick
= "";
919 server_search
= None
;
920 server_search_timeout
= 0;
923 server_messages
= [];
924 server_read_messages
= 0;
927 dummy_server_impl
with
929 impl_server_ops
= server_ops
;
932 server_add server_impl
;
933 Hashtbl.add
servers_by_ip ips h;
936 (* Add server to connected servers *)
937 let add_connected_server s =
939 if not
(List.memq
s !connected_servers) then
940 connected_servers := s :: !connected_servers
942 (* Remove servers contacts to users and from connected servers *)
943 let remove_connected_server s =
945 connected_servers := List2.removeq
s !connected_servers;
949 s.server_hub_state
<- Waiting
;
950 s.server_search
<- None
;
953 (* Remove server from known servers list *)
954 let server_remove s =
955 server_remove (as_server
s.server_server
);
956 Hashtbl.remove
servers_by_ip (Ip.to_string
s.server_ip
)
957 (*decr nknown_servers;*)
958 (*servers_list := List2.removeq s !servers_list*)
960 (* Iter all servers in connected list *)
961 let dc_with_connected_servers f =
966 (* Return hub state text *)
967 let dc_hubstate_to_text s =
968 (match s.server_hub_state
with
969 | Waiting
-> "Not connected"
974 (* Search server by ip and port *)
975 (*let search_server_by_addr addr port =
977 Hashtbl.find servers_by_addr (addr, port)
981 (* add new result to results-hashtable - return the found or new result *)
982 let new_result user tiger_root
(directory
:string) (filename:string) (filesize
:int64
) =
983 let basename = Filename2.basename filename in
984 let key = (directory
, basename, filesize
) in
985 (*let r_username = "......" ^ user.user_nick in*)
987 Hashtbl.find
dc_results_by_file key (* if result with dir&name&size exists, return the found result *)
988 with _
-> (* otherwise... *)
990 if tiger_root
<> "" then begin
993 ignore
(Hashtbl.find
dc_shared_files_by_hash tiger_root
);
997 if found then [filename;"ALREADY DOWNLOADED"]
1001 ignore
(Hashtbl.find
dc_files_by_unchecked_hash tiger_root
);
1005 if found then [filename;"FILE DOWNLOADING..."]
1010 let rec r = { (* add new result *)
1012 result_names = result_names;
1013 result_tags
= [ {tag_name
= Field_UNKNOWN
user.user_nick
; tag_value
= String
""} ];
1014 result_size
= filesize
;
1015 result_source_network
= network.network_num
;
1017 let rs = update_result_num
r in (* CommonResult.update_result_num, returns Commontypes.result *)
1018 Hashtbl.add
dc_results_by_file key rs;
1021 (*let hash_file () =
1022 let dcsh = List.hd !dc_shared_files in
1023 let info = CommonUploads.IndexedSharedFiles.get_result dcsh.dc_shared_shared.shared_info in
1024 if dcsh.dc_shared_chunk <> dc_get_nchunks info.shared_size then compute_tigertree_chunk dcsh*)
1027 (* Hashtbl.iter (fun n sh ->
1028 lprintf_nl "(%s)" sh.shared_codedname;
1029 let info = CommonUploads.IndexedSharedFiles.get_result sh.shared_info in
1030 lprintf_nl "(%s)" info.shared_fullname
1032 ) CommonUploads.shared_files*)
1035 /** We don't keep leaves for blocks smaller than this... */
1036 static const int64_t MIN_BLOCK_SIZE = 64*1024;
1038 (*CommonHasher.compute_tiger :
1039 string -> int64 -> int64 -> (Md4.TigerTree.t job -> unit) -> unit
1044 while(bl * (int64_t)d->getTigerTree().getLeaves().size() < d->getTigerTree().getFileSize())
1046 d->getTigerTree().setBlockSize(bl);
1047 d->getTigerTree().calcRoot();
1050 AdcCommand Download::getCommand(bool zlib, bool tthf) {
1051 AdcCommand cmd(AdcCommand::CMD_GET);
1052 if(isSet(FLAG_TREE_DOWNLOAD)) {
1053 cmd.addParam("tthl");
1054 } else if(isSet(FLAG_PARTIAL_LIST)) {
1055 cmd.addParam("list");
1057 cmd.addParam("file");
1059 if(tthf && getTTH() != NULL) {
1060 cmd.addParam("TTH/" + getTTH()->toBase32());
1062 cmd.addParam(Util::toAdcFile(getSource()));
1064 cmd.addParam(Util::toString(getPos()));
1065 cmd.addParam(Util::toString(getSize() - getPos()));
1067 if(zlib && getSize() != -1 && BOOLSETTING(COMPRESS_TRANSFERS)) {
1068 cmd.addParam("ZL1");