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
20 open CommonInteractive
25 open CommonInteractive
30 open TcpBufferedSocket
47 let log_prefix = "[dcCli]"
50 lprintf_nl2
log_prefix fmt
52 (* Check current client state and setup for future proceeding with client *)
53 let set_client_state_on_disconnect c
=
54 (match c
.client_state
with
55 | DcDownloadWaiting file
(* if client is downloading a file ...*)
57 | DcDownloadConnecting
(file
,_
) ->
58 (*if !verbose_download then
59 lprintf_nl "Client (%s) state is (DcDownload/-Waiting/-Connecting file) on closing socket" (clients_username c);*)
60 c
.client_state
<- DcDownloadWaiting file
; (* continue downloading later *)
61 set_client_state c NewHost
62 | DcUploadList file_fd
(* if client was handling file lists... *)
63 | DcDownloadList file_fd
->
64 (*if !verbose_upload || !verbose_download then
65 lprintf_nl "Client (%s) state is (DcUploadList/DcDownloadList) on closing socket" (clients_username c); *)
66 c
.client_state
<- DcIdle
;
67 Unix32.close file_fd
; (* close file descriptor and remove client *)
69 | DcUploadStarting
(dcsh
,_
,_
)
70 | DcUpload
(dcsh
,_
,_
,_
) -> (* if client was uploading file ... *)
71 (*if !verbose_upload then
72 lprintf_nl "Client (%s) state is (DcUpload/UploadStarting) on closing socket" (clients_username c);*)
73 if not
(counts_as_minislot dcsh
.dc_shared_size
) then dc_remove_uploader
(); (* check if we have to free a slot *)
74 set_client_has_a_slot
(as_client c
.client_client
) NoSlot
; (* inform GUI *)
75 (match c
.client_state
with
76 | DcUpload
(_
,file_fd
,_
,_
) -> Unix32.close file_fd
78 c
.client_state
<- DcIdle
;
80 | DcDownloadListWaiting
(* filelist downloading *)
81 | DcDownloadListConnecting _
->
82 (*if !verbose_download then
83 lprintf_nl "Client (%s) state is (DcDownloadListWaiting/-Connecting) on closing socket" (clients_username c);*)
84 c
.client_state
<- DcDownloadListWaiting
;
85 set_client_state c NewHost
;
86 | DcConnectionStyle _
->
87 (*if !verbose_upload || !verbose_download then
88 lprintf_nl "Client (%s) state is (DcConnectionStyle) on closing socket" (clients_username c);*)
89 c
.client_state
<- DcIdle
;
91 | DcUploadListStarting _
92 | DcUploadDoneWaitingForMore
94 (*if !verbose_upload then
95 lprintf_nl "Client (%s) state is (DcUploadListStarting/DoneWaitingForMore) on closing socket" (clients_username c);*)
96 c
.client_state
<- DcIdle
;
100 (* check that file can be started and no other client is downloading it *)
101 let can_file_start_downloading f
=
103 (match (file_state f
) with (* check file state *)
104 | FileDownloaded
| FileShared
| FileCancelled
| FileAborted _
| FilePaused
-> raise BreakIter
106 List.iter
(fun c
-> (* check files all other clients that they are not already possibly loading *)
107 if is_client_blocking_downloading c
then raise BreakIter
112 (* Try to find alternative client to file *)
113 let find_downloadable_client_for_file file
= (* CHECK possible user state also... *)
115 List.iter
(fun c
-> (* chech all files sources *)
116 (match c
.client_user
with
118 if (can_user_start_downloading u
) then begin
119 if is_client_waiting c
then begin
120 (match c
.client_error
with
121 | NoError
| NoFreeSlots
| UploadError
-> raise
(Found_client c
)
122 | FileNotAvailable
| UserNotReachable
| ClosedOnInit
| ConnectionResetByPeer
123 | UserDontReplyOnTime
-> () )
130 | Found_client c
-> Some c
)
132 (* Disconnect client with proper even if no socket yet handling *)
133 let dc_disconnect_client c reason
=
134 (match c
.client_sock
with
136 connection_failed c
.client_connection_control
;
137 dc_set_client_disconnected c reason
;
138 TcpBufferedSocket.close sock reason
;
139 c
.client_sock
<- NoConnection
;
140 | ConnectionWaiting token
->
142 c
.client_sock
<- NoConnection
;
144 set_client_state_on_disconnect c
;
145 (match c
.client_user
with
147 user
.user_state
<- UserIdle
; (* initialize also possible user *)
150 (* Move file to last in global filelist *)
151 let move_file_to_last_in_files f
=
152 if List.length
!current_files
> 1 then begin
153 let list = List2.removeq_first f
!current_files
in
154 current_files
:= list @ [f
];
157 (* Move client to first in files clientlist *)
158 let move_client_to_first_in_fileslist c
=
159 (match c
.client_file
with
161 if List.length f
.file_clients
> 1 then begin
162 let list = List2.removeq_first c f
.file_clients
in
163 f
.file_clients
<- c
:: list; (* lets put this client on top of files clients *)
167 (* Move client to first in users clientlist *)
168 let move_client_to_first_in_userslist c
=
169 (match c
.client_user
with
171 if List.length u
.user_clients
> 1 then begin
172 let list = List2.removeq_first c u
.user_clients
in
173 u
.user_clients
<- c
:: list; (* lets put this client on top of users clients *)
177 (* Move client to last in users clientlist *)
178 let move_client_to_last_in_userslist c
=
179 (match c
.client_user
with
181 if List.length u
.user_clients
> 1 then begin
182 let list = List2.removeq_first c u
.user_clients
in (* lets move this client to last in users list *)
183 u
.user_clients
<- list @ [c
];
187 (* Move client to last in users clientlist *)
188 let move_client_to_last_in_fileslist c
=
189 (match c
.client_file
with
191 if List.length f
.file_clients
> 1 then begin
192 let list = List2.removeq_first c f
.file_clients
in (* lets move this client to last in files list *)
193 f
.file_clients
<- list @ [c
];
197 (* Try to find next available source to this file and make it next in line, return true if found *)
198 let find_existing_source c
=
199 (match c
.client_file
with
201 let alternative = find_downloadable_client_for_file f
in
202 (match alternative with
203 | Some ac
-> (* we have a client that we can try to download *)
204 move_client_to_first_in_fileslist ac
; (* lets top this client to be next in line to be tried *)
205 move_client_to_first_in_userslist ac
;
210 (* Send search request to server *)
211 let server_send_search s search filetype sname
=
212 if !verbose_msg_clients
&& (List.length
!connected_servers
) > 0 then
213 lprintf_nl "Sending: $Search (%s) (%s)" (shorten_string s
.server_name
20) sname
;
214 do_if_connected s
.server_sock
(fun sock
->
215 let module S
= DcProtocol.Search
in
216 let msg = DcProtocol.SearchReq
{
217 S.passive
= !!firewalled
;
218 S.nick
= if !!firewalled
then s
.server_last_nick
else empty_string
;
219 S.ip
= if !!firewalled
then empty_string
else Ip.to_string
(CommonOptions.client_ip
(Some sock
));
220 S.port
= if !!firewalled
then empty_string
else (string_of_int
!!dc_port
);
221 S.sizelimit
= NoLimit
;
222 S.filetype
= filetype
;
223 S.words_or_tth
= sname
;
225 dc_send_msg sock
msg;
226 s
.server_search
<- Some search
;
227 s
.server_search_timeout
<- last_time
() + !!search_timeout
;
230 (* Received SR from servers or by udp *)
231 let received_new_search_result s
msg =
232 if s
.server_search_timeout
< last_time
() then s
.server_search
<- None
;
233 (match s
.server_search
with
237 let user = new_user
(Some s
) msg.S.owner
in (* create possibly new user *)
238 let result = new_result
user msg.S.tth
msg.S.directory
msg.S.filename
msg.S.filesize
in (* new or retrieve existing *)
239 (* result dir,filename,size *)
240 ignore
(add_info_to_result
result user msg.S.tth
msg.S.directory
); (* add info to dc-fields *)
241 CommonInteractive.search_add_result
false q
result;
242 if !!autosearch_by_tth
&& (msg.S.tth
<> empty_string
) then begin
244 let f = Hashtbl.find dc_files_by_unchecked_hash
msg.S.tth
in (* if this hash is in downloads *)
245 if (List.length
f.file_clients
< !!max_sources_file
) then begin
247 List.iter
(fun c
-> (* check if some of users client is already on this files list *)
248 if (List.mem c
f.file_clients
) then raise BreakIter
250 let c = new_client_to_user_with_file
user f in
251 c.client_state
<- DcDownloadWaiting
f;
252 if !verbose_msg_clients
then lprintf_nl "New client (%s) created by tth search to file (%s)"
253 (clients_username
c) f.file_name
254 with _
-> () ) (* user has already client with this file *)
256 with _
-> () ) (* this file is in active downloads *)
259 (* Create new search automatically if possible *)
260 let create_new_search f =
261 let query = QAnd
(QHasField
(Field_Type
, "TTH") , (QHasWord
f.file_unchecked_tiger_root
)) in
262 let search = CommonSearch.new_search
(CommonUserDb.find_ui_user
CommonUserDb.admin_user_name
)
263 (let module G
= GuiTypes
in
265 G.search_query
= query;
266 G.search_max_hits
= 1000;
267 G.search_type
= RemoteSearch
;
268 G.search_network
= network
.network_num
;
271 dc_with_connected_servers
(fun s
-> (* iter all servers *)
272 server_send_search s
search 9 f.file_unchecked_tiger_root
274 (match !dc_last_autosearch
with
275 | Some s
-> CommonSearch.search_forget
(CommonUserDb.find_ui_user
CommonUserDb.admin_user_name
) s
277 dc_last_autosearch
:= Some
search;
278 dc_last_autosearch_time
:= current_time
();
279 f.file_autosearch_count
<- succ
f.file_autosearch_count
281 let create_autosearch () =
282 let s_time_out = float !!search_timeout
in
283 let c_time = current_time
() in
284 if (!dc_last_autosearch_time
+. s_time_out) < c_time &&
285 (!dc_last_manual_search
+. s_time_out) < c_time && (List.length
!connected_servers
) > 0 then begin
286 (*lprintf_nl "Created autosearch";*)
290 List.iter
(fun c -> (* lets check that file is not already being downloaded *)
291 if is_client_blocking_downloading
c then raise BreakIter
293 if (List.length
f.file_clients
< !!max_sources_file
) &&
294 (is_valid_tiger_hash
f.file_unchecked_tiger_root
) then begin
296 raise
(Found_file
f);
301 (*lprintf_nl "Autosearch end not found"; *)
303 move_file_to_last_in_files f ) (* lets give next search to different file *)
304 (*lprintf_nl "Autosearch end file found" )*)
307 (* Memorize and take action on different client error situations *)
308 let new_client_error c error
=
309 let same_as_before = (error
= c.client_error
) in
310 if not
same_as_before then c.client_error_count
<- 1
311 else c.client_error_count
<- succ
c.client_error_count
;
313 | NoFreeSlots
-> (* MaxedOut *)
314 move_client_to_last_in_userslist c;
315 move_client_to_last_in_fileslist c;
316 c.client_error
<- NoFreeSlots
;
317 | FileNotAvailable
->
318 c.client_error
<- FileNotAvailable
;
319 (match c.client_state
with
320 | DcDownloadList _
-> (* on rare condition... eg. DC++ 0.401 *)(* TODO Try to load MyList.DcLst *)
323 move_client_to_last_in_userslist c;
324 move_client_to_last_in_fileslist c;
326 | UserNotReachable
->
327 c.client_error
<- UserNotReachable
;
329 if same_as_before then begin
330 (* what to do if connection closes on init phase without clear reason *)
331 if (is_even_to_tenths
c.client_error_count
) then begin
332 if not
(find_existing_source c) then begin (* if not available source *)
333 if !verbose_msg_clients
then
334 lprintf_nl "ClosedOnInit: (%s)" (clients_username
c);
338 c.client_error
<- ClosedOnInit
340 | ConnectionResetByPeer
->
341 if same_as_before then begin
342 (* what to do if client resets connection without error messages and without reason *)
343 if (c.client_error_count
> 10) then begin
344 if not
(find_existing_source c) then begin (* if not available source *)
345 if !verbose_msg_clients
then
346 lprintf_nl "ConnectionResetByPeer: (%s)" (clients_username
c);
350 c.client_error
<- ConnectionResetByPeer
353 c.client_error
<- UploadError
;
354 if !verbose_msg_clients
|| !verbose_upload
then
355 lprintf_nl "UploadError: (%s)" (clients_username
c);
356 (*ready_for_upload (as_client c.client_client);*) (* still try to continue upload, or what ? *)
357 | UserDontReplyOnTime
->
358 (* what to do if we have sent Rev/ConnectToMe to user but got no MyNick on time *)
359 if same_as_before then begin
360 if (is_even_to_twos
c.client_error_count
) then begin
361 if not
(find_existing_source c) then begin (* if not available source *)
362 if !verbose_msg_clients
then
363 lprintf_nl "UserDontReplyOnTime: (%s)" (clients_username
c);
365 dc_disconnect_client c (Closed_for_error
"User waiting timeout") (* disconnect connection anyway *)
368 c.client_error
<- UserDontReplyOnTime
373 (* Client connection closing handler *)
374 let client_disconnected sock reason
c =
375 (match c.client_sock
with
376 | Connection csock
->
377 if not
(sock
== csock
) then
378 if !verbose_msg_clients
|| !verbose_unexpected_messages
then
379 lprintf_nl " On (client_disconnected sock reason c) sock <> c.client_sock ?!?"
381 dc_disconnect_client c reason
383 (* Try to send connection messages to client, return true if sent *)
384 let try_connect_client c =
385 (* if connection_can_try c.client_connection_control then begin *)
386 (match c.client_user
with
389 List.iter
(fun s
-> (* find first server we are connected to iter users servers *)
390 (match s
.server_sock
with (* send to only one server *)
391 | Connection sock
-> (* if we are connected to this server already *)
392 if user.user_state
= TryingToSendFirstContact
then begin
393 if !!firewalled
then begin (* if we are in passive mode *)
394 user.user_state
<- UserActiveUserInitiating
; (* mark user to be connecting to/for us *)
396 let module C
= RevConnectToMe
in
398 C.orig
= s
.server_last_nick
;
399 C.dest
= user.user_nick
;
402 end else begin (* if we are in active mode *)
403 user.user_state
<- UserActiveMeInitiating
; (* mark user to be connecting us directly *)
405 let module C
= ConnectToMe
in
407 C.nick
= user.user_nick
;
408 C.ip
= CommonOptions.client_ip
(Some sock
);
415 | _
-> () ) (* do nothing if we are not already connected to this server *)
419 if user.user_state
= TryingToSendFirstContact
then begin (* if no connection try was sent at all *)
420 (match c.client_state
with
421 | DcDownloadConnecting
(f,_
) -> c.client_state
<- DcDownloadWaiting
f
422 | DcDownloadListConnecting _
-> c.client_state
<- DcDownloadListWaiting
423 | _
-> lprintf_nl "Wrong client state on trying to connect" );
424 new_client_error c UserNotReachable
;
425 user.user_state
<- UserIdle
; (* go back to waiting *)
428 | _
-> lprintf_nl "no user for client"; false )
430 lprintf_nl "c.client_connection_control denies connection to %s" c.client_name
433 (* Ask all files sources for download activation *)
434 let ask_file_sources_for_download f =
437 (match c.client_user
with
439 if (can_user_start_downloading u
) then begin (* check if download can be started *)
440 (match c.client_file
with
442 if (can_file_start_downloading f) then begin
443 c.client_state
<- DcDownloadConnecting
(f,current_time
());
444 u
.user_state
<- TryingToSendFirstContact
;
445 if try_connect_client c then raise
(Found_client
c)
448 end else begin (* otherwise do clients timeout checkings here *)
449 (match c.client_state
with
450 | DcDownloadListConnecting
(_
,_
,time
)
451 | DcDownloadConnecting
(_
,time
) ->
452 if (current_time
() -. time
) > float_of_int
!!client_timeout
then begin (* if waiting timeout is reached *)
453 new_client_error c UserDontReplyOnTime
;
461 | Found_client
c -> Some
c
462 | Not_found
-> None
)
464 (* Check a user pending downloads if they can be started, return client *)
465 let ask_user_for_download u
=
467 if (can_user_start_downloading u
) then begin (* check if download can be started *)
468 List.iter
(fun c -> (* that have clients ... *)
469 if (is_client_waiting
c) then begin
470 (match c.client_file
with
472 c.client_state
<- DcDownloadConnecting
(f,current_time
());
473 u
.user_state
<- TryingToSendFirstContact
;
474 if try_connect_client c then raise
(Found_client
c)
477 end else begin (* otherwise check possible clients timeout *)
478 (match c.client_state
with
479 | DcDownloadListConnecting
(_
,_
,time
)
480 | DcDownloadConnecting
(_
,time
) ->
481 if (current_time
() -. time
) > float_of_int
!!client_timeout
then begin (* if waiting timeout is reached *)
482 new_client_error c UserDontReplyOnTime
;
487 end else begin (* Check users, that have sent RevConnectToMe and we have sent ConnectToMe and we are waiting *)
492 | Found_client
c -> Some
c
493 | Not_found
-> None
)
495 (* Check all users pending downloads if they can be started *)
496 let ask_all_users_for_files () =
497 Hashtbl.iter
(fun _ u
-> (* with all users .. *)
498 ignore
(ask_user_for_download u
);
502 (* Try to resume all files *)
503 let try_to_resume_files () =
504 List.iter
(fun f -> (* with all files *)
506 ignore
(ask_file_sources_for_download f)
509 (* Get clients server and send contact messages to client *)
510 let init_connection c sock
=
511 c.client_receiving
<- Int64.zero
;
512 c.client_sock
<- Connection sock
;
513 connection_ok
c.client_connection_control
;
514 (match c.client_state
with
515 | DcConnectionStyle
(ClientActive _
) ->
516 (* we are sending to unknown client and have to decide correct state later *)
518 (match c.client_user
with
520 (match user.user_servers
with
521 | [] -> local_login
()
522 | s
:: _
-> s
.server_last_nick
) (* pick first servers nick that is known to both... *)
523 | _
-> local_login
() )
525 dc_send_msg sock
(MyNickReq
my_nick); (* send nick and lock requests to client *)
526 dc_send_msg sock
(LockReq
{
527 Lock.info
= empty_string
;
528 Lock.key
= DcKey.create_key
;
529 Lock.extended_protocol
= true
531 | DcDownloadListConnecting _
532 | DcConnectionStyle
(MeActive _
) -> ()
534 if !verbose_unexpected_messages
then lprintf_nl "In (init_connection) c.client_state was invalid";
537 let print_client_error sock txt
=
538 ignore
(Printf.sprintf
" %s (%s)" txt
539 (match find_sockets_client sock
with
540 | Some
c -> (clients_username
c)
541 | _
-> "(Client not found)" )
544 (* Client handler for if proper connection yet not exist *)
545 let client_handler sock event
=
547 | BASIC_EVENT LTIMEOUT
->
548 if !verbose_msg_clients
then
549 print_client_error sock
"BASIC_EVENT LTIMEOUT";
550 close sock Closed_for_timeout
551 | BASIC_EVENT RTIMEOUT
->
552 if !verbose_msg_clients
then
553 print_client_error sock
"BASIC_EVENT RTIMEOUT";
554 close sock Closed_for_timeout
555 (*| WRITE_DONE -> lprintf_nl " Event: WRITE_DONE";
556 | CAN_REFILL -> lprintf_nl " Event: CAN_REFILL";
557 | CONNECTED -> lprintf_nl " Event: CONNECTED";*)
559 if !verbose_msg_clients
then
560 print_client_error sock
"Event: BUFFER_OVERFLOW";
561 close sock Closed_for_overflow
562 (*| READ_DONE _ -> lprintf_nl " Event: READ_DONE";*)
563 | BASIC_EVENT
(CLOSED reason
) ->
564 (match find_sockets_client sock
with
566 if !verbose_msg_clients
then
567 lprintf_nl "BASIC_EVENT CLOSED: (%s) (%s)" (closing_reason_to_text reason
) (clients_username
c);
568 new_client_error c ClosedOnInit
;
569 dc_disconnect_client c reason
;
571 if !verbose_msg_clients
then
572 lprintf_nl "BASIC_EVENT CLOSED: No client exists for socket on CLOSE" )
573 | BASIC_EVENT WTIMEOUT
->
574 if !verbose_msg_clients
then
575 print_client_error sock
"BASIC_EVENT WTIMEOUT";
576 close sock Closed_for_timeout
577 (* | BASIC_EVENT CAN_READ -> lprintf_nl " Event: Other BASIC_EVENT CAN_READ"*)
578 (* | BASIC_EVENT CAN_WRITE -> lprintf_nl " Event: Other BASIC_EVENT CAN_WRITE"*)
581 (* Get first message from totally new client, return new client *)
582 let read_first_message t sock
=
584 | MyNickReq n
-> (* if very first client to client message is $MyNick, then continue... *)
585 let ip,port
as peer_addr
= TcpBufferedSocket.peer_addr sock
in
586 if !verbose_msg_clients
then lprintf_nl "Received FIRST MyNick with name %S from %s:%u" n
(Ip.to_string
ip) port
;
588 let u = search_user_by_name n
in (* check if user with this name exists *)
590 (match u.user_state
with
591 | UserActiveMeInitiating
-> (* client already present, find the right one *)
594 (match fc
.client_state
with
595 | DcDownloadListConnecting _
| DcDownloadConnecting _
-> raise
(Found_client fc
)
598 if !verbose_msg_clients
|| !verbose_unexpected_messages
then
599 lprintf_nl "In FIRST MyNick users client (%s) state not correct" u.user_nick
;
602 | Found_client fc
-> fc
)
603 | UserPassiveUserInitiating _
-> (* create new client *)
604 let c = new_client
() in
605 c.client_name
<- Some n
;
606 add_client_to_user
c u;
609 if !verbose_msg_clients
|| !verbose_unexpected_messages
then
610 lprintf_nl "In FIRST MyNick user (%s) state not correct" n
;
613 set_client_state
c (Connected
0);
614 TcpBufferedSocket.set_closer sock
(fun _ reason
-> client_disconnected sock reason
c);
615 (match c.client_state
with
616 | DcDownloadListConnecting _
-> ()
618 (match u.user_state
with
619 | UserPassiveUserInitiating _
->
620 c.client_state
<- DcConnectionStyle
(MeActive
(Download
0)) (* level is set after $Directions *)
621 | UserActiveMeInitiating
->
622 c.client_state
<- DcConnectionStyle
(MeActive
(Upload
0))
624 if !verbose_msg_clients
|| !verbose_unexpected_messages
then
625 lprintf_nl "Should not happen: In FIRST MyNick user (%s)" n
;
627 u.user_state
<- UserIdle
; (* initialize user_state for later correct usage *)
628 c.client_addr
<- Some peer_addr
;
629 init_connection c sock
;
630 Some
c (* return client *)
632 close sock
(Closed_for_error
"Closed in FIRST MyNick");
633 None
) (* return no client *)
634 | _
-> (* all other first messages are ignored and connection is closed *)
635 if !verbose_msg_clients
then
636 lprintf_nl "In FIRST message from client: not MyNick";
637 close sock
(Closed_for_error
"First message not MyNick");
638 None
) (* return no client *)
640 (* Get combination on own and client supports *)
641 let get_client_supports c = (* return ( xmlbzlist , adc ,tthf ) xmlbzlist means also ugetblock *)
642 let xmlbzlist , adc
, tthf
=
643 (match c.client_supports
with
645 (mldonkey_dc_client_supports
.xmlbzlist && c_supports
.xmlbzlist), (* own support && clients support *)
646 (mldonkey_dc_client_supports
.adcget
&& c_supports
.adcget
),
647 (mldonkey_dc_client_supports
.tthf
&& c_supports
.tthf
)
648 | None
-> false,false,false )
650 xmlbzlist , adc
, tthf
652 (* Send download commands to client *)
653 let dc_send_download_command c sock
=
654 let xmlbzlist, adc
, tthf
= get_client_supports c in
656 match c.client_state
with
658 let separator = String2.of_char '
/'
in
659 let fname = file
.file_directory ^
separator ^ file
.file_name
in
660 let preload_bytes = (* calculate preread bytes position *)
661 let from_pos = file_downloaded file
in
662 if from_pos < int64_kbyte
then begin (* if read under 1k bytes from client, start over *)
663 c.client_pos
<- Int64.zero
;
666 c.client_pos
<- from_pos;
670 c.client_preread_bytes_left
<- preload_bytes;
671 `Normal
(fname, file
.file_unchecked_tiger_root
), c.client_pos
-- (Int64.of_int
preload_bytes)
673 c.client_pos
<- Int64.zero
;
674 `List
(if xmlbzlist then mylistxmlbz2
else mylist
), c.client_pos
676 if !verbose_msg_clients
|| !verbose_download
then
678 let (fname,tth
) = match name with `Normal
(name,tth
) -> name,tth
| `List
name -> name,"" in
679 lprintf_nl "Sending $Get/$ADCGET: (%s)(%s)(%s)(%Ld)" (clients_username
c) fname tth
from_pos;
681 let msg = match adc
, tthf
, name with
682 | true, true, `Normal
(_
,tth
) when tth
<> "" ->
684 AdcGet.adctype
= AdcFile
(NameTTH tth
);
685 start_pos
= from_pos;
686 bytes
= Int64.minus_one
; (* TODO load file from from_pos to anywhere *)
689 | true, _
, `List
name ->
691 AdcGet.adctype
= AdcFile
(NameSpecial
name); (* FIXME AdcList *)
692 start_pos
= from_pos;
693 bytes
= Int64.minus_one
;
696 | _
, _
, (`Normal
(name,_
) | `List
name) ->
697 if xmlbzlist then (* if client supports ugetblock ...*)
699 UGetBlock.ufilename
= name;
700 UGetBlock.ubytes
= Int64.minus_one
;
701 UGetBlock.upos
= from_pos;
703 else (* else send normal GET *)
706 Get.pos
= Int64.succ
from_pos }
710 (* clients messages normal reader *)
711 let rec client_reader c t sock
=
716 (*if !verbose_msg_clients then lprintf_nl "Received $Direction (%s)" (clients_username c);*)
717 (match c.client_state
with
718 | DcDownloadListConnecting
(our_level
,_
,_
) (* We are downloading filelist *)
719 | DcConnectionStyle
(ClientActive
(Upload our_level
)) (* We are in passive mode *)
720 | DcConnectionStyle
(MeActive
(Upload our_level
)) -> (* We are in active mode, client needs to upload) *)
721 (match t
.Direction.direction
with
723 if !verbose_msg_clients
then
724 lprintf_nl "We have a conflict with (%s), both want to download..." (clients_username
c);
725 if (t
.Direction.level
> our_level
) then begin (* client gets to start download first *)
726 if !verbose_msg_clients
then lprintf_nl " Client won the election...";
727 (match c.client_state
with (* memorize list loading if that is the case *)
728 | DcConnectionStyle _
-> (* if file was tried to download ... *)
729 let nc = new_copy_client
c in
730 nc.client_sock
<- NoConnection
;
731 nc.client_addr
<- None
;
732 (match c.client_file
with
734 add_client_to_file
nc file
;
735 (match c.client_user
with
737 add_client_to_user
nc user;
739 nc.client_state
<- DcDownloadWaiting file
741 remove_client_from_clients_file
c
742 | _
-> (* DcDownloadListConnecting *) (* if filelist was tried to download *)
743 let nc = new_copy_client
c in
744 nc.client_sock
<- NoConnection
;
745 nc.client_addr
<- None
;
746 (match c.client_user
with
747 | Some
user -> add_client_to_user
nc user
749 nc.client_state
<- DcDownloadListWaiting
);
750 (* we change our direction *)
751 (match c.client_state
with (* check which one is the case *)
752 | DcConnectionStyle
(ClientActive
(Upload _
)) -> (* if client was initiating *)
753 c.client_state
<- DcConnectionStyle
(ClientActive
(Download
65535)) (* 65535 means to KeyReq that *)
754 | DcConnectionStyle
(MeActive
(Upload _
)) (* direction is changed *)
755 | DcDownloadListConnecting _
-> (* if we were initiating *)
756 c.client_state
<- DcConnectionStyle
(MeActive
(Download
65535))
758 (* we check in GetReq if we can start a new download immediately *)
760 end else if (t
.Direction.level
< our_level
) then begin (* we win and start downloading *)
761 if !verbose_msg_clients
then lprintf_nl " We won the election..."
762 end else (* otherwise close connection *)
763 if !verbose_msg_clients
then
764 lprintf_nl " Stalemate (levels are equal), closing";
765 close sock
(Closed_for_error
"Negotiation download: Stalemate" )
766 | _
-> () ) (* Upload *)
767 | DcConnectionStyle
(MeActive
(Download our_level
))
768 | DcConnectionStyle
(ClientActive
(Download our_level
)) -> (* connection is ready for uploading *)
769 (match t
.Direction.direction
with
770 | Upload level
-> (* Active mode and client wants to upload too ?? *)
771 if !verbose_msg_clients
then lprintf_nl "We have a conflict, both want to upload...";
772 (match c.client_state
with
773 | DcConnectionStyle MeActive _
->
774 if !verbose_msg_clients
then
775 lprintf_nl " and client (%s) is in passive mode" (clients_username
c)
777 if !verbose_msg_clients
then
778 lprintf_nl " and client (%s) is in active mode" (clients_username
c) );
779 close sock
(Closed_for_error
"Negotiation upload: conflict" );
780 | _
-> () ) (* Download *)
782 if !verbose_msg_clients
|| !verbose_unexpected_messages
then
783 lprintf_nl "In Direction: client state invalid";
784 close sock
(Closed_for_error
"Negotiation: client state invalid" ) )
787 | FailedReq errortxt
->
788 if !verbose_msg_clients
then begin
790 | ErrorReq _
-> lprintf_nl "Received (%s) from (%s)" errortxt
(clients_username
c)
791 | _
-> lprintf_nl "Received (%s) from (%s)" errortxt
(clients_username
c))
793 (match String2.split_simplify errortxt ' '
with
794 | [ _
; "File" ; txt1
; txt2
] ->
795 (* $Error File Not Available
796 $Error File not available *)
797 if (String.length txt1
= 3) && (txt2
.[1] = 'v'
) then new_client_error c FileNotAvailable
798 | _
-> lprintf_nl "New errortext: (%s) - make handling ??" errortxt
);
799 close sock
(Closed_for_error
(Printf.sprintf
"From client (%s): (%s)" (clients_username
c) errortxt
) )
804 if !verbose_msg_clients
then begin
806 | FileLengthReq _
-> lprintf_nl "Received $FileLength from (%s)" (clients_username
c)
807 | _
-> lprintf_nl "Received $AdcSnd from (%s)" (clients_username
c) ) (* AdcSnd *)
809 TcpBufferedSocket.set_rtimeout sock
(float !!client_read_timeout
);
810 (match c.client_state
with
814 | FileLengthReq t
-> t
815 | AdcSndReq t
-> (* check file current position with to be sended data position *)
816 let size = file_downloaded file
in
817 if !verbose_download
then
818 lprintf_nl "AdcSnd: file_downloaded=(%Ld) preread=(%d) start_pos=(%Ld)"
819 size c.client_preread_bytes_left t
.AdcSnd.start_pos
;
820 if size -- (Int64.of_int
c.client_preread_bytes_left
) = t
.AdcSnd.start_pos
then begin
821 if t
.AdcSnd.bytes = Int64.minus_one
then file_size file
else t
.AdcSnd.bytes
823 if !verbose_unexpected_messages
|| !verbose_download
then
824 lprintf_nl "AdcSnd: Current file=(%s) size=(%Ld) don't match start_pos=(%Ld) for user=(%s)"
825 file
.file_name
size t
.AdcSnd.start_pos
(clients_username
c);
828 | _
-> raise Not_found
)
830 c.client_receiving
<- bytes;
831 c.client_error
<- NoError
;
832 file_add file
.file_file FileDownloading
;
835 dc_send_msg sock SendReq
836 | _
-> () ) (* AdcSnd *)
838 | DcDownloadListConnecting _
->
839 let filelist_name = Filename.concat filelist_directory
(
840 (match c.client_user
with
842 (match c.client_supports
with
844 if c_supports
.xmlbzlist then u.user_nick ^ mylistxmlbz2_ext
845 else u.user_nick ^ mylist_ext
846 | None
-> u.user_nick ^ mylist_ext
)
847 | None
-> failwith
"No User" )
849 if !verbose_msg_clients
|| !verbose_download
then
850 lprintf_nl "Creating filelist with name: (%s)" filelist_name;
851 let filelist_fd = Unix32.create_rw
filelist_name in
854 | FileLengthReq t
-> t
855 | AdcSndReq t
-> (* check that adc client send the size of file in here *)
856 if t
.AdcSnd.bytes > Int64.zero
then t
.AdcSnd.bytes
857 else failwith
"Wrong bytes in AdcSnd"
858 | _
-> raise Not_found
)
860 c.client_state
<- DcDownloadList
filelist_fd;
861 c.client_receiving
<- bytes;
862 c.client_error
<- NoError
;
865 dc_send_msg sock SendReq
866 | _
-> () ) (* AdcSnd *)
868 failwith
"Nothing to download" )
870 if !verbose_unexpected_messages
then
871 lprintf_nl "Exception (%s) FileLength/AdcSnd:" (Printexc2.to_string e
);
872 close sock
(Closed_for_error
(Printexc2.to_string e
)) )
876 | UGetBlockReq _
-> (* TODO downloading a section of file *) (* TODO state checking ? *)
878 if (c.client_state
= DcUploadDoneWaitingForMore
) then begin (* if this is a continual loading *)
879 if !verbose_upload
|| !verbose_msg_clients
then lprintf_nl " Continuing upload/slot";
880 TcpBufferedSocket.set_lifetime sock infinite_timeout
; (* restore connection lifetime *)
883 let direction_change = (* memorize possible direction change *)
884 (match c.client_state
with
885 | DcConnectionStyle MeActive Download
65535
886 | DcConnectionStyle ClientActive Download
65535 -> true (* these mean direction change and we have lost *)
894 | AdcGetReq
{ AdcGet.zl
= true } ->
895 failwith
"ZLib not yet supported"
897 | AdcGetReq
{ AdcGet.adctype
= AdcList
(dir
,re1
) } -> `PartialList
(dir
,re1
)
899 | AdcGetReq
{ AdcGet.adctype
= AdcFile
(NameSpecial
name) }
900 | GetReq
{ Get.filename
= name }
901 | UGetBlockReq
{ UGetBlock.ufilename
= name }
902 when name = mylist
|| name = mylistxmlbz2
-> `FullList
name
904 | AdcGetReq
{ AdcGet.adctype
= AdcFile
(NameSpecial
name) } ->
905 failwith
("ADCGET special name not supported : " ^
name)
907 | AdcGetReq
{ AdcGet.adctype
= AdcFile
(NameTTH tth
); start_pos
=start
; bytes=bytes } ->
908 `File
(`TTH tth
, start
, bytes)
911 let name = String2.replace t
.Get.filename char92
"/" in
912 `File
(`Name
name, Int64.pred t
.Get.pos
, Int64.minus_one
)
915 let name = String2.replace t
.UGetBlock.ufilename char92
"/" in
916 `File
(`Name
name, t
.UGetBlock.upos
, t
.UGetBlock.ubytes
)
918 | _
-> failwith
"Unexpected request"
922 lprintf_nl "Client %S requested FullList %s" (clients_username
c) name;
924 let mylist_filename = Filename.concat directconnect_directory
name in
925 c.client_state
<- DcUploadListStarting
mylist_filename;
926 c.client_pos
<- Int64.zero
;
927 let size = Unix32.getsize
mylist_filename in
930 dc_send_msg sock
(AdcSndReq
{
931 AdcSnd.adctype
= t
.AdcGet.adctype
;
932 AdcSnd.start_pos
= 0L;
934 AdcSnd.zl
= false; (* CHECK *)
936 client_reader c SendReq sock
(* call ourselves again with send starting *)
937 | _
-> (* GetReq _ | UGetBlockReq _ *)
938 dc_send_msg sock
(FileLengthReq
size)
941 | `PartialList
(dir
,_re
) ->
942 lprintf_nl "Client %s requested PartialList %s" (clients_username
c) dir
;
944 let mylist = try DcShared.make_xml_mylist
(DcShared.find_dir_exn dir
)
945 with exn
-> failwith
(Printf.sprintf
"PartialList %s : %s" dir
(Printexc2.to_string exn
))
947 let filename = CommonFile.concat_file directconnect_directory
948 (Printf.sprintf
"mylist.%s.partial.xml.bz2" (clients_username
c))
950 DcShared.buffer_to_bz2_to_file
mylist filename;
951 c.client_state
<- DcUploadListStarting
filename;
952 c.client_pos
<- Int64.zero
;
953 let size = Int64.of_int
(Buffer.length
mylist) in
956 dc_send_msg sock
(AdcSndReq
{
957 AdcSnd.adctype
= t
.AdcGet.adctype
;
958 AdcSnd.start_pos
= 0L;
960 AdcSnd.zl
= false; (* CHECK *)
962 client_reader c SendReq sock
(* call ourselves again with send starting *)
963 | _
-> (* GetReq _ | UGetBlockReq _ *)
967 | `File
(name, start_pos
, bytes) -> (* client wants normal file *)
968 let dcsh = match name with
970 (try (* lets find file by tth *)
971 Hashtbl.find dc_shared_files_by_hash tth
973 failwith
(Printf.sprintf
"Shared file not found by tth %S" tth
))
975 (try (* so lets find filename then *)
976 Hashtbl.find dc_shared_files_by_codedname
fname
978 failwith
(Printf.sprintf
"Shared file not found by codedname %S" fname))
980 lprintf_nl "Client %S wants to download %S (%s) %Ld bytes from pos: %Ld" (clients_username
c)
981 dcsh.dc_shared_fullname
dcsh.dc_shared_tiger_root
bytes start_pos
;
982 (* check if upload still exists *)
983 c.client_pos
<- start_pos
;
984 let rem = dcsh.dc_shared_size
-- c.client_pos
in
985 if dc_can_upload
() || (counts_as_minislot
dcsh.dc_shared_size
) then
986 begin (* if free slots or file size *)
987 if not
(counts_as_minislot
dcsh.dc_shared_size
) then dc_insert_uploader
();(* increase uploaders *)
988 c.client_state
<- DcUploadStarting
(dcsh,start_pos
,bytes);
991 dc_send_msg sock
(AdcSndReq
{
992 AdcSnd.adctype
= t
.AdcGet.adctype
;
993 start_pos
= start_pos
;
995 zl
= false; (* CHECK *)
997 client_reader c SendReq sock
(* call ourselves again with send starting *)
998 | _
-> (* GetReq _ | UGetBlockReq _ *)
999 dc_send_msg sock
(FileLengthReq
rem) )
1002 (*lprintf_nl "Sending MaxedOut to (%s)" (clients_username c);*)
1003 dc_send_msg sock MaxedOutReq
;
1004 close sock
(Closed_for_error
("By us: Maxedout"))
1007 if !verbose_upload
then
1008 lprintf_nl "Error answering GET/ADCGET: %s" (Printexc2.to_string exn
);
1009 let errortxt = "File Not Available" in
1013 dc_send_msg sock
(ErrorReq
errortxt)
1014 | _
-> (* UGetBlockReq _ *)
1015 dc_send_msg sock
(FailedReq
errortxt)
1017 close sock
(Closed_for_error
("By us:" ^
errortxt))
1019 if direction_change then begin (* now the users clients states wont interfere this check *)
1020 (match c.client_user
with (* we can check if we can start new download immediately *)
1022 lprintf_nl "Because we lost conflict we now try to start new download from %s" user.user_nick
;
1023 ignore
(ask_user_for_download user)
1027 | GetListLenReq
-> ()
1030 (*lprintf_nl "Received $Key ... dumping it";*)
1031 (*lprintf_nl "Client state: %s" (client_state_to_string c);*)
1032 let level = Random.int 32767 in
1033 let send_downloading_command dir
c = (* inside Key function ... *)
1034 (match dir
with (* Send first $Get if necessary *)
1035 | Upload _
-> (* sent we want to download and client needs to be uploading part *)
1036 (match c.client_file
with (* here we set the downloading file back again to client state *)
1038 close sock
(Closed_for_error
"Nothing to download")
1040 c.client_state
<- DcDownload file
;
1041 dc_send_download_command c sock
)
1042 | _
-> () ) (* we are uploading and wait for $Get now *)
1044 (match c.client_state
with
1045 | DcDownloadListConnecting
(_
,passive
,time
) ->
1046 (match passive
with (* if we were/are in passive mode *)
1048 (*lprintf_nl "Connection state is: DcDownloadListConnecting )"; *)
1049 (match c.client_supports
with (* send $Supports if necessary *)
1051 | Some dc_client_supports
->
1052 dc_send_msg sock
( SupportsReq
(ClientSupports mldonkey_dc_client_supports
) ) );
1053 c.client_state
<- DcDownloadListConnecting
(level,true,time
); (* memorise $Direction level *)
1054 dc_send_msg sock
( DirectionReq
{
1055 Direction.direction
= Download
level; Direction.level = level } );
1056 dc_send_msg sock
( KeyReq
{ Key.key
= DcKey.calculate_key
c.client_lock
})
1058 dc_send_download_command c sock
;
1060 | DcConnectionStyle
( ClientActive dir
) ->
1061 (match dir
with (* check that direction was not changed on election *)
1062 | Download
65535 -> () (* if was, do nothing and wait the Get from client *)
1064 (match c.client_supports
with (* send $Supports if necessary *)
1066 | Some dc_client_supports
-> (* if EXTENDEDPROTOCOL supported by client, send own $Supports *)
1067 dc_send_msg sock
( SupportsReq
(ClientSupports mldonkey_dc_client_supports
) ) );
1069 (match dir
with (* send $Direction *)
1070 | Upload _
-> (* client seems to be uploading so ... *)
1071 c.client_state
<- DcConnectionStyle
(ClientActive
(Upload
level)); (* set level *)
1072 dc_send_msg sock
( DirectionReq
{ (* we thank and send Download *)
1073 Direction.direction
= Download
level; Direction.level = level } )
1074 | Download _
-> (* clients want to download from us ... *)
1075 (* we send possible no slot later *)
1076 c.client_state
<- DcConnectionStyle
(ClientActive
(Download
level));
1077 dc_send_msg sock
( DirectionReq
{ (* we prepare for uploading file *)
1078 Direction.direction
= Upload
level; Direction.level = level } ) );
1081 KeyReq
{ Key.key
= DcKey.calculate_key
c.client_lock
});
1083 send_downloading_command dir
c )
1085 | DcConnectionStyle
(MeActive dir
) ->
1086 (match dir
with (* check that direction was not changed on election *)
1087 | Download
65535 -> () (* if was, do nothing and wait the Get from client *)
1088 | _
-> send_downloading_command dir
c )
1093 (*lprintf_nl "Received $Lock";*)
1094 (*lprintf_nl "Client state: %s" (client_state_to_string c);*)
1095 c.client_lock
<- lock
.Lock.key
; (* save the clients lock for later use *)
1097 (match c.client_state
with
1098 | DcDownloadListConnecting _
1099 | DcConnectionStyle
( MeActive _
) -> (* we are answering to a connection initialized by passive client *)
1100 let dir = (* lets set dir to DcDownloadListConnecting also ... *)
1101 (match c.client_state
with
1102 | DcDownloadListConnecting
(level,_
,_
) -> Upload
level
1103 | DcConnectionStyle
( MeActive
dir ) -> dir
1107 (match c.client_user
with
1109 (match user.user_servers
with
1110 | [] -> local_login
()
1111 | s
:: _
-> s
.server_last_nick
) (* pick first servers nick that is known to both... *)
1112 | _
-> local_login
() )
1114 dc_send_msg sock
(MyNickReq
my_nick); (* send nick and lock requests to client *)
1115 dc_send_msg sock
(LockReq
{
1116 Lock.info
= empty_string
;
1117 Lock.key
= DcKey.create_key
;
1118 Lock.extended_protocol
= true
1120 dc_send_msg sock
( SupportsReq
(ClientSupports mldonkey_dc_client_supports
) );
1121 let level = Random.int 32767 in
1124 (match c.client_state
with
1125 | DcConnectionStyle _
-> c.client_state
<- DcConnectionStyle
( MeActive
(Upload
level))
1126 | _
-> () (* DcDownloadListConnecting *) );
1127 dc_send_msg sock
( DirectionReq
{
1128 Direction.direction
= Download
level; (* we are downloading *)
1129 Direction.level = level } )
1130 | Download _
-> (* we set level to 0 so that we lose possible conflict all the time purposely *)
1131 (match c.client_state
with
1132 | DcConnectionStyle _
-> c.client_state
<- DcConnectionStyle
( MeActive
(Download
0))
1133 | _
-> () (* DcDownloadListConnecting *) );
1134 dc_send_msg sock
( DirectionReq
{
1135 Direction.direction
= Upload
0; (* we are uploading *)
1136 Direction.level = level } ) );
1137 dc_send_msg sock
(KeyReq
{ Key.key
= DcKey.calculate_key
c.client_lock
} );
1138 if !verbose_msg_clients
then
1139 lprintf_nl "Sent answer to (%s) (MyNick,Lock,Supports,Direction,Key)" (clients_username
c)
1143 (*lprintf_nl "Received MaxedOut";*)
1144 new_client_error c NoFreeSlots
;
1145 close sock
(Closed_for_error
"MaxedOut from client")
1148 if !verbose_msg_clients
then
1149 lprintf_nl "Received Normal $MyNick with nick (%s)" n
;
1150 (*lprintf_nl "Client state: %s" (client_state_to_string c);*)
1151 connection_ok
c.client_connection_control
;
1153 let u = search_user_by_name n
in (* connect first correct user and client together *)
1154 (match u.user_state
with
1155 | UserActiveUserInitiating
-> (* RevConnect sent, another client present already *)
1156 (* Now we have to swap clients info *)
1158 List.iter
(fun fc
->
1159 (match c.client_state
with
1160 | DcConnectionStyle ClientActive Upload
0 -> raise
(Found_client fc
)
1163 failwith
"Not found client with correct state"
1165 | Found_client fc
->
1166 (match fc
.client_file
with
1167 | Some file
-> add_client_to_file
c file
;
1169 remove_client_from_clients_file fc
;
1172 | UserPassiveUserInitiating _
(* ConnectToMe sent as answer to RevConnect, should not hapen here *)
1173 | UserActiveMeInitiating
(* ConnectToMe sent, another client already present, should not happen in here *)
1175 failwith
"User state is wrong" );
1177 add_client_to_user
c u;
1178 c.client_name
<- Some
u.user_nick
;
1179 set_client_state
c (Connected
0);
1181 (match c.client_state
with (* now decide correct state *)
1182 | DcDownloadListConnecting _
-> () (* if client state is filelist downloading... *)
1184 (match u.user_state
with
1185 | UserActiveUserInitiating
-> (* we sent RevConnect ... *)
1186 c.client_state
<- DcConnectionStyle
(ClientActive
(Upload
0)); (* level assigned later *)
1187 | UserIdle
-> (* totally new connection initialized by client *)
1188 c.client_state
<- DcConnectionStyle
(ClientActive
(Download
0))
1190 failwith
"Invalid user state" )
1192 u.user_state
<- UserIdle
; (* not needed anymore *)
1194 if !verbose_unexpected_messages
|| !verbose_msg_clients
then
1195 lprintf_nl "In normal MyNick: (%s) when received nick=(%s)" (Printexc2.to_string e
) n
;
1196 close sock
(Closed_for_error
"Error in $MyNick") )
1199 (*lprintf_nl "Received or commanded $Send";*)
1201 (match c.client_state
with
1202 | DcUploadListStarting
fname ->
1203 let file_fd = Unix32.create_ro
fname in
1204 c.client_state
<- DcUploadList
file_fd;
1205 c.client_endpos
<- Unix32.getsize64
file_fd;
1206 let file = new_upfile None
file_fd (CommonUserDb.admin_user
()) in (* FIXME user? *)
1207 c.client_file
<- Some
file;
1208 set_clients_upload
c (as_file
file.file_file
);
1209 | DcUploadStarting
(dcsh,start_pos
,bytes) ->
1211 if bytes = Int64.minus_one
then dcsh.dc_shared_size
1213 let client_wants = start_pos
++ bytes in (* if client requests too much data *)
1214 if client_wants > dcsh.dc_shared_size
then failwith
"Start_pos + bytes > dcsh.dc_shared_size"
1218 let file_fd = Unix32.create_ro
dcsh.dc_shared_fullname
in
1219 c.client_state
<- DcUpload
(dcsh,file_fd,start_pos
,bytes);
1220 c.client_endpos
<- endpos;
1221 let file = new_upfile
(Some
dcsh) file_fd (CommonUserDb.admin_user
()) in (* FIXME user? *)
1222 c.client_file
<- Some
file;
1223 set_clients_upload
c (as_file
file.file_file
);
1224 | _
-> failwith
"Wrong client state in Send" );
1226 set_client_has_a_slot
(as_client
c.client_client
) NormalSlot
;
1227 (*client_enter_upload_queue (as_client c.client_client);*)
1228 TcpBufferedSocket.set_wtimeout sock
(float !!client_write_timeout
)
1231 lprintf_nl "Exception %s in upload creation" (Printexc2.to_string e
);
1232 close sock
(Closed_for_error
"Error in upload creation");
1233 failwith
"Error in upload creation" )
1235 | SupportsReq t
-> (* After EXTENDEDPROTOCOL support list from client ... *)
1236 (*lprintf_nl "Received $Supports";*)
1238 | ClientSupports t
-> c.client_supports
<- Some t
(* Save supports into clientdata *)
1243 if !verbose_unexpected_messages
|| !verbose_msg_clients
then begin
1244 let l = String.length s
in
1245 let txt = Printf.sprintf
"Unknown client message: (%s)" (clients_username
c) in
1246 if l > 50 then lprintf_nl "%s (%s...%d chars)" txt (shorten_string s
50) l
1247 else lprintf_nl "%s (%s)" txt s
1251 lprintf_nl "--> Unhandled client message. Implement ?:";
1252 DcProtocol.dc_print t
)
1254 (* Find next download from this user/client *)
1255 let find_next_client c =
1256 (match c.client_user
with
1259 if !verbose_download
then lprintf_nl "Trying to find next download to user (%s)" u.user_nick
;
1261 List.iter
(fun cl
-> (* check first if filelist is waiting ... *)
1262 (match cl
.client_state
with
1263 | DcDownloadListWaiting
-> raise
(Found_client cl
)
1265 ) u.user_clients
; (* then normal downloads ... *)
1266 List.iter
(fun cl
->
1267 (match cl
.client_state
with
1268 | DcDownloadWaiting _
-> raise
(Found_client cl
)
1271 None
(* return false to calling function that closes the socket *)
1273 | Found_client cl
-> (Some cl
) (* we have a next file with existing client to download *)
1277 (* Start next download from user and if change, current client <-> pending client *)
1278 (* Remove other client if not change *)
1279 let next_download change
c sock cl
= (* c is current connection, cl is the pending download *)
1280 (match cl
.client_state
with
1281 | DcDownloadWaiting
file ->
1282 if change
then begin (* we need to change current download with pending one *)
1283 (match c.client_state
with
1284 | DcDownload
f -> (* here we exchange pending client to existing client socket *)
1285 c.client_state
<- DcDownload
file;
1286 cl
.client_state
<- DcDownloadWaiting
f;
1287 remove_client_from_clients_file
c;
1288 remove_client_from_clients_file cl
;
1289 add_client_to_file
c file;
1290 add_client_to_file cl
f;
1293 remove_client_from_clients_file
c; (* because file commit removes the file <-> client connection also, *)
1294 (* this has to be done before assigning new file to this reused client, *)
1295 (* so that file remove don't erase this clients file *)
1296 add_client_to_file
c file; (* no change needed *)
1297 remove_client cl
; (* remove not needed client *)
1298 c.client_state
<- DcDownload
file;
1299 dc_send_download_command c sock
1301 | DcDownloadListWaiting
->
1302 if not change
then begin (* filelists changing not currently possible *)
1304 c.client_state
<- DcDownloadListConnecting
(0,!!firewalled
,nan
);
1305 dc_send_download_command c sock
1309 (* File is finished downloading, so remove file from clients list and client from files list *)
1310 let file_complete file =
1311 if !verbose_download
then lprintf_nl "File %s downloaded" file.file_name
;
1312 file_completed
(as_file
file.file_file
); (* update_file_state impl FileDownloaded; *)
1313 List.iter
(fun c -> (* remove this files clients except current connection *)
1314 (match c.client_state
with (* because we use this connection possibly for next download *)
1315 | DcDownload
f -> () (* only one client should be in this state *)
1320 let closing_text = "All files downloaded"
1321 (* Continue downloading from client that we have initialized *)
1322 let client_downloaded c sock nread
= (* TODO check tth while loading, abort if error *)
1323 if nread
> 0 then begin
1324 (match c.client_state
with
1325 | DcDownload
file ->
1326 let b = TcpBufferedSocket.buf sock
in
1328 if c.client_preread_bytes_left
> 0 then begin (* if precheck not yet done *)
1329 let check_bytes = min nread
c.client_preread_bytes_left
in (* which is smaller... *)
1330 let check_buffer = String.create
check_bytes in
1331 Unix32.read
(file_fd file) (c.client_pos
-- (Int64.of_int
c.client_preread_bytes_left
))
1332 check_buffer 0 check_bytes;
1333 let str2 = String.sub
b.buf
b.pos
check_bytes in
1334 if (String.compare
check_buffer str2) = 0 then begin (* if downloaded is ok *)
1335 c.client_preread_bytes_left
<- c.client_preread_bytes_left
- check_bytes;
1336 if c.client_preread_bytes_left
= 0 then begin (* if checked all preread bytes *)
1337 let downloaded = b.len
- check_bytes in
1338 if downloaded > 0 then begin (* check if buffer has bytes to write to file *)
1339 Unix32.write
(file_fd file) c.client_pos
b.buf
(b.pos
+check_bytes) downloaded
1341 Int64.of_int
downloaded
1343 end else begin (* if file check failed *)
1344 if !verbose_download
then
1345 lprintf_nl "Corrupted file (%s) download from (%s)" file.file_name
(clients_username
c);
1346 c.client_state
<- DcIdle
; (* now closing sock removes the client also *)
1347 close sock
(Closed_for_error
"Corrupted file");
1350 end else begin (* precheck done, normal flow *)
1351 Unix32.write
(file_fd file) c.client_pos
b.buf
b.pos
b.len
;
1355 c.client_pos
<- c.client_pos
++ downloaded;
1356 (match c.client_user
with
1357 | Some
u -> u.user_downloaded
<- u.user_downloaded
++ downloaded
1359 c.client_downloaded <- c.client_downloaded ++ downloaded;
1361 if c.client_pos
> (file_downloaded
file) then (* update downloading state *) (* TODO check tth while loading *)
1362 add_file_downloaded
(as_file
file.file_file
) (c.client_pos
-- (file_downloaded
file));
1363 if (file_downloaded
file) = (file_size
file) then begin
1365 c.client_receiving
<- Int64.zero
; (* this marks client as receiving normal commands again *)
1366 c.client_pos
<- Int64.zero
;
1367 TcpBufferedSocket.set_rtimeout sock infinite_timeout
; (* back to normal *)
1368 (* update myinfo ? *)
1369 (match (find_next_client c) with (* try to continue slot *)
1370 | Some cl
-> next_download false c sock cl
(* connected client , sock , client download_waiting *)
1372 c.client_state
<- DcIdle
; (* now closing sock removes the client also *)
1373 close sock
(Closed_for_error
closing_text) )
1376 | DcDownloadList
filelist_fd -> (* downloading file list *)
1377 let b = TcpBufferedSocket.buf sock
in
1378 let len = Int64.of_int
b.len in
1379 Unix32.write
filelist_fd c.client_pos
b.buf
b.pos
b.len;
1380 c.client_pos
<- c.client_pos
++ len;
1381 (match c.client_user
with
1382 | Some
u -> u.user_downloaded
<- u.user_downloaded
++ len
1384 c.client_downloaded <- c.client_downloaded ++ len;
1385 c.client_receiving
<- c.client_receiving
-- len;
1387 if c.client_receiving
= Int64.zero
then begin
1388 Unix32.close
filelist_fd;
1389 if !verbose_download
then lprintf_nl "Received filelist from (%s)" (clients_username
c);
1390 c.client_receiving
<- Int64.zero
; (* this marks client as receiving commands again *)
1391 c.client_pos
<- Int64.zero
;
1392 TcpBufferedSocket.set_rtimeout sock infinite_timeout
;
1393 (match (find_next_client c) with
1395 next_download false c sock cl
(* connected client , sock , client download_waiting *)
1397 c.client_state
<- DcIdle
;
1398 close sock
(Closed_for_error
closing_text) )
1400 | _
-> raise Not_found
)
1403 (* initialize a new connection when nothing is known from client *)
1404 let init_anon_client sock
=
1405 TcpBufferedSocket.set_read_controler sock download_control
;
1406 TcpBufferedSocket.set_write_controler sock upload_control
;
1407 TcpBufferedSocket.set_rtimeout sock infinite_timeout
; (* client timeouts *)
1408 TcpBufferedSocket.set_wtimeout sock infinite_timeout
;
1409 TcpBufferedSocket.set_reader sock
(dc_handler_client
(ref (None
))
1410 read_first_message client_reader client_downloaded)
1412 (* create listening socket for incoming connection, return socket or None *)
1413 let create_tcp_socket () =
1415 let sock = TcpServerSocket.create
"DC client listening" (Ip.to_inet_addr
!!client_bind_addr
) !!dc_port
1418 | TcpServerSocket.CONNECTION
(s
, Unix.ADDR_INET
(from_ip
, from_port
)) ->
1419 (*lprintf_nl "Listen: connection received from %s:%d"
1420 (Ip.to_string (Ip.of_inet_addr from_ip)) from_port; *)
1422 (* CHECK Allow this connection or not ? *)
1423 let token = create_token connection_manager
in
1424 let sock = TcpBufferedSocket.create
token
1425 "DC client connection" s
client_handler(*(fun _ _ -> ())*) in
1426 init_anon_client sock
1429 (*lprintf_nl "Created listening socket..." ;*)
1430 dc_tcp_listen_sock
:= Some
sock;
1431 (match (Unix.getsockname
(BasicSocket.fd
(TcpServerSocket.sock sock))) with
1432 | Unix.ADDR_INET
(addr
,port
) -> Some
sock
1434 with e
-> lprintf_nl "Exception %s while initializing DC listen socket" (Printexc2.to_string e
);
1439 (* Parse udp messages *)
1440 let udp_parse buf
sock =
1441 if !verbose_udp
then lprintf_nl "UDP Receive: (%s)" buf
;
1442 let str = String2.splitn buf ' '
1 in
1445 let module S
= SR
in
1446 let msg = S.parse
(String2.replace args '
|' empty_string
) in (* strip following '|' from message *)
1447 if msg.S.filename = empty_string
then ()
1448 (*lprintf_nl "This result seems to be directory result, we don't support it atm."*)
1451 let s = Hashtbl.find servers_by_ip
msg.S.server_ip
in
1452 received_new_search_result s msg;
1453 with _
-> if !verbose_udp
then
1454 lprintf_nl "UDP: Not valid ip-address (%s) in $SR" msg.S.server_ip
)
1456 | [cmd
] -> if !verbose_udp
then lprintf_nl "UDP: Unknown command %s" cmd
1457 | _
-> if !verbose_udp
then lprintf_nl "UDP: Unknown message %s" (String.escaped buf
) )
1460 let udp_send ip port m
=
1464 Buffer.add_char buf '
|'
;
1465 let s = Buffer.contents buf
in
1466 (match !dc_udp_sock
with
1468 (*if !verbose_udp || !verbose_msg_clients then lprintf_nl "UDP Send: (%s)" s;*)
1469 UdpSocket.write
sock false s ip port
1470 | None
-> failwith
"No UDP socket" );
1472 if !verbose_udp
|| !verbose_msg_clients
then
1473 lprintf_nl "Exception (%s) in UDP send" (Printexc2.to_string e
) )
1475 (* Udp event handling *)
1476 let udp_handler sock event
=
1478 | UdpSocket.READ_DONE
->
1479 UdpSocket.read_packets
sock (fun p
->
1481 let pbuf = p
.UdpSocket.udp_content
in
1482 let len = String.length
pbuf in
1489 (* create listening udp port *)
1490 let create_udp_socket () =
1492 let sock = UdpSocket.create
(Ip.to_inet_addr
!!client_bind_addr
) !!dc_port
1493 (fun sock event
-> udp_handler sock event
)
1495 dc_udp_sock
:= Some
sock;
1496 UdpSocket.set_write_controler
sock udp_write_controler
;
1499 lprintf_nl "Exception %s while binding UDP socket" (Printexc2.to_string e
);
1502 (* Start a connection to client *)
1503 let connect_client c =
1505 add_pending_connection connection_manager
(fun token ->
1507 match c.client_addr
with
1510 connection_try
c.client_connection_control
;
1511 let sock = TcpBufferedSocket.connect
token "client connection" (Ip.to_inet_addr
ip) port
1512 client_handler (*(fun _ _ -> ())*)
1514 TcpBufferedSocket.set_read_controler
sock download_control
; (* CommonGlobals.download_control *)
1515 TcpBufferedSocket.set_write_controler
sock upload_control
;
1516 TcpBufferedSocket.set_rtimeout
sock infinite_timeout
; (* client timeouts *)
1517 TcpBufferedSocket.set_wtimeout
sock infinite_timeout
;
1518 TcpBufferedSocket.set_closer
sock (fun _ reason
-> client_disconnected sock reason
c);
1519 TcpBufferedSocket.set_reader
sock (dc_handler_client
(ref (Some
c)) read_first_message
1520 client_reader client_downloaded);
1521 init_connection c sock; (* Send first answer messages to client *)
1523 lprintf_nl "Exception: %s, while connecting to client" (Printexc2.to_string e
);
1525 c.client_sock
<- ConnectionWaiting
token
1527 (* Upload to client *)
1528 let dc_upload c bytes =
1529 (match c.client_sock
with
1530 | Connection
sock ->
1532 if (bytes > 0) && can_write_len
sock bytes then begin
1533 (* update upload rate from len bytes *)
1534 (*Rate.update c.client_upload_rate (float_of_int len);*)
1537 count_filerequest c; *)
1539 (match c.client_state
with
1540 | DcUpload
(_
,fd
,_
,_
) -> fd
1541 | DcUploadList fd
-> fd
1542 | _
-> failwith
"No fd in upload" )
1544 let rlen = int64_min_int
(c.client_endpos
-- c.client_pos
) bytes in
1545 CommonUploads.consume_bandwidth
rlen;
1546 let upload_buffer = String.create
rlen in
1547 Unix32.read
file_fd c.client_pos
upload_buffer 0 rlen;
1548 TcpBufferedSocket.write
sock upload_buffer 0 rlen;
1549 (*lprintf_nl " Wrote (%d) bytes" rlen;*)
1550 let uploaded = Int64.of_int
rlen in
1551 c.client_pos
<- c.client_pos
++ uploaded;
1552 dc_total_uploaded
:= !dc_total_uploaded
++ uploaded;
1553 (match c.client_user
with
1554 | Some
u -> u.user_uploaded
<- u.user_uploaded
++ uploaded
1556 c.client_uploaded
<- c.client_uploaded
++ uploaded;
1557 (match c.client_state
with
1558 | DcUpload
(dcsh,_
,_
,_
) ->
1560 let sh = CommonUploads.find_by_name
dcsh.dc_shared_codedname
in
1561 sh.shared_impl
.impl_shared_uploaded
<- c.client_pos
;
1562 shared_must_update
(as_shared
sh.shared_impl
)
1565 if c.client_pos
= c.client_endpos
then begin
1566 if !verbose_upload
then lprintf_nl "Finished uploading to (%s)" (clients_username
c);
1567 Unix32.close
file_fd;
1568 set_refill
sock (fun _
-> () );
1569 (match c.client_state
with
1570 | DcUpload
(dcsh,_
,_
,_
) ->
1571 if not
(counts_as_minislot
dcsh.dc_shared_size
) then dc_remove_uploader
() (* slots *)
1573 c.client_state
<- DcUploadDoneWaitingForMore
;
1574 set_client_has_a_slot
(as_client
c.client_client
) NoSlot
; (* inform GUI *)
1575 TcpBufferedSocket.set_lifetime
sock (float !!wait_for_next_upload
);
1577 ready_for_upload
(as_client
c.client_client
);
1579 end else begin (* HMMM Is it ok to bang this line over and over again ? *)
1580 ready_for_upload
(as_client
c.client_client
)
1583 if !verbose_upload
then lprintf_nl "Exception (%s) in upload" (Printexc2.to_string e
);
1584 new_client_error c UploadError
)
1586 if !verbose_upload
then
1587 lprintf_nl "Socket not connected in uploading to (%s)" (clients_username
c);
1588 c.client_state
<- DcIdle
;
1589 set_client_has_a_slot
(as_client
c.client_client
) NoSlot
;
1590 dc_disconnect_client c (Closed_for_error
"No socket in upload") )
1594 (* register client operations *)
1596 client_ops
.op_client_info
<- (fun c ->
1597 let name = clients_username
c in
1598 let kind,total_downloaded
,total_uploaded
=
1600 (match c.client_addr
with
1601 | Some
(ip,port
) -> ip,port
1602 | None
-> Ip.null
,0 )
1604 (match c.client_user
with
1607 if (user_active
user) then Known_location
(ip,port
)
1608 else Indirect_location
(empty_string
,Md4.null
,ip,port
)
1610 kind,user.user_downloaded
,user.user_uploaded
1612 let kind = Indirect_location
(empty_string
,Md4.null
,ip,port
) in
1613 kind,Int64.zero
,Int64.zero
)
1615 let software, version
=
1616 match c.client_user
with
1617 | Some
u -> u.user_myinfo
.client_brand
, u.user_myinfo
.version
1618 | None
-> empty_string
, empty_string
1621 (match c.client_file
with
1622 | Some
file -> file.file_name
1625 { (impl_client_info
c.client_client
) with
1626 P.client_network
= network
.network_num
;
1627 P.client_kind
= kind;
1628 P.client_state
= client_state
(as_client
c.client_client
);
1629 P.client_type
= client_type
c;
1630 P.client_name
= name;
1631 P.client_num
= (client_num
(as_client
c.client_client
));
1632 P.client_connect_time
= c.client_connect_time
;
1633 P.client_software
= software;
1634 P.client_release
= version
;
1635 P.client_emulemod
= empty_string
;
1636 P.client_session_downloaded
= c.client_downloaded;
1637 P.client_session_uploaded
= c.client_uploaded
;
1638 P.client_total_downloaded
= total_downloaded
;
1639 P.client_total_uploaded
= total_uploaded
;
1640 P.client_upload
= Some
filename;
1641 P.client_sui_verified
= None
; (* new 2.6.5 *)
1642 (* P.client_sock_addr = ""; *)
1645 client_ops
.op_client_browse
<- (fun _ _ -> lprintf_nl "Received (op_client_browse)" );
1646 client_ops
.op_client_can_upload
<- (fun c bytes -> dc_upload c bytes );
1647 client_ops
.op_client_enter_upload_queue
<- (fun c ->
1648 if !verbose_msg_clients
|| !verbose_upload
then
1649 lprintf_nl "Client (%s) started to upload" (clients_username
c);
1650 ready_for_upload
(as_client
c.client_client
)
1654 mutable op_client_network : CommonTypes.network;
1655 mutable op_client_connect : 'a -> unit;
1656 mutable op_client_disconnect : 'a -> unit;
1657 mutable op_client_say : 'a -> string -> unit;
1658 mutable op_client_files : 'a -> (string * CommonTypes.result) list;
1659 mutable op_client_clear_files : 'a -> unit;
1660 mutable op_client_bprint : 'a -> Buffer.t -> unit;
1661 mutable op_client_dprint :
1662 'a -> CommonTypes.ui_conn -> CommonTypes.file -> unit;
1663 mutable op_client_dprint_html :
1664 'a -> CommonTypes.ui_conn -> CommonTypes.file -> string -> bool;
1665 mutable op_client_debug : 'a -> bool -> unit;