patch #7308
[mldonkey.git] / src / networks / direct_connect / dcClients.ml
blob7798f5ac0dd94eb2de3b6060d6f16d2ba6c0eead
1 (* Copyright 2001, 2002 b8_bavard, b8_fee_carabine, INRIA *)
2 (*
3 This file is part of mldonkey.
5 mldonkey is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 mldonkey is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with mldonkey; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 open CommonInteractive
21 open Printf2
22 open Int64ops
23 open Md4
24 open CommonUploads
25 open CommonInteractive
27 open CommonClient
28 open BasicSocket
29 open CommonOptions
30 open TcpBufferedSocket
31 open CommonGlobals
32 open CommonFile
33 open CommonUser
34 open CommonRoom
35 open CommonServer
36 open CommonResult
37 open CommonTypes
38 open CommonShared
39 open CommonSearch
40 open Options
42 open DcTypes
43 open DcOptions
44 open DcGlobals
45 open DcProtocol
47 let log_prefix = "[dcCli]"
49 let lprintf_nl fmt =
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 ...*)
56 | DcDownload 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 *)
68 remove_client c
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
77 | _ -> () );
78 c.client_state <- DcIdle;
79 remove_client c
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;
90 remove_client c
91 | DcUploadListStarting _
92 | DcUploadDoneWaitingForMore
93 | DcIdle ->
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;
97 remove_client c );
100 (* check that file can be started and no other client is downloading it *)
101 let can_file_start_downloading f =
102 (try
103 (match (file_state f) with (* check file state *)
104 | FileDownloaded | FileShared | FileCancelled | FileAborted _ | FilePaused -> raise BreakIter
105 | _ -> () );
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
108 ) f.file_clients;
109 true
110 with _ -> false )
112 (* Try to find alternative client to file *)
113 let find_downloadable_client_for_file file = (* CHECK possible user state also... *)
114 (try
115 List.iter (fun c -> (* chech all files sources *)
116 (match c.client_user with
117 | Some u ->
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 -> () )
126 | None -> () )
127 ) file.file_clients;
128 None
129 with
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
135 | Connection sock ->
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 ->
141 cancel_token token;
142 c.client_sock <- NoConnection;
143 | _ -> () );
144 set_client_state_on_disconnect c;
145 (match c.client_user with
146 | Some user ->
147 user.user_state <- UserIdle; (* initialize also possible user *)
148 | _ -> () )
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
160 | Some f ->
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 *)
165 | _ -> () )
167 (* Move client to first in users clientlist *)
168 let move_client_to_first_in_userslist c =
169 (match c.client_user with
170 | Some u ->
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 *)
175 | _ -> () )
177 (* Move client to last in users clientlist *)
178 let move_client_to_last_in_userslist c =
179 (match c.client_user with
180 | Some u ->
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];
185 | _ -> () )
187 (* Move client to last in users clientlist *)
188 let move_client_to_last_in_fileslist c =
189 (match c.client_file with
190 | Some f ->
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];
195 | _ -> () )
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
200 | Some f ->
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;
206 true
207 | None -> false )
208 | None -> false )
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;
224 } in
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
234 | None -> ()
235 | Some q ->
236 let module S = SR in
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
243 (try
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
246 (try
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
249 ) user.user_clients;
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 *)
257 end )
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
264 { G.search_num = 0;
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
276 | _ -> () );
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";*)
287 (try
288 List.iter (fun f ->
289 (try
290 List.iter (fun c -> (* lets check that file is not already being downloaded *)
291 if is_client_blocking_downloading c then raise BreakIter
292 ) f.file_clients;
293 if (List.length f.file_clients < !!max_sources_file) &&
294 (is_valid_tiger_hash f.file_unchecked_tiger_root) then begin
295 create_new_search f;
296 raise (Found_file f);
297 end
298 with
299 | BreakIter -> () );
300 ) !current_files;
301 (*lprintf_nl "Autosearch end not found"; *)
302 with Found_file f ->
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;
312 (match error with
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 *)
322 | _ ->
323 move_client_to_last_in_userslist c;
324 move_client_to_last_in_fileslist c;
326 | UserNotReachable ->
327 c.client_error <- UserNotReachable;
328 | ClosedOnInit ->
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);
337 end else begin
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);
349 end else begin
350 c.client_error <- ConnectionResetByPeer
352 | UploadError ->
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);
364 end;
365 dc_disconnect_client c (Closed_for_error "User waiting timeout") (* disconnect connection anyway *)
367 end else begin
368 c.client_error <- UserDontReplyOnTime
370 | NoError -> () );
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 ?!?"
380 | _ -> () );
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
387 | Some user ->
388 (try
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 *)
395 dc_send_msg sock (
396 let module C = RevConnectToMe in
397 RevConnectToMeReq {
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 *)
404 dc_send_msg sock (
405 let module C = ConnectToMe in
406 ConnectToMeReq {
407 C.nick = user.user_nick;
408 C.ip = CommonOptions.client_ip (Some sock);
409 C.port = !!dc_port;
412 end;
413 raise BreakIter
415 | _ -> () ) (* do nothing if we are not already connected to this server *)
416 ) user.user_servers;
417 with _ -> () );
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 *)
426 false
427 end else true
428 | _ -> lprintf_nl "no user for client"; false )
429 (* end else begin
430 lprintf_nl "c.client_connection_control denies connection to %s" c.client_name
431 end *)
433 (* Ask all files sources for download activation *)
434 let ask_file_sources_for_download f =
435 (try
436 List.iter (fun c ->
437 (match c.client_user with
438 | Some u ->
439 if (can_user_start_downloading u) then begin (* check if download can be started *)
440 (match c.client_file with
441 | Some f ->
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)
447 | _ -> () )
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;
455 | _ -> () )
457 | _ -> () )
458 ) f.file_clients;
459 None
460 with
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 =
466 (try
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
471 | Some f ->
472 c.client_state <- DcDownloadConnecting (f,current_time ());
473 u.user_state <- TryingToSendFirstContact;
474 if try_connect_client c then raise (Found_client c)
475 else raise Not_found
476 | _ -> () )
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;
484 | _ -> () )
486 ) u.user_clients;
487 end else begin (* Check users, that have sent RevConnectToMe and we have sent ConnectToMe and we are waiting *)
488 check_passive_user u
489 end;
490 None
491 with
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);
499 ) users_by_name;
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)
507 ) !current_files
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 *)
517 let my_nick =
518 (match c.client_user with
519 | Some user ->
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 _ ) -> ()
533 | _ ->
534 if !verbose_unexpected_messages then lprintf_nl "In (init_connection) c.client_state was invalid";
535 raise Not_found )
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 =
546 (match event with
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";*)
558 | BUFFER_OVERFLOW ->
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
565 | Some c ->
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;
570 | _ ->
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"*)
579 | _ -> () )
581 (* Get first message from totally new client, return new client *)
582 let read_first_message t sock =
583 (match t with
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;
587 (try
588 let u = search_user_by_name n in (* check if user with this name exists *)
589 let c =
590 (match u.user_state with
591 | UserActiveMeInitiating -> (* client already present, find the right one *)
592 (try
593 List.iter (fun fc ->
594 (match fc.client_state with
595 | DcDownloadListConnecting _ | DcDownloadConnecting _ -> raise (Found_client fc)
596 | _ -> () )
597 ) u.user_clients;
598 if !verbose_msg_clients || !verbose_unexpected_messages then
599 lprintf_nl "In FIRST MyNick users client (%s) state not correct" u.user_nick;
600 raise Not_found
601 with
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;
608 | _ ->
609 if !verbose_msg_clients || !verbose_unexpected_messages then
610 lprintf_nl "In FIRST MyNick user (%s) state not correct" n;
611 raise Not_found )
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 _ -> ()
617 | _ ->
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))
623 | _ ->
624 if !verbose_msg_clients || !verbose_unexpected_messages then
625 lprintf_nl "Should not happen: In FIRST MyNick user (%s)" n;
626 raise Not_found ) );
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 *)
631 with _ ->
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
644 | Some c_supports ->
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
655 let name, from_pos =
656 match c.client_state with
657 | DcDownload file ->
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;
665 end else begin
666 c.client_pos <- from_pos;
667 !dc_download_preread
670 c.client_preread_bytes_left <- preload_bytes;
671 `Normal (fname, file.file_unchecked_tiger_root), c.client_pos -- (Int64.of_int preload_bytes)
672 | _ ->
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
677 begin
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;
680 end;
681 let msg = match adc, tthf, name with
682 | true, true, `Normal (_,tth) when tth <> "" ->
683 AdcGetReq {
684 AdcGet.adctype = AdcFile (NameTTH tth);
685 start_pos = from_pos;
686 bytes = Int64.minus_one; (* TODO load file from from_pos to anywhere *)
687 zl = false;
689 | true, _, `List name ->
690 AdcGetReq {
691 AdcGet.adctype = AdcFile (NameSpecial name); (* FIXME AdcList *)
692 start_pos = from_pos;
693 bytes = Int64.minus_one;
694 zl = false;
696 | _, _, (`Normal (name,_) | `List name) ->
697 if xmlbzlist then (* if client supports ugetblock ...*)
698 UGetBlockReq {
699 UGetBlock.ufilename = name;
700 UGetBlock.ubytes = Int64.minus_one;
701 UGetBlock.upos = from_pos;
703 else (* else send normal GET *)
704 GetReq {
705 Get.filename = name;
706 Get.pos = Int64.succ from_pos }
708 dc_send_msg sock msg
710 (* clients messages normal reader *)
711 let rec client_reader c t sock =
713 (match t with
715 | DirectionReq t ->
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
722 | Download _ ->
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
733 | Some file ->
734 add_client_to_file nc file;
735 (match c.client_user with
736 | Some user ->
737 add_client_to_user nc user;
738 | _ -> () );
739 nc.client_state <- DcDownloadWaiting file
740 | None -> () );
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
748 | _ -> () );
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))
757 | _ -> () );
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)
776 | _ ->
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 *)
781 | _ ->
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" ) )
786 | ErrorReq errortxt
787 | FailedReq errortxt ->
788 if !verbose_msg_clients then begin
789 (match t with
790 | ErrorReq _ -> lprintf_nl "Received (%s) from (%s)" errortxt (clients_username c)
791 | _ -> lprintf_nl "Received (%s) from (%s)" errortxt (clients_username c))
792 end;
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) )
801 | FileLengthReq _
802 | AdcSndReq _ ->
803 (try
804 if !verbose_msg_clients then begin
805 (match t with
806 | FileLengthReq _ -> lprintf_nl "Received $FileLength from (%s)" (clients_username c)
807 | _ -> lprintf_nl "Received $AdcSnd from (%s)" (clients_username c) ) (* AdcSnd *)
808 end;
809 TcpBufferedSocket.set_rtimeout sock (float !!client_read_timeout);
810 (match c.client_state with
811 | DcDownload file ->
812 let bytes =
813 (match t 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
822 end else begin
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);
826 raise Not_found
828 | _ -> raise Not_found )
830 c.client_receiving <- bytes;
831 c.client_error <- NoError;
832 file_add file.file_file FileDownloading;
833 (match t with
834 | FileLengthReq _ ->
835 dc_send_msg sock SendReq
836 | _ -> () ) (* AdcSnd *)
838 | DcDownloadListConnecting _ ->
839 let filelist_name = Filename.concat filelist_directory (
840 (match c.client_user with
841 | Some u ->
842 (match c.client_supports with
843 | Some c_supports ->
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" )
848 ) in
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
852 let bytes =
853 (match t with
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;
863 (match t with
864 | FileLengthReq _ ->
865 dc_send_msg sock SendReq
866 | _ -> () ) (* AdcSnd *)
867 | _ ->
868 failwith "Nothing to download" )
869 with e ->
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)) )
874 | AdcGetReq _
875 | GetReq _
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 *)
881 end;
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 *)
887 | _ -> false );
890 begin try
892 let req =
893 match t with
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)
910 | GetReq t ->
911 let name = String2.replace t.Get.filename char92 "/" in
912 `File (`Name name, Int64.pred t.Get.pos, Int64.minus_one)
914 | UGetBlockReq t ->
915 let name = String2.replace t.UGetBlock.ufilename char92 "/" in
916 `File (`Name name, t.UGetBlock.upos, t.UGetBlock.ubytes)
918 | _ -> failwith "Unexpected request"
920 match req with
921 | `FullList name ->
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
928 begin match t with
929 | AdcGetReq t ->
930 dc_send_msg sock (AdcSndReq {
931 AdcSnd.adctype = t.AdcGet.adctype;
932 AdcSnd.start_pos = 0L;
933 AdcSnd.bytes = size;
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 = Filename.concat directconnect_directory
948 (DcGlobals.safe_filename (Printf.sprintf "mylist.%s.partial.xml" (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
954 begin match t with
955 | AdcGetReq t ->
956 dc_send_msg sock (AdcSndReq {
957 AdcSnd.adctype = t.AdcGet.adctype;
958 AdcSnd.start_pos = 0L;
959 AdcSnd.bytes = size;
960 AdcSnd.zl = false; (* CHECK *)
962 client_reader c SendReq sock (* call ourselves again with send starting *)
963 | _ -> (* GetReq _ | UGetBlockReq _ *)
964 assert false
967 | `File (name, start_pos, bytes) -> (* client wants normal file *)
968 let dcsh = match name with
969 | `TTH tth ->
970 (try (* lets find file by tth *)
971 Hashtbl.find dc_shared_files_by_hash tth
972 with _ ->
973 failwith (Printf.sprintf "Shared file not found by tth %S" tth))
974 | `Name fname ->
975 (try (* so lets find filename then *)
976 Hashtbl.find dc_shared_files_by_codedname fname
977 with _ ->
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);
989 (match t with
990 | AdcGetReq t ->
991 dc_send_msg sock (AdcSndReq {
992 AdcSnd.adctype = t.AdcGet.adctype;
993 start_pos = start_pos;
994 bytes = bytes;
995 zl = false; (* CHECK *)
996 } );
997 client_reader c SendReq sock (* call ourselves again with send starting *)
998 | _ -> (* GetReq _ | UGetBlockReq _ *)
999 dc_send_msg sock (FileLengthReq rem) )
1001 end else begin
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"))
1006 with exn ->
1007 if !verbose_upload then
1008 lprintf_nl "Error answering GET/ADCGET: %s" (Printexc2.to_string exn);
1009 let errortxt = "File Not Available" in
1010 begin match t with
1011 | AdcGetReq _
1012 | GetReq _ ->
1013 dc_send_msg sock (ErrorReq errortxt)
1014 | _ -> (* UGetBlockReq _ *)
1015 dc_send_msg sock (FailedReq errortxt)
1016 end;
1017 close sock (Closed_for_error ("By us:" ^ errortxt))
1018 end;
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 *)
1021 | Some user ->
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)
1024 | _ -> () );
1027 | GetListLenReq -> ()
1029 | KeyReq _ ->
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 *)
1037 | None ->
1038 close sock (Closed_for_error "Nothing to download")
1039 | Some file ->
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 *)
1047 | true ->
1048 (*lprintf_nl "Connection state is: DcDownloadListConnecting )"; *)
1049 (match c.client_supports with (* send $Supports if necessary *)
1050 | None -> ()
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 })
1057 | _ -> () );
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 *)
1063 | _ ->
1064 (match c.client_supports with (* send $Supports if necessary *)
1065 | None -> ()
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 } ) );
1080 dc_send_msg sock (
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 )
1090 | _ -> () )
1092 | LockReq lock ->
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
1104 | _ -> Upload 0 )
1106 let my_nick =
1107 (match c.client_user with
1108 | Some user ->
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
1119 } );
1120 dc_send_msg sock ( SupportsReq (ClientSupports mldonkey_dc_client_supports) );
1121 let level = Random.int 32767 in
1122 (match dir with
1123 | Upload _ ->
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)
1140 | _ -> () )
1142 | MaxedOutReq ->
1143 (*lprintf_nl "Received MaxedOut";*)
1144 new_client_error c NoFreeSlots;
1145 close sock (Closed_for_error "MaxedOut from client")
1147 | MyNickReq n ->
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;
1152 (try
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 *)
1157 (try
1158 List.iter (fun fc ->
1159 (match c.client_state with
1160 | DcConnectionStyle ClientActive Upload 0 -> raise (Found_client fc)
1161 | _ -> () )
1162 ) u.user_clients;
1163 failwith "Not found client with correct state"
1164 with
1165 | Found_client fc ->
1166 (match fc.client_file with
1167 | Some file -> add_client_to_file c file;
1168 | _ -> () );
1169 remove_client_from_clients_file fc;
1170 remove_client fc )
1171 | UserIdle -> ()
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 *)
1174 | _ ->
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... *)
1183 | _ ->
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))
1189 | _ ->
1190 failwith "Invalid user state" )
1192 u.user_state <- UserIdle; (* not needed anymore *)
1193 with e ->
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") )
1198 | SendReq ->
1199 (*lprintf_nl "Received or commanded $Send";*)
1200 (try
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 in
1207 c.client_file <- Some file;
1208 set_clients_upload c (as_file file.file_file);
1209 | DcUploadStarting (dcsh,start_pos,bytes) ->
1210 let endpos =
1211 if bytes = Int64.minus_one then dcsh.dc_shared_size
1212 else begin
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"
1215 else client_wants
1216 end
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 in
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)
1230 with e ->
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";*)
1237 (match t with
1238 | ClientSupports t -> c.client_supports <- Some t (* Save supports into clientdata *)
1239 | _ -> () )
1241 | UnknownReq s ->
1242 if s <> "" then
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
1250 | _ ->
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
1257 | None -> None
1258 | Some u ->
1259 if !verbose_download then lprintf_nl "Trying to find next download to user (%s)" u.user_nick;
1260 (try
1261 List.iter (fun cl -> (* check first if filelist is waiting ... *)
1262 (match cl.client_state with
1263 | DcDownloadListWaiting -> raise (Found_client cl)
1264 | _ -> () )
1265 ) u.user_clients; (* then normal downloads ... *)
1266 List.iter (fun cl ->
1267 (match cl.client_state with
1268 | DcDownloadWaiting _ -> raise (Found_client cl)
1269 | _ -> () )
1270 ) u.user_clients;
1271 None (* return false to calling function that closes the socket *)
1272 with
1273 | Found_client cl -> (Some cl) (* we have a next file with existing client to download *)
1274 | _ -> None )
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;
1291 | _ -> () )
1292 end else begin
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 *)
1303 remove_client cl;
1304 c.client_state <- DcDownloadListConnecting (0,!!firewalled,nan);
1305 dc_send_download_command c sock
1307 | _ -> () )
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 *)
1316 | _ ->
1317 remove_client c )
1318 ) file.file_clients
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
1327 let downloaded =
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
1340 end;
1341 Int64.of_int downloaded
1342 end else Int64.zero
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");
1348 Int64.zero
1350 end else begin (* precheck done, normal flow *)
1351 Unix32.write (file_fd file) c.client_pos b.buf b.pos b.len;
1352 Int64.of_int 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
1358 | _ -> () );
1359 c.client_downloaded <- c.client_downloaded ++ downloaded;
1360 buf_used b b.len;
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
1364 file_complete file;
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 *)
1371 | None ->
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
1383 | _ -> () );
1384 c.client_downloaded <- c.client_downloaded ++ len;
1385 c.client_receiving <- c.client_receiving -- len;
1386 buf_used b b.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
1394 | Some cl ->
1395 next_download false c sock cl (* connected client , sock , client download_waiting *)
1396 | None ->
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 () =
1414 (try
1415 let sock = TcpServerSocket.create "DC client listening" (Ip.to_inet_addr !!client_bind_addr) !!dc_port
1416 (fun sock event ->
1417 match event with
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
1427 | _ -> () )
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
1433 | _ -> None )
1434 with e -> lprintf_nl "Exception %s while initializing DC listen socket" (Printexc2.to_string e);
1435 None )
1437 (* UDP *)
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
1443 (match str with
1444 | [cmd; args] ->
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."*)
1449 else begin
1450 (try
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) )
1459 (* Udp sending *)
1460 let udp_send ip port m =
1461 (try
1462 Buffer.reset buf;
1463 dc_write buf m;
1464 Buffer.add_char buf '|';
1465 let s = Buffer.contents buf in
1466 (match !dc_udp_sock with
1467 | Some sock ->
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" );
1471 with e ->
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 =
1477 match event with
1478 | UdpSocket.READ_DONE ->
1479 UdpSocket.read_packets sock (fun p ->
1480 (try
1481 let pbuf = p.UdpSocket.udp_content in
1482 let len = String.length pbuf in
1483 if len > 0 then
1484 udp_parse pbuf sock
1485 with e -> () )
1487 | _ -> ()
1489 (* create listening udp port *)
1490 let create_udp_socket () =
1491 (try
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;
1497 Some sock
1498 with e ->
1499 lprintf_nl "Exception %s while binding UDP socket" (Printexc2.to_string e);
1500 None )
1502 (* Start a connection to client *)
1503 let connect_client c =
1504 let token =
1505 add_pending_connection connection_manager (fun token ->
1507 match c.client_addr with
1508 | None -> ()
1509 | Some (ip,port) ->
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 *)
1522 with e ->
1523 lprintf_nl "Exception: %s, while connecting to client" (Printexc2.to_string e);
1524 ) in
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 ->
1531 (try
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);*)
1535 (* update stats *)
1536 (*ignore (
1537 count_filerequest c; *)
1538 let file_fd =
1539 (match c.client_state with
1540 | DcUpload (_,fd,_,_) -> fd
1541 | DcUploadList fd -> fd
1542 | _ -> failwith "No fd in upload" )
1544 let rlen =
1545 let rem = Int64.to_int (c.client_endpos -- c.client_pos) in
1546 if rem > bytes then bytes else rem
1548 CommonUploads.consume_bandwidth rlen;
1549 let upload_buffer = String.create rlen in
1550 Unix32.read file_fd c.client_pos upload_buffer 0 rlen;
1551 TcpBufferedSocket.write sock upload_buffer 0 rlen;
1552 (*lprintf_nl " Wrote (%d) bytes" rlen;*)
1553 let uploaded = Int64.of_int rlen in
1554 c.client_pos <- c.client_pos ++ uploaded;
1555 dc_total_uploaded := !dc_total_uploaded ++ uploaded;
1556 (match c.client_user with
1557 | Some u -> u.user_uploaded <- u.user_uploaded ++ uploaded
1558 | _ -> () );
1559 c.client_uploaded <- c.client_uploaded ++ uploaded;
1560 (match c.client_state with
1561 | DcUpload (dcsh,_,_,_) ->
1562 (try
1563 let sh = CommonUploads.find_by_name dcsh.dc_shared_codedname in
1564 sh.shared_impl.impl_shared_uploaded <- c.client_pos;
1565 shared_must_update (as_shared sh.shared_impl)
1566 with _ -> () )
1567 | _ -> () );
1568 if c.client_pos = c.client_endpos then begin
1569 if !verbose_upload then lprintf_nl "Finished uploading to (%s)" (clients_username c);
1570 Unix32.close file_fd;
1571 set_refill sock (fun _ -> () );
1572 (match c.client_state with
1573 | DcUpload (dcsh,_,_,_) ->
1574 if not (counts_as_minislot dcsh.dc_shared_size) then dc_remove_uploader () (* slots *)
1575 | _ -> () );
1576 c.client_state <- DcUploadDoneWaitingForMore;
1577 set_client_has_a_slot (as_client c.client_client) NoSlot; (* inform GUI *)
1578 TcpBufferedSocket.set_lifetime sock (float !!wait_for_next_upload);
1579 end else begin
1580 ready_for_upload (as_client c.client_client);
1582 end else begin (* HMMM Is it ok to bang this line over and over again ? *)
1583 ready_for_upload (as_client c.client_client)
1585 with e ->
1586 if !verbose_upload then lprintf_nl "Exception (%s) in upload" (Printexc2.to_string e);
1587 new_client_error c UploadError )
1588 | _ ->
1589 if !verbose_upload then
1590 lprintf_nl "Socket not connected in uploading to (%s)" (clients_username c);
1591 c.client_state <- DcIdle;
1592 set_client_has_a_slot (as_client c.client_client) NoSlot;
1593 dc_disconnect_client c (Closed_for_error "No socket in upload") )
1595 module P = GuiTypes
1597 (* register client operations *)
1598 let _ =
1599 client_ops.op_client_info <- (fun c ->
1600 let name = clients_username c in
1601 let kind,total_downloaded,total_uploaded =
1602 let ip,port =
1603 (match c.client_addr with
1604 | Some (ip,port) -> ip,port
1605 | None -> Ip.null,0 )
1607 (match c.client_user with
1608 | Some user ->
1609 let kind =
1610 if (user_active user) then Known_location (ip,port)
1611 else Indirect_location (empty_string,Md4.null,ip,port)
1613 kind,user.user_downloaded,user.user_uploaded
1614 | _ ->
1615 let kind = Indirect_location (empty_string,Md4.null,ip,port) in
1616 kind,Int64.zero,Int64.zero )
1618 let software, version =
1619 match c.client_user with
1620 | Some u -> u.user_myinfo.client_brand, u.user_myinfo.version
1621 | None -> empty_string, empty_string
1623 let filename =
1624 (match c.client_file with
1625 | Some file -> file.file_name
1626 | _ -> "" )
1628 { (impl_client_info c.client_client) with
1629 P.client_network = network.network_num;
1630 P.client_kind = kind;
1631 P.client_state = client_state (as_client c.client_client);
1632 P.client_type = client_type c;
1633 P.client_name = name;
1634 P.client_num = (client_num (as_client c.client_client));
1635 P.client_connect_time = c.client_connect_time;
1636 P.client_software = software;
1637 P.client_release = version;
1638 P.client_emulemod = empty_string;
1639 P.client_session_downloaded = c.client_downloaded;
1640 P.client_session_uploaded = c.client_uploaded;
1641 P.client_total_downloaded = total_downloaded;
1642 P.client_total_uploaded = total_uploaded;
1643 P.client_upload = Some filename;
1644 P.client_sui_verified = None; (* new 2.6.5 *)
1645 (* P.client_sock_addr = ""; *)
1648 client_ops.op_client_browse <- (fun _ _ -> lprintf_nl "Received (op_client_browse)" );
1649 client_ops.op_client_can_upload <- (fun c bytes -> dc_upload c bytes );
1650 client_ops.op_client_enter_upload_queue <- (fun c ->
1651 if !verbose_msg_clients || !verbose_upload then
1652 lprintf_nl "Client (%s) started to upload" (clients_username c);
1653 ready_for_upload (as_client c.client_client)
1657 mutable op_client_network : CommonTypes.network;
1658 mutable op_client_connect : 'a -> unit;
1659 mutable op_client_disconnect : 'a -> unit;
1660 mutable op_client_say : 'a -> string -> unit;
1661 mutable op_client_files : 'a -> (string * CommonTypes.result) list;
1662 mutable op_client_clear_files : 'a -> unit;
1663 mutable op_client_bprint : 'a -> Buffer.t -> unit;
1664 mutable op_client_dprint :
1665 'a -> CommonTypes.ui_conn -> CommonTypes.file -> unit;
1666 mutable op_client_dprint_html :
1667 'a -> CommonTypes.ui_conn -> CommonTypes.file -> string -> bool;
1668 mutable op_client_debug : 'a -> bool -> unit;