patch #7308
[mldonkey.git] / src / networks / direct_connect / dcInteractive.ml
blobc9c92ec3ad33658d3d617690e1724695f8b34122
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 Int64ops
21 open Options
22 open Printf2
23 open Md4
24 open BasicSocket
26 open GuiTypes
28 open CommonInteractive
29 open CommonOptions
30 open CommonGlobals
31 open CommonComplexOptions
32 open CommonClient
33 open CommonFile
34 open CommonUser
35 open CommonNetwork
36 open CommonRoom
37 open CommonServer
38 open CommonResult
39 open CommonTypes
40 open CommonSearch
42 open DcTypes
43 open DcOptions
44 open DcGlobals
45 open DcShared
46 open DcProtocol
47 open DcServers
48 open Xml
51 let log_prefix = "[dcInt]"
53 let lprintf_nl fmt =
54 lprintf_nl2 log_prefix fmt
56 (* Start new dowload from result *)
57 let start_new_download u tth fdir fname fsize =
58 try
59 ignore (Hashtbl.find dc_shared_files_by_hash tth);
60 if !verbose_download then lprintf_nl "Shared file with same hash exists (%s) (%s)" fname tth;
61 None
62 with _ ->
63 let f = new_file tth fdir fname fsize in (* ...create new file *)
64 match (file_state f) with
65 | FileDownloaded | FileShared -> if !verbose_download then lprintf_nl "File already downloaded"; None
66 | FileDownloading -> if !verbose_download then lprintf_nl "File being downloaded"; None
67 | FilePaused -> if !verbose_download then lprintf_nl "File paused"; None
68 | FileAborted _ | FileCancelled | FileQueued ->
69 if !verbose_download then lprintf_nl "File state invalid"; None
70 | FileNew ->
71 file_add f.file_file FileDownloading;
72 match u with
73 | None -> Some f
74 | Some user ->
75 let c = new_client_to_user_with_file user f in
76 c.client_state <- DcDownloadWaiting f;
77 if (can_user_start_downloading user) then begin
78 user.user_state <- TryingToSendFirstContact;
79 c.client_state <- DcDownloadConnecting (f,current_time ());
80 ignore (DcClients.try_connect_client c)
81 end;
82 Some f
84 (* Start downloading of a file by user selection from resultlist *)
85 let start_result_download r =
86 let filename = List.hd r.result_names in
87 let rinfo = Hashtbl.find dc_result_info r.result_num in
88 let newfile = start_new_download (Some rinfo.user) rinfo.tth rinfo.directory filename r.result_size in
89 (match newfile with
90 | Some f -> as_file f.file_file (* return CommonFile.file *)
91 | _ -> raise Not_found )
93 let exn_catch f x = try `Ok (f x) with exn -> `Exn exn
94 let opt_default default = function None -> default | Some v -> v
96 let parse_url url user group =
97 match exn_catch parse_magnet_url url with
98 | `Exn _ -> "Not a magnet url", false
99 | `Ok magnet ->
100 if !verbose then
101 lprintf_nl "Got magnet url %S" url;
102 (* TODO multiple TTHs, multiple xt, automatic merge of downloads from different networks (?!) *)
103 match List2.filter_map (function TigerTree tth -> Some tth | _ -> None) magnet#uids with
104 | [] -> "No TTH found in magnet url", false
105 | tth::_ ->
106 let _ = start_new_download None (TigerTree.to_string tth) "" magnet#name (opt_default 0L magnet#size) in
107 magnet#name, true
109 (* register DC commands *)
110 let register_commands list =
111 register_commands (List2.tail_map (fun (n,f,h) -> (n, "Direct Connect", f,h)) list)
113 let td_command text title ?(blink=false) ?(target=`Output) cmd =
114 Printf.sprintf
115 "\\<td class=\\\"srb\\\" %sonMouseOver=\\\"mOvr(this);\\\"
116 onMouseOut=\\\"mOut(this);\\\" title=\\\"%s\\\"
117 onClick=\\\"parent.%s.location.href='submit?q=%s'\\\"\\>%s\\</td\\>"
118 (if blink then "style=\\\"text-decoration:blink\\\" " else "")
119 title (match target with `Output -> "output" | `Status -> "fstatus")
120 (String.concat "+" cmd) (* Url.encode ? *)
121 text
123 (* Print DC hubs header *)
124 let dc_hublist_print_html_header buf ext =
125 html_mods_table_header buf "serversTable" (Printf.sprintf "servers%s" ext) [
126 ( "1", "srh", "Hub number", "#" ) ;
127 ( "0", "srh", "Add hub to servers", "Add" ) ;
128 ( "0", "srh", "Hub name", "Hub name" ) ;
129 ( "0", "srh", "IP address", "IP address" ) ;
130 ( "1", "srh", "Users in hub", "Users" ) ;
131 ( "0", "srh", "Hub info", "Info" ) ]
133 (* print in html or txt list of hubs *)
134 let hublist_print h hnum o =
135 let buf = o.conn_buf in
136 let hname = shorten_string h.dc_name 50 in
137 let hinfo = shorten_string h.dc_info 50 in
138 if use_html_mods o then begin
139 Printf.bprintf buf "
140 \\<tr class=\\\"dl-%d\\\"\\>
141 \\<td class=\\\"srb\\\" \\>%d\\</td\\>
143 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
144 \\<td class=\\\"sr\\\"\\>%s:%d\\</td\\>
145 \\<td class=\\\"sr\\\"\\>%d\\</td\\>
146 \\<td width=\\\"100%%\\\" class=\\\"sr\\\"\\>%s\\</td\\>\\</tr\\>\n"
147 (html_mods_cntr ())
148 hnum
149 (td_command "Add" "Add" ~target:`Status
150 ["dcn"; Ip.string_of_addr h.dc_ip; string_of_int h.dc_port])
151 hname
152 (Ip.string_of_addr h.dc_ip) h.dc_port
153 h.dc_nusers hinfo
154 end else begin
155 Printf.bprintf buf "[%5d] %20s %25s:%-10d Users:%-8d %20s\n"
156 hnum
157 hname
158 (Ip.string_of_addr h.dc_ip) h.dc_port
159 h.dc_nusers
160 hinfo
163 (* Print DC users header *)
164 let dc_user_print_html_header buf =
165 html_mods_table_header buf "serversTable" "servers" [
166 ( "1", "srh", "User number", "#" );
167 ( "0", "srh", "User name", "Name" );
168 ( "0", "srh", "User type", "Type" );
169 ( "1", "srh", "Users slots (all/free)", "Slots" );
170 ( "1", "srh", "Users connected hubs (Normal/Vipped/Opped)", "Hubs" );
171 ( "0", "srh", "Users mode", "Mode" );
172 ( "1", "srh", "Users shared size", "Shared" );
173 ( "0", "srh", "User state", "State" );
174 ( "0", "srh", "User description field", "Description" );
175 ( "1", "srh", "User clients number", "Clients" );
176 ( "1", "srh", "Users servers number", "Servers" );
177 ( "0", "srh", "Download this clients filelist", "Filelist" );
178 ( "0", "srh", "Open chat window with this user. Blinking tells there are new unread messages", "Chat");
179 ( "1", "srh", "User total uploaded bytes", "Up" );
180 ( "1", "srh", "User total downloaded bytes", "Down" );
181 ( "0", "srh", "User client supports", "Supports" ); ];
184 (* print in html or txt list of users *)
185 let user_print user num o =
186 let buf = o.conn_buf in
187 let utype =
188 (match user.user_type with
189 | Normal -> "Normal"
190 | Vip -> "Vip"
191 | Op -> "Op"
192 (*| Bot -> "Bot"*) )
194 let state =
195 (match user.user_state with
196 | UserIdle -> "NotDefined"
197 | TryingToSendFirstContact -> "TryingToSendFirstContact"
198 | UserActiveMeInitiating -> "UserActiveMeInitiating"
199 | UserActiveUserInitiating -> "UserActiveUserInitiating"
200 | UserPassiveUserInitiating _ -> "UserPassiveUserInitiating" )
202 let clients = List.length user.user_clients in
203 let servers = List.length user.user_servers in
204 let messages = user_has_new_messages user in
205 let hasmynick = has_my_nick user in
206 let hubs =
207 let a,b,c = user.user_myinfo.hubs in
208 Printf.sprintf "(%d/%d/%d)" a b c
210 let supports =
211 if (List.length user.user_clients > 0) then begin
212 let c = List.hd user.user_clients in
213 (match c.client_supports with
214 | Some supports ->
215 DcProtocol.Supports.create_supports_string (ClientSupports supports)
216 | _ -> empty_string )
217 end else empty_string
219 if use_html_mods o then begin
220 Printf.bprintf buf "
221 \\<tr class=\\\"dl-%d\\\"\\>
222 \\<td class=\\\"srb\\\" \\>%d\\</td\\>
223 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
224 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
225 \\<td class=\\\"sr\\\"\\>%d\\</td\\>
226 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
227 \\<td class=\\\"sr\\\"\\>%c\\</td\\>
228 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
229 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
230 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
231 \\<td class=\\\"sr\\\"\\>%d\\</td\\>
232 \\<td class=\\\"sr\\\"\\>%d\\</td\\>
233 %s%s
234 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
235 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
236 \\<td class=\\\"sr\\\"\\>%s\\</td\\>\\</tr\\>\n"
237 (html_mods_cntr ()) num user.user_nick utype user.user_myinfo.slots hubs user.user_myinfo.mode
238 (size_of_int64 user.user_myinfo.sharesize) state user.user_myinfo.description clients servers
239 (if not hasmynick && (servers > 0) then (* is connected to any servers with us *)
240 td_command "Get List" "Download user filelist" ~target:`Status ["dcloadfilelist"; user.user_nick]
241 else begin
242 let txt =
243 if hasmynick then "Me"
244 else "No connection"
246 Printf.sprintf "\\<td class=\\\"sr\\\"\\>%s\\</td\\>" txt
247 end )
248 (if not hasmynick then (* not me *)
249 td_command "Open chat" "Open message window to this user" ~blink:messages ["dcmessages"; user.user_nick]
250 else
251 "\\<td class=\\\"sr\\\"\\>\\</td\\>" )
252 (size_of_int64 user.user_uploaded) (size_of_int64 user.user_downloaded) supports
253 end else
254 Printf.bprintf buf "[%5d] %-20s %8s %20s\n" num user.user_nick utype state
256 (* Print DC hubs header *)
257 let dc_hub_print_html_header buf =
258 html_mods_table_header buf "serversTable" "servers" [
259 ( "1", "srh", "Hub number", "#" ) ;
260 ( "0", "srh", "Set/UnSet server autoconnection state", "Auto" ) ;
261 ( "0", "srh", "Hub name", "Hub name" ) ;
262 ( "0", "srh", "IP address", "IP address" ) ;
263 ( "0", "srh", "My state in this hub", "State" ) ;
264 ( "1", "srh", "Users in hub", "Users" ) ;
265 ( "0", "srh", "Hub info", "Info" );
266 ( "0", "srh", "Open chat window with this hub. Blinking tells there are new unread message", "Chat" ) ]
268 (* Print list of connected hubs *)
269 let hub_print s num o =
270 let buf = o.conn_buf in
271 let sinfo = shorten_string s.server_info 50 in
272 let sname = shorten_string s.server_name 50 in
273 let sip = Ip.to_string s.server_ip in
274 let sport = s.server_port in
275 let susers = List.length s.server_users in
276 let smessages = ((List.length s.server_messages) > s.server_read_messages) in
277 let sstate = dc_hubstate_to_text s in
278 if use_html_mods o then begin
279 Printf.bprintf buf "
280 \\<tr class=\\\"dl-%d\\\"\\>
281 \\<td class=\\\"srb\\\" \\>%d\\</td\\>
283 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
284 \\<td class=\\\"sr\\\"\\>%s:%d\\</td\\>
285 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
287 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
288 %s\\</tr\\>\n"
289 (html_mods_cntr ())
291 (td_command
292 (if s.server_autoconnect then "UnSet" else "Set")
293 "Set this hub autoconnection state"
294 ["dcautoconnect"; (if s.server_autoconnect then "false" else "true"); sip] )
295 sname sip sport sstate
296 (td_command (string_of_int susers) "Show users for this hub only" ["dcusers";sip] )
297 sinfo
298 (td_command "Open chat" "Open this hubs chat windows" ~blink:smessages ["dcmessages";sip;string_of_int sport])
299 end else begin
300 Printf.bprintf buf "[%5d] %20s %25s:%-10d Users:%-8d %20s\n"
302 sname
303 sip sport
304 susers
305 sinfo
308 (* Print DC clients header *)
309 let dc_client_print_html_header buf =
310 html_mods_table_header buf "serversTable" "servers" [
311 ( "1", "srh", "Client number", "#" );
312 ( "0", "srh", "Remove Client", "Rem" );
313 ( "0", "srh", "Client name", "Name" );
314 ( "0", "srh", "Client ip/port", "Ip:Port" );
315 ( "0", "srh", "Client state", "State" );
316 ( "0", "srh", "Client connection", "Conn" );
317 ( "0", "srh", "Client last error/count", "Error" );
318 ( "0", "srh", "Client file", "File" ); ];
321 (* print in html or txt list of clients *)
322 let client_print name client num o =
323 let buf = o.conn_buf in
324 let ip,port =
325 (match client.client_addr with
326 | Some (ip,port) -> Ip.to_string ip,port
327 | None -> "None",0 )
329 let conn =
330 (match client.client_sock with
331 | Connection _ -> "Connected"
332 | ConnectionWaiting _ -> "Connecting..."
333 | NoConnection -> "NoConnection" )
335 let state = client_state_to_string client in
336 let error =
337 (match client.client_error with
338 | NoError -> empty_string
339 | NoFreeSlots -> Printf.sprintf "NoFreeSlots %d" client.client_error_count
340 | FileNotAvailable -> Printf.sprintf "FileNotAvailable %d" client.client_error_count
341 | UserNotReachable -> Printf.sprintf "UserNotReachable %d" client.client_error_count
342 | ClosedOnInit -> Printf.sprintf "ClosedOnInit %d" client.client_error_count
343 | ConnectionResetByPeer -> Printf.sprintf "ConnectionResetByPeer %d" client.client_error_count
344 | UploadError -> Printf.sprintf "UploadError %d" client.client_error_count
345 | UserDontReplyOnTime -> Printf.sprintf "UserDontReplyOnTime %d" client.client_error_count )
347 let fil =
348 (match client.client_state with
349 | DcDownloadWaiting file
350 | DcDownloadConnecting (file,_)
351 | DcDownload file -> file.file_name
352 | DcUpload (_,fd_file,_,_)
353 | DcUploadList fd_file
354 | DcDownloadList fd_file -> Unix32.filename fd_file
355 | DcUploadListStarting filename -> filename
356 | DcUploadStarting (dcsh,_,_) -> dcsh.dc_shared_codedname
357 | _ -> "None" )
359 if use_html_mods o then begin
360 Printf.bprintf buf "
361 \\<tr class=\\\"dl-%d\\\"\\>
362 \\<td class=\\\"sr\\\" \\>%d\\</td\\>
364 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
365 \\<td class=\\\"sr\\\"\\>%s:%d\\</td\\>
366 \\<td class=\\\"sr\\\" \\>%s\\</td\\>
367 \\<td class=\\\"sr\\\" \\>%s\\</td\\>
368 \\<td class=\\\"sr\\\" \\>%s\\</td\\>
369 \\<td class=\\\"sr\\\" \\>%s\\</td\\>\\</tr\\>\n"
370 (html_mods_cntr ()) num
371 (td_command "Rem" "Remove client" ~target:`Status
372 ["dcremclient"; string_of_int (client_num (as_client client.client_client))] )
373 name ip port state conn error fil
374 end else
375 Printf.bprintf buf "[%5d] %25s %25s:%-10d S:%15s C:%15s F:%15s\n"
376 num name ip port state conn fil
378 (* Print DC files header *)
379 let dc_file_print_html_header buf =
380 html_mods_table_header buf "serversTable" "servers" [
381 ( "1", "srh", "File number", "#" );
382 ( "0", "srh", "File name/path", "File" );
383 ( "1", "srh", "File size", "Size" );
384 ( "0", "srh", "Tiger Tree Hash and magnet url", "TTH and magnet" );
385 ( "1", "srh", "Files clients number (sources)", "Clients" );
386 ( "1", "srh", "Autosearches done", "Searches" );
387 ( "0", "srh", "Find new source by tth", "Find TTH" );
388 ( "0", "srh", "Find new source by similar name context", "Find similar" ); ];
391 let html_show_tth file size tth =
392 begin match exn_catch TigerTree.of_string tth with
393 | `Exn _ -> ""
394 | `Ok hash ->
395 let magnet = object
396 method name = Filename.basename file
397 method size = match size with 0L -> None | _ -> Some size (* do not report size if not available *)
398 method uids = [TigerTree hash]
399 end in
400 Printf.sprintf "\\<a href=\\\"%s\\\"\\>%s\\</a\\>" (show_magnet_url magnet) tth
403 let html_show_shared dcsh =
404 html_show_tth dcsh.dc_shared_fullname dcsh.dc_shared_size dcsh.dc_shared_tiger_root
406 let html_show_file file =
407 html_show_tth file.file_name file.file_file.impl_file_size file.file_unchecked_tiger_root
409 (* print in html or txt list of files *)
410 let file_print file num o =
411 let buf = o.conn_buf in
412 let fname = ref (String.copy file.file_name) in
413 String2.replace_char !fname char32 char42; (* to * *)
414 String2.replace_char !fname char39 char58; (* ' to : *)
415 String2.replace_char !fname char60 char38; (* & to < *)
416 if use_html_mods o then begin
417 Printf.bprintf buf "
418 \\<tr class=\\\"dl-%d\\\"\\>
419 \\<td class=\\\"srb\\\" \\>%d\\</td\\>
420 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
421 \\<td class=\\\"srb\\\" \\>%Ld\\</td\\>
422 \\<td class=\\\"srb\\\" \\>%s\\</td\\>
423 \\<td class=\\\"srb\\\" \\>%d\\</td\\>
424 \\<td class=\\\"srb\\\" \\>%d\\</td\\>
426 %s\\</tr\\>\n"
427 (html_mods_cntr ()) num file.file_name file.file_file.impl_file_size
428 (html_show_file file) (List.length file.file_clients) file.file_autosearch_count
429 (td_command "Find TTH" "Find new client for this file by TTH" ["dcfindsource"; file.file_unchecked_tiger_root])
430 (td_command "Find similar" "Find new client for this file by similar name" ["dcfindsource"; !fname])
431 end else
432 Printf.bprintf buf "[%5d] %40s %-15Ld %5d\n"
433 num file.file_name file.file_file.impl_file_size (List.length file.file_clients)
435 (* Print DC shared files header *)
436 let dc_shared_print_html_header buf =
437 html_mods_table_header buf "serversTable" "servers" [
438 ( "1", "srh", "File number", "#" );
439 ( "0", "srh", "Shared file name", "Name" );
440 ( "1", "srh", "Shared file size", "Size" );
441 ( "0", "srh", "Tiger Tree Hash and magnet url", "TTH and magnet" );
442 (*( "1", "srh", "Shared files Tiger tree array length", "TTree #" );*) ];
446 (* print in html or txt list of shared files *)
447 let shared_print dcsh num o =
448 let buf = o.conn_buf in
449 if use_html_mods o then begin
450 Printf.bprintf buf "
451 \\<tr class=\\\"dl-%d\\\"\\>
452 \\<td class=\\\"srb\\\" \\>%d\\</td\\>
453 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
454 \\<td class=\\\"srb\\\" \\>%Ld\\</td\\>
455 \\<td class=\\\"srb\\\" \\>%s\\</td\\>\\</tr\\>\n"
456 (html_mods_cntr ()) num dcsh.dc_shared_codedname dcsh.dc_shared_size
457 (html_show_shared dcsh)
458 end else
459 Printf.bprintf buf "[%5d] %40s %-15Ld %24s\n"
460 num dcsh.dc_shared_codedname dcsh.dc_shared_size dcsh.dc_shared_tiger_root
462 (* Print DC filelist header *)
463 let dc_filelist_print_html_header buf =
464 html_mods_table_header buf "serversTable" "servers" [
465 ( "1", "srh", "Number", "#" ) ;
466 ( "0", "srh", "Filelist name", "Filelist" ) ]
468 (* Print one line from filelist *)
469 let filelist_print fname line o =
470 let buf = o.conn_buf in
471 if use_html_mods o then begin
472 Printf.bprintf buf "
473 \\<tr class=\\\"dl-%d\\\"\\>
474 \\<td class=\\\"srb\\\" \\>%d\\</td\\>
476 \\</tr\\>\n"
477 (html_mods_cntr ())
478 line
479 (td_command fname "Open filelist" ["dcshowfilelist"; fname])
480 end else begin
481 Printf.bprintf buf "[%5d] %s\n" line fname
484 type dc_int_groups = G_users|G_hubs|G_clients|G_files|G_shared|G_filelists
486 (* register users,clients,files *)
487 let dc_list o group_type group_name =
488 let buf = o.conn_buf in
489 let num = ref 1 in
490 html_mods_cntr_init ();
491 let html f = if use_html_mods o then f buf else () in
492 begin try
493 begin match group_type with
494 | G_users ->
495 let new_messages_list = ref [] in (* lets order users with unread messages to the top *)
496 let others_list = ref [] in
497 Hashtbl.iter (fun _ user ->
498 if user_has_new_messages user then new_messages_list := user :: !new_messages_list
499 else others_list := user :: !others_list
500 ) users_by_name;
501 html dc_user_print_html_header;
502 List.iter (fun user -> user_print user !num o; incr num) !new_messages_list;
503 List.iter (fun user -> user_print user !num o; incr num) !others_list;
504 | G_hubs ->
505 html dc_hub_print_html_header;
506 Hashtbl.iter (fun _ s -> hub_print s !num o; incr num) servers_by_ip
507 (*List.iter (fun s -> hub_print s !num o; incr num) !connected_servers*)
508 | G_clients ->
509 html dc_client_print_html_header;
510 List.iter (fun c ->
511 (match c.client_name with
512 | Some n -> client_print n c !num o; incr num
513 | None -> () )
514 ) !clients_list
515 | G_files ->
516 html dc_file_print_html_header;
517 List.iter (fun file -> file_print file !num o; incr num) !current_files;
518 | G_shared ->
519 html dc_shared_print_html_header;
520 Hashtbl.iter (fun _ dcsh -> shared_print dcsh !num o; incr num) dc_shared_files_by_codedname
521 | G_filelists ->
522 html dc_filelist_print_html_header;
523 let filelist = Unix2.list_directory filelist_directory in
524 List.iter (fun fname -> filelist_print fname !num o; incr num) filelist;
525 end;
526 if use_html_mods o then
527 Printf.bprintf buf "\\</table\\>\\</div\\>";
528 with e ->
529 lprintf_nl "Exception %s in printing %s" (Printexc2.to_string e) group_name
530 end;
531 empty_string
533 (* Print DC filelist files header *)
534 let dc_filelist_files_print_html_header buf =
535 html_mods_table_header buf "serversTable" (Printf.sprintf "servers") [
536 ( "1", "srh", "Number", "#" );
537 ( "0", "srh", "File/Directory name", "File/Directory name" );
538 ( "1", "srh", "File Size", "Size" );
539 ( "0", "srh", "Files TTH", "TTH" ) ]
541 (* Print one line from filelist file *)
542 let filelist_file_print is_file spaces username dir fname fsize ftth line o =
543 (* is_file = if true, make the whole filename a link with submit command to load a file
544 spaces
545 username = username to submit in command
546 dir = current directory path to submit in command
547 fname = filename from mylist
548 fsize = filesize from mylist
549 ftth = tth from mylist *)
550 let buf = o.conn_buf in
551 let sdir = ref (String.copy dir) in
552 let sname = ref (String.copy fname) in
553 String2.replace_char !sdir char32 char42; (* to * *)
554 String2.replace_char !sdir char39 char58; (* ' to : *)
555 String2.replace_char !sdir char38 char60; (* & to < *)
556 String2.replace_char !sdir char43 char62; (* + to > *)
557 String2.replace_char !sname char32 char42;
558 String2.replace_char !sname char39 char58;
559 String2.replace_char !sname char38 char60;
560 String2.replace_char !sname char43 char62;
561 if use_html_mods o then begin
562 Printf.bprintf buf "
563 \\<tr class=\\\"dl-%d\\\"\\>
564 \\<td class=\\\"srb\\\" \\>%d\\</td\\>
566 \\<td class=\\\"srb\\\" \\>%s\\</td\\>
567 \\<td class=\\\"srb\\\" \\>%s\\</td\\>\\</tr\\>\n"
568 (html_mods_cntr ())
569 line
570 (if is_file then
571 td_command (spaces^fname) "Start downloading" ~target:`Status
572 ["dcloadfile"; username; ftth; !sdir; !sname; fsize]
573 else
574 Printf.sprintf "\\<td class=\\\"srb\\\" \\>\\<b\\>%s%s\\</b\\>\\</td\\>" spaces fname
576 fsize
577 ftth
578 end else begin
579 Printf.bprintf buf "%30s %10s %30s\n" fname fsize ftth
582 (* Print DC info header *)
583 let dc_info_html_header buf =
584 html_mods_table_header buf "sharesTable" "shares" [
585 ( "0", "srh", "Direct Connect information", "DC Info" ) ;
586 ( "0", "srh", empty_string, empty_string ) ]
588 (* Print DC info *)
589 let dc_info_print info data line o =
590 let buf = o.conn_buf in
591 if use_html_mods o then begin
592 Printf.bprintf buf "
593 \\<tr class=\\\"dl-%d\\\"\\>
594 \\<td class=\\\"sr\\\"\\>%s\\</td\\>
595 \\<td class=\\\"sr\\\"\\>%s\\</td\\>\\</tr\\>"
596 (html_mods_cntr ()) info data
597 end else begin
598 Printf.bprintf buf "%s: %s\n" info data
601 let show_dc_buttons o =
602 let buf = o.conn_buf in
603 let button id ?(cmd="dc"^id) ?(txt=String.capitalize id) () =
604 Printf.bprintf buf "\\<form style=\\\"margin: 0px;\\\" id=\\\"%s\\\" name=\\\"%s\\\"
605 action=\\\"javascript:parent.output.location.href='submit?q=%s'\\\"\\>
606 \\<td\\>\\<input style=\\\"font-family: verdana; font-size: 12px;\\\" type=submit
607 Value=\\\"%s\\\"\\>\\</td\\>\\</form\\>" id id cmd txt
609 if use_html_mods o then
610 begin
611 Printf.bprintf buf "\\<table\\>\\<tr\\>";
612 button "users" ~cmd:"dcusers+all" ();
613 button "clients" ~cmd:"dcclients" ();
614 button "hubs" ();
615 button "shared" ();
616 button "files" ();
617 button "info" ~txt:"DC Info" ();
618 button "hublistshow" ~cmd:"dchublist" ~txt:"Show hublist" ();
619 button "filelists" ();
620 Printf.bprintf buf "\\</tr\\>\\</table\\>";
623 (* List of commands to register *)
624 let commands = [
626 "dc", Arg_none (fun o ->
627 if use_html_mods o then
628 show_dc_buttons o
629 else
630 Printf.bprintf buf "Try `?? dc` for more commands\n";
631 dc_list o G_hubs "hubs"
632 ), ": Show Direct Connect buttons";
634 (* 'dcn address [port]' Add a new DC server with optional port (default 411) *)
635 "dcn", Arg_multiple (fun args o ->
636 let ip, port =
637 (match args with
638 | [ip ; port] -> ip, port
639 | [ip] -> ip, "411"
640 | _ -> failwith "dcn <ip> [<port>]: bad argument number" )
642 let ip_addr = Ip.addr_of_string ip in
643 Ip.async_ip_of_addr ip_addr (fun t -> (* do DNS check here *)
644 let port = int_of_string port in
645 if !verbose_msg_servers then lprintf_nl "New server being added: (%s) (%s) (%d)" ip (Ip.to_string t) port;
646 if (Ip.valid t) && (port>0) && (port<65536) then
647 ignore (new_server ip_addr t port) ) (fun _ -> ());
648 empty_string
649 ), "<ip> [<port>] : Add a server. Default port number is 411";
651 (* List connected hubs for chatting *)
652 "dchubs", Arg_none (fun o -> show_dc_buttons o; dc_list o G_hubs "hubs"
653 ), ": Show connected DC hubs";
655 (* List all DC users *)
656 "dcusers", Arg_one (fun args o ->
657 show_dc_buttons o;
658 let buf = o.conn_buf in
659 (match args with
660 | "all" -> dc_list o G_users "users"
661 | ip ->
662 (try
663 let s = Hashtbl.find servers_by_ip ip in
664 let num = ref 1 in
665 let new_messages_list = ref [] in (* lets order users with unread messages to the top *)
666 let others_list = ref [] in
667 List.iter (fun user ->
668 if user_has_new_messages user then new_messages_list := user :: !new_messages_list
669 else others_list := user :: !others_list
670 ) s.server_users;
671 dc_user_print_html_header buf;
672 List.iter (fun user -> user_print user !num o; incr num) !new_messages_list;
673 List.iter (fun user -> user_print user !num o; incr num) !others_list;
674 empty_string
675 with _ -> "dcusers <ip> : ip not valid" ) );
676 ), "<all>|<ip> :Show DC users";
678 (* List all DC clients *)
679 "dcclients", Arg_none (fun o -> show_dc_buttons o; dc_list o G_clients "clients"
680 ), ": Show all DC clients";
682 (* List all DC files *)
683 "dcfiles", Arg_none (fun o -> show_dc_buttons o; dc_list o G_files "files"
684 ), ": Show all DC files";
686 (* List all DC shared files *)
687 "dcshared", Arg_none (fun o -> show_dc_buttons o; dc_list o G_shared "shared"
688 ), ": Show all DC shared files. All/Hashed ";
690 (* 'dchublist [args]' - Show dchub list with optional filters args (max 5) *)
691 "dchublist", Arg_multiple (fun args o ->
692 show_dc_buttons o;
693 let buf = o.conn_buf in
694 let filter = ref [] in
695 let print_hublist () =
696 if use_html_mods o then
697 begin
698 html_mods_table_one_row buf "serversTable" "servers" [
699 (empty_string, "srh", Printf.sprintf "Showing hublist"); ];
700 Printf.bprintf buf "\\</table\\>\\</div\\>"
702 else
703 Printf.bprintf buf "Showing hublist";
704 html_mods_cntr_init ();
705 let nb_hubs = ref 0 in
706 if use_html_mods o then dc_hublist_print_html_header buf empty_string;
707 let show_all = if (!filter = []) then true else false in
708 List.iter (fun h ->
709 let hub_has_string searched =
710 if String2.contains (Ip.string_of_addr h.dc_ip) searched ||
711 String2.contains (string_of_int h.dc_port) searched ||
712 String2.contains h.dc_info searched ||
713 String2.contains h.dc_name searched then true
714 else false in
715 let print_hub () =
716 (try
717 hublist_print h !nb_hubs o;
718 incr nb_hubs;
719 with e ->
720 if !verbose_msg_servers then
721 lprintf_nl "Exception %s in hub_print\n" (Printexc2.to_string e))
723 if show_all then
724 print_hub ()
725 else
726 begin
727 let print = ref false in
728 let finished = ref false in
729 let counter = ref 0 in
730 let filters_length = List.length !filter in
731 while (!print = false) && (!finished = false) do
732 if (!counter = filters_length) || (!counter > 5) then
733 finished := true
734 else
735 if (hub_has_string (List.nth !filter !counter)) then print := true;
736 incr counter
737 done;
738 if (!print = true) then print_hub ()
740 ) !dc_hublist;
741 let txt = if show_all then "(showing all hubs from hublist)" else "(filtered)" in
742 if use_html_mods o then
743 begin
744 Printf.bprintf buf "\\</table\\>\\</div\\>";
745 html_mods_table_one_row buf "serversTable" "servers" [
746 (empty_string, "srh", Printf.sprintf "Hubs: %d known %s" !nb_hubs txt); ]
748 else
749 Printf.bprintf buf "Hubs: %d known %s" !nb_hubs txt
751 (match args with
752 | [] -> ()
753 | rest_args -> filter := rest_args
755 print_hublist ();
756 empty_string
757 ), "[filtertext]: dchublist fin - filters hubs with text fin";
759 (* 'dcuserip name' query user-ip from hub *)
760 "dcuserip", Arg_multiple (fun args o ->
761 let buf = o.conn_buf in
762 (match args with
763 | [hub ; port ; name] ->
764 (try
765 let s = Hashtbl.find servers_by_ip hub in
766 (match s.server_sock with
767 | Connection sock -> dc_send_msg sock ( UserIPReq ( [name] ))
768 | _ -> () )
769 with _ ->
770 if !verbose_unexpected_messages then
771 lprintf_nl "dcuserip: No server found by ip (%s) (%s) (%s)" hub port name )
772 | _ -> if !verbose_unexpected_messages then lprintf_nl "dcuserip: Invalid args count (%d)" (List.length args) );
773 Printf.bprintf buf "User query sent to hubs\n";
774 empty_string
775 ), "<user> : Query users ip from hub";
777 "dcmsglog", Arg_multiple (fun args o ->
778 let buf = o.conn_buf in
779 let counter = ref 0 in
780 let messages,name,topic =
781 (match args with
782 | delay :: ip :: port :: _ ->
783 (try
784 let s = Hashtbl.find servers_by_ip ip in
785 let topic =
786 (match s.server_sock with
787 | Connection _ -> s.server_topic
788 | _ -> "NOT CONNECTED TO SERVER" )
790 s.server_read_messages <- List.length s.server_messages; (* messages are set as read before *)
791 s.server_messages, (* they are actually printed to user *)
792 (shorten_string s.server_name 50),
793 topic
794 with _ ->
795 if !verbose_unexpected_messages then lprintf_nl "dcmsglog: No server with address found";
796 raise Not_found )
797 | delay :: n :: _ ->
798 (try
799 let u = search_user_by_name n in
800 let connected = ((List.length u.user_servers) > 0) in
801 u.user_read_messages <- List.length u.user_messages; (* messages are set as read before *)
802 u.user_messages, u.user_nick, (* they are actually printed to user *)
803 (if connected then empty_string
804 else "User not connected to any servers at the moment...")
805 with _ ->
806 if !verbose_unexpected_messages then lprintf_nl "dcmsglog: No user found";
807 raise Not_found )
808 | _ ->
809 if !verbose_unexpected_messages then lprintf_nl "dcmsglog: Invalid args count (%d)" (List.length args);
810 raise Not_found )
812 if use_html_mods o then begin
813 Printf.bprintf buf "\\<div class=\\\"messages\\\"\\>";
814 Printf.bprintf buf "\\<div\\>Chatting with \\<b\\>%s\\</b\\> - %d logged messages\\</div\\>"
815 name (List.length messages);
816 Printf.bprintf buf "\\<div\\>\\<i\\>%s\\</i\\>\\</div\\>" topic
817 end else
818 Printf.bprintf buf "%d logged messages\n" (List.length messages);
819 if use_html_mods o then html_mods_table_header buf "serversTable" "servers" [
820 ( "0", "srh", "Timestamp", "Time" );
821 ( "0", "srh", "Who message is from", "From" );
822 ( "0", "srh", "Message text", "Message" ) ];
823 List.iter (fun (t,f,m) ->
824 let msg =
825 (match m with
826 | PrivateMessage (_, msg) -> msg
827 | PublicMessage (_, msg) -> msg
828 | ServerMessage msg -> msg )
830 if use_html_mods o then begin
831 Printf.bprintf buf "\\<tr class=\\\"%s\\\"\\>"
832 (if (!counter mod 2 == 0) then "dl-1" else "dl-2");
833 html_mods_td buf [
834 (empty_string, "sr", Date.simple (BasicSocket.date_of_int t));
835 (empty_string, "sr", f);
836 (empty_string, "srw", String2.replace msg '\r' "\\<br/\\>") ];
837 Printf.bprintf buf "\\</tr\\>"
838 end else begin
839 Printf.bprintf buf "\n%s [%s] : %s\n" (Date.simple (BasicSocket.date_of_int t)) f msg
840 end;
841 incr counter
842 ) messages;
843 if use_html_mods o then Printf.bprintf buf "\\</table\\>\\</div\\>\\</div\\>";
844 empty_string
845 ), "<refresh> <user> | <refresh> <serverip> <serverport>";
847 "dcmessages", Arg_multiple (fun args o ->
848 show_dc_buttons o;
849 let buf = o.conn_buf in
850 let s,u =
851 (match args with
852 | ip :: port :: _ ->
853 (try
854 let s = Hashtbl.find servers_by_ip ip in
855 Some s, None
856 with _ ->
857 if !verbose_unexpected_messages then lprintf_nl "dcmessages: No server with address found";
858 raise Not_found )
859 | n :: _ ->
860 (try
861 let u = search_user_by_name n in
862 None, Some u
863 with _ ->
864 if !verbose_unexpected_messages then lprintf_nl "dcmessages: No user found";
865 raise Not_found )
866 | _ ->
867 if !verbose_unexpected_messages then lprintf_nl "dcmessages: No user or server";
868 raise Not_found )
870 if use_html_mods o then begin
871 Printf.bprintf buf "\\<script type=\\\"text/javascript\\\"\\>
872 \\<!--
873 function submitCmd() {
874 var formID = document.getElementById(\\\"msgForm\\\")
875 parent.output.location.href='submit?q=dcmessages+'+formID.sendCmd.value
877 function submitMessageForm() {
878 var formID = document.getElementById(\\\"msgForm\\\")
879 var regExp = new RegExp (' ', 'gi')
880 var msgTextOut = formID.msgText.value.replace(regExp, '+')
881 parent.fstatus.location.href='submit?q=dcsendmsg+'+formID.sendCmd.value+\\\"+\\\"+msgTextOut
882 formID.msgText.value=\\\"\\\"
883 msgWindow.location.reload();
885 //--\\>
886 \\</script\\>";
887 let sendmsg,namelist,first =
888 (match s with
889 | Some s ->
890 let ip,port = (Ip.to_string s.server_ip),(string_of_int s.server_port) in
891 Printf.sprintf "dcmsglog+20+%s+%s" ip port, s.server_users, Printf.sprintf "%s %s" ip port
892 | None ->
893 (match u with
894 | Some u ->
895 Printf.sprintf "dcmsglog+20+%s" u.user_nick, [], u.user_nick
896 | None ->
897 if !verbose_unexpected_messages then lprintf_nl "dcmessages: No user or server";
898 raise Not_found )
901 Printf.bprintf buf "\\<iframe id=\\\"msgWindow\\\" name=\\\"msgWindow\\\" height=\\\"80%%\\\"
902 width=\\\"100%%\\\" scrolling=yes src=\\\"submit?q=%s\\\"\\>\\</iframe\\>" sendmsg;
903 Printf.bprintf buf "\\<form style=\\\"margin: 0px;\\\" name=\\\"msgForm\\\" id=\\\"msgForm\\\" action=\\\"javascript:submitMessageForm()\\\"\\>";
904 Printf.bprintf buf "\\<table width=100%% cellspacing=0 cellpadding=0 border=0\\>\\<tr\\>\\<td\\>";
905 Printf.bprintf buf "\\<select style=\\\"font-family: verdana; font-size: 12px; width: 150px;\\\" id=\\\"sendCmd\\\" name=\\\"sendCmd\\\" \\>";
906 Printf.bprintf buf "\\<option value=\\\"%s\\\"\\>%s" first first;
907 List.iter (fun u ->
908 if not (has_my_nick u) then
909 Printf.bprintf buf "\\<option value=\\\"%s\\\"\\>%s" u.user_nick u.user_nick
910 ) namelist;
911 Printf.bprintf buf "\\</select\\>\\</td\\>";
912 Printf.bprintf buf "\\<td width=100%%\\>\\<input style=\\\"width: 99%%; font-family: verdana; font-size: 12px;\\\"
913 type=text id=\\\"msgText\\\" name=\\\"msgText\\\" size=50 \\>\\</td\\>";
914 Printf.bprintf buf "\\<td\\>\\<input style=\\\"font-family: verdana;
915 font-size: 12px;\\\" type=submit value=\\\"Send\\\"\\>\\</td\\>\\</form\\>";
916 Printf.bprintf buf "\\<form style=\\\"margin: 0px;\\\" id=\\\"refresh\\\" name=\\\"refresh\\\"
917 action=\\\"javascript:msgWindow.location.reload();\\\"\\>
918 \\<td\\>\\<input style=\\\"font-family: verdana; font-size: 12px;\\\" type=submit
919 Value=\\\"Refresh\\\"\\>\\</td\\>\\</form\\>\\</tr\\>\\</table\\>";
920 Printf.bprintf buf "\\<table\\>\\<tr\\>\\<form style=\\\"margin: 0px;\\\" id=\\\"users\\\" name=\\\"users\\\"
921 action=\\\"javascript:parent.output.location.href='submit?q=dcusers'\\\"\\>
922 \\<td\\>\\<input style=\\\"font-family: verdana; font-size: 12px;\\\" type=submit
923 Value=\\\"Users\\\"\\>\\</td\\>\\</form\\>";
924 Printf.bprintf buf "\\<form style=\\\"margin: 0px;\\\" id=\\\"hubs\\\" name=\\\"hubs\\\"
925 action=\\\"javascript:parent.output.location.href='submit?q=dchubs'\\\"\\>
926 \\<td\\>\\<input style=\\\"font-family: verdana; font-size: 12px;\\\" type=submit
927 Value=\\\"Hubs\\\"\\>\\</td\\>\\</form\\>";
928 Printf.bprintf buf "\\<form style=\\\"margin: 0px;\\\" id=\\\"hubs\\\" name=\\\"hubs\\\"
929 action=\\\"javascript:submitCmd()\\\"\\>
930 \\<td\\>\\<input style=\\\"font-family: verdana; font-size: 12px;\\\" type=submit
931 Value=\\\"Open chat\\\"\\>\\</td\\>\\</form\\>";
932 Printf.bprintf buf "\\</tr\\>\\</table\\>";
933 empty_string
934 end else
935 _s "Usage: dcmessages <username> | <serverip> <serverport>\n"
936 ), "<username> | <serverip> <serverport> : Show user or server messages ";
939 (* message type = (int * room_message) list
940 room_message type = | ServerMessage of string
941 | PublicMessage of int * string
942 | PrivateMessage of int * string *)
943 (* 'dcsendmsg hub port user message' - send message to specific user *)
944 "dcsendmsg", Arg_multiple (fun args o ->
945 let buf = o.conn_buf in
946 (*let failtxt = "dcsendmsg <user> <message> | <serverip> <serverport> <message> : bad arguments" in*)
947 let u = (* check if first argument is valid user *)
948 (try
949 let u = search_user_by_name (List.hd args) in
950 Some u
951 with _ -> None )
953 let s =
954 (try
955 let s = Hashtbl.find servers_by_ip (List.hd args) in
956 Some s
957 with _ -> None )
959 (match u with
960 | Some u -> (* message is private usermessage *)
961 (match args with
962 | _ :: messages ->
963 if not (has_my_nick u) && ((List.length u.user_servers) > 0) then begin
964 let sent = ref false in
965 List.iter (fun s -> (* find a server we are connected to with this user *)
966 (match s.server_sock with
967 | Connection sock ->
968 if not !sent then begin
969 let msg = String2.unsplit messages ' 'in
970 dc_send_msg sock (
971 ToReq { To.dest = u.user_nick;
972 To.from = s.server_last_nick;
973 To.message = msg } );
974 sent := true;
975 u.user_messages <- u.user_messages @ [
976 (int_of_float (current_time ()), s.server_last_nick, PrivateMessage (0, msg))];
978 | _ -> () )
979 ) u.user_servers
981 | _ -> () )
982 | None ->
983 (match s with (* message is probably hub chatmessage but check ip *)
984 | None -> if !verbose_unexpected_messages then lprintf_nl "dcsendmsg: No User or Server found"
985 | Some s ->
986 (match args with
987 | _ :: _ :: messages ->
988 (match s.server_sock with
989 | Connection sock ->
990 let msg = String2.unsplit messages ' 'in
991 dc_send_msg sock ( MessageReq { Message.from = s.server_last_nick; Message.message = msg } );
992 (* don't save this message, it is echoed from hub back to us and saved then *)
993 (* message window is refreshed too quickly to show this *)
994 | _ -> () )
995 | _ -> () )
998 Printf.bprintf buf "User query sent to hubs\n";
999 empty_string
1000 ), "<user> <message> : Send message to user";
1002 (* Try to load file from filelist *)
1003 "dcloadfile", Arg_multiple (fun args o ->
1004 let buf = o.conn_buf in
1005 (match args with
1006 | [uname ; tth ; dir ; fname ; fsize] -> (* convert filenames back to normal *)
1007 if !verbose_download then lprintf_nl "dcloadfile: (%s) (%s) (%s)" dir fname tth;
1008 let sdir = ref (String.copy dir) in
1009 let sname = ref (String.copy fname) in
1010 String2.replace_char !sdir char42 char32; (* * to *)
1011 String2.replace_char !sdir char58 char39; (* : to ' *)
1012 String2.replace_char !sdir char60 char38; (* < to & *)
1013 String2.replace_char !sdir char62 char43; (* > to + *)
1014 String2.replace_char !sname char42 char32;
1015 String2.replace_char !sname char58 char39;
1016 String2.replace_char !sname char60 char38;
1017 String2.replace_char !sname char62 char43;
1018 Printf.bprintf buf "Trying to download file: %s from user: %s\n" !sname uname;
1019 (try
1020 let u = search_user_by_name uname in
1021 ignore (start_new_download (Some u) tth !sdir !sname (Int64.of_string fsize))
1022 with _ -> if !verbose_download then lprintf_nl "dcloadfile: No user found" )
1023 | _ ->
1024 if !verbose_unexpected_messages then
1025 lprintf_nl "dcloadfile: bad arguments count (%d)" (List.length args) );
1026 empty_string
1027 ), "<username> <tth> <directory> <filename> : Load a file";
1029 (* load filelist from user *)
1030 "dcloadfilelist", Arg_one (fun args o ->
1031 let buf = o.conn_buf in
1032 (match args with
1033 | name ->
1034 (try
1035 let u = search_user_by_name name in
1036 if ((List.length u.user_servers) > 0) then begin
1037 if not (filelist_already_downloading u) then begin (* and is connected to server with us *)
1038 if !verbose_msg_clients || !verbose_download then
1039 lprintf_nl "Loading filelist from user %s" name; (* not already loading filelist *)
1040 let c = new_client () in
1041 c.client_name <- Some u.user_nick;
1042 add_client_to_user c u;
1043 c.client_state <- DcDownloadListWaiting;
1044 if (can_user_start_downloading u) then begin
1045 u.user_state <- TryingToSendFirstContact;
1046 c.client_state <- DcDownloadListConnecting (0,!!firewalled,current_time ()); (* level is set later *)
1047 ignore (DcClients.try_connect_client c);
1050 end
1051 with _ ->
1052 if !verbose_unexpected_messages then lprintf_nl "dcloadfilelist: No user (%s) found" name) );
1053 Printf.bprintf buf "Trying to download filelist\n";
1054 empty_string
1055 ), "<name> : Download filelist from user";
1057 "dcfilelists", Arg_none (fun o -> show_dc_buttons o; dc_list o G_filelists "filelists"
1058 ), ": List all filelists on disk";
1060 "dcremclient", Arg_one (fun args o ->
1061 let buf = o.conn_buf in
1062 (match args with
1063 | num ->
1064 (try
1065 let cc = CommonClient.client_find (int_of_string num) in
1066 let impl = as_client_impl cc in
1067 let c = impl.impl_client_val in
1068 (match c.client_state with
1069 | DcDownloadWaiting _ | DcDownloadListWaiting ->
1070 Printf.bprintf buf "Removing one client by name %s" (clients_username c);
1071 remove_client c
1072 | _ -> () )
1073 with _ ->
1074 if !verbose_msg_clients then lprintf_nl "dcremclient: bad arguments (internal command)" ) );
1075 empty_string
1076 ), "<num> : Remove client by num";
1078 "dcfindsource", Arg_one (fun args o ->
1079 (match args with
1080 | tth_or_filename ->
1081 (*lprintf_nl "Got dcfindsource command: (%s)" tth_or_filename;*)
1082 let tth_or_filename = ref (String.copy tth_or_filename) in
1083 String2.replace_char !tth_or_filename char42 char32;
1084 String2.replace_char !tth_or_filename char58 char39;
1085 String2.replace_char !tth_or_filename char38 char60;
1086 if (is_valid_tiger_hash !tth_or_filename) then begin
1087 let query = QAnd (QHasField (Field_Type , "TTH") , (QHasWord !tth_or_filename)) in
1088 let search = CommonSearch.new_search o.conn_user
1089 (let module G = GuiTypes in
1090 { G.search_num = 0;
1091 G.search_query = query;
1092 G.search_max_hits = 10000;
1093 G.search_type = RemoteSearch;
1094 G.search_network = network.network_num;
1095 } )
1097 dc_with_connected_servers (fun s -> DcClients.server_send_search s search 9 !tth_or_filename);
1098 dc_last_manual_search := current_time ();
1099 end else begin
1100 let fname = Filename.basename !tth_or_filename in
1101 let words = clean_string fname in
1102 let words_list = String2.split_simplify words ' ' in
1103 let rec add_query list =
1104 (match list with
1105 | hd :: [] -> QHasWord hd
1106 | hd :: tail -> QAnd ((QHasWord hd) , (add_query tail))
1107 | [] -> failwith "No words to search")
1109 let query = add_query words_list in
1110 let search = CommonSearch.new_search o.conn_user
1111 (let module G = GuiTypes in
1112 { G.search_num = 0;
1113 G.search_query = query;
1114 G.search_max_hits = 10000;
1115 G.search_type = RemoteSearch;
1116 G.search_network = network.network_num;
1119 dc_with_connected_servers (fun s -> DcClients.server_send_search s search 1 !tth_or_filename);
1120 dc_last_manual_search := current_time ();
1121 end );
1122 empty_string
1123 ), ": Find new source for a file";
1125 "dcinfo", Arg_none (fun o ->
1126 show_dc_buttons o;
1127 let buf = o.conn_buf in
1128 let server_list =
1129 let lst = ref [] in
1130 List.iter (fun s ->
1131 let data =
1132 shorten_string s.server_name 20 ^ " (nick = " ^ s.server_last_nick ^ ") (uptime = " ^
1133 (Date.time_to_string (int_of_float (current_time ()) -
1134 int_of_float (s.server_connection_time)) "verbose") ^
1135 (string_of_int (List.length s.server_users)) ^ ")"
1137 lst := !lst @ [ (empty_string, data) ]
1138 ) !connected_servers;
1139 !lst
1141 let norm_hubs,reg_hubs,opped_hubs = get_myhubs_info () in
1142 html_mods_cntr_init ();
1143 if use_html_mods o then dc_info_html_header buf;
1144 let nservers = List.length !connected_servers in
1145 let list = [
1146 ("Hub supports", (DcProtocol.Supports.create_supports_string (HubSupports mldonkey_dc_hub_supports)) );
1147 ("Client supports", (DcProtocol.Supports.create_supports_string (ClientSupports mldonkey_dc_client_supports)) );
1148 ("All/Open slots", Printf.sprintf "%d / %d" (open_slots ()) (current_slots ()) );
1149 ("Mode", (if !!firewalled then "Passive" else "Active") );
1150 ("Connected servers", (if nservers > 0 then string_of_int nservers else ""));
1151 (" Server list:", empty_string ); ]
1152 @ server_list @ [
1153 ("Hubs", (Printf.sprintf "Normal:%d Vipped:%d Opped:%d" norm_hubs reg_hubs opped_hubs) ); ]
1155 let counter = ref 0 in
1156 List.iter (fun (info,data) ->
1157 dc_info_print info data line o;
1158 incr counter
1159 ) list;
1160 if use_html_mods o then Printf.bprintf buf "\\</table\\>\\</div\\>";
1161 empty_string
1162 ), ": Direct Connect info";
1164 (* load filelist from user *)
1165 "dcshowfilelist", Arg_one (fun args o ->
1166 show_dc_buttons o;
1167 let buf = o.conn_buf in
1168 (match args with
1169 | filename ->
1170 let username, extension =
1171 (try
1172 if (Filename.check_suffix filename mylist_ext) then (* if extension is DcLst ... *)
1173 Filename.chop_suffix filename mylist_ext,mylist_ext (* return filename without it and ext *)
1174 else if (Filename.check_suffix filename bz2_ext) then begin (* else if extension is bz2 ... *)
1175 let filename = Filename.chop_suffix filename bz2_ext in (* chop it off *)
1176 if (Filename.check_suffix filename xml_ext) then (* check if there is extension xml ...*)
1177 Filename.chop_suffix filename xml_ext,mylistxmlbz2_ext (* return filename without it and ext *)
1178 else raise Not_found
1179 end else raise Not_found
1180 with _ -> filename, empty_string)
1182 let spaces num = (* add as many "...":s to string that counter num *)
1183 let s = ref "" in
1184 let rec iter num =
1185 if (num <> 0) then begin
1186 s := !s ^ "...";
1187 iter (num-1)
1189 in iter num;
1192 if (extension = mylist_ext) then begin (* parse MyList.DcLst *)
1193 (try
1194 let s = file_to_che3_to_string (Filename.concat filelist_directory filename) in
1195 if not (Charset.is_utf8 s) then lprintf_nl "not utf8 : %S" s;
1196 let s = Charset.Locale.to_utf8 s in (* really needed? *)
1197 (try
1198 String2.replace_char s char13 '\n';
1199 let lines = String2.split_simplify s '\n' in
1200 let mlist = ref ([] : dc_mylistnode list) in (* root node of the MyList *)
1201 let tablist = ref [(-1, mlist)] in (* list of previous open directory node for every tab *)
1202 (* [(0 , list ref); (1 , list ref) ... *)
1203 let rec count_tabs line pos = (* count current lines tabs *)
1204 if line.[pos] <> '\t' then pos else
1205 count_tabs line (pos+1)
1207 let add_dir name tabs list =
1208 let newlist = ref ([] : dc_mylistnode list) in
1209 list := !list @ [MylistDirectory (name, newlist)]; (* add this dir node to current list *)
1210 if (List.mem_assq tabs !tablist) then begin (* check if a directory exists already for this tab *)
1211 tablist := List.remove_assq tabs !tablist; (* remove existing previous tab *)
1212 end;
1213 tablist := !tablist @ [(tabs, newlist)]; (* add current list to this tab *)
1215 let add_file name size list =
1216 list := !list @ [MylistFile (name, size)]; (*add this file to current node *)
1218 let find_tab_dir tabs = (* find the node of last directory for this tab *)
1219 let nlist = List.assq tabs !tablist in
1220 nlist (* return list ref *)
1222 let rec parse lines ctabs clist =
1223 (match lines with
1224 | first :: tail ->
1225 let ltabs = count_tabs first 0 in (* count lines tabs *)
1226 (match (String2.split first '|') with
1227 | dir :: [] -> (* if line is directory *)
1228 let dir = String2.after dir ltabs in
1229 if ltabs = ctabs then begin
1230 let nlist = find_tab_dir (ltabs-1) in
1231 add_dir dir ltabs nlist;
1232 if (tail <> []) then begin
1233 let nlist = find_tab_dir ltabs in
1234 parse tail ltabs nlist
1236 end else if ltabs > ctabs then begin
1237 add_dir dir ltabs clist;
1238 let nlist = find_tab_dir ltabs in
1239 if (tail <> []) then parse tail ltabs nlist
1240 end else begin
1241 let nlist = find_tab_dir ltabs in
1242 parse lines ltabs nlist
1244 | name :: size :: [] -> (* if line is file *)
1245 let name = String2.after name ltabs in
1246 if ltabs > ctabs then begin
1247 add_file name size clist;
1248 if (tail <> []) then parse tail ctabs clist
1249 end else if ltabs <= ctabs then begin
1250 let nlist = find_tab_dir (ltabs-1) in
1251 add_file name size nlist;
1252 parse tail (ltabs-1) nlist
1254 | _ -> failwith (Printf.sprintf "Unknown line (%s)" first ) )
1255 | [] -> () )
1257 parse lines (pred 0) mlist;
1259 html_mods_cntr_init ();
1260 let line = ref 0 in
1261 if use_html_mods o then dc_filelist_files_print_html_header buf;
1262 let rec print node dir tabs =
1263 incr line;
1264 (match node with
1265 | MylistFile (name, size) ->
1266 filelist_file_print true (spaces tabs) username dir name size empty_string !line o;
1267 (*lprintf_nl "(%s) (%s)" (!spaces ^ name) (Int64.to_string size);*)
1268 | MylistDirectory (name, nlist) ->
1269 let dir =
1270 if dir = "" then name
1271 else dir ^ "/" ^ name
1273 filelist_file_print false (spaces tabs) username dir name empty_string empty_string !line o;
1274 (*lprintf_nl "(%s) list_count=(%d)" (!spaces ^ dir) (List.length !nlist);*)
1276 List.iter (fun node -> print node dir (tabs+1)) !nlist )
1278 List.iter (fun node -> print node empty_string 0) !mlist;
1279 if use_html_mods o then Printf.bprintf buf "\\</table\\>\\</div\\>";
1280 with e ->
1281 if !verbose_unexpected_messages then
1282 lprintf_nl "dcshowfilelist: (%s) in .DcLst parsing" (Printexc2.to_string e) )
1283 with _ -> if !verbose_unexpected_messages then lprintf_nl "Error in che3 decompressing" )
1285 end else if (extension = mylistxmlbz2_ext) then begin (* parse .xml.bz2 *)
1286 (try (* try to unzip *)
1287 let s = Buffer.contents (file_to_bz2_to_buffer (Filename.concat filelist_directory filename)) in
1288 (try (* try to parse xml and make a www page *)
1289 let xml = Xml.parse_string s in
1290 let parse_xml_chars s = (* in xml there are html-escapes mixed in *)
1291 let s = dc_replace_str_to_str s "&amp;" "&" in
1294 if ((Xml.tag xml) <> "FileListing") then failwith "Xml-file don't start with FileListing";
1295 html_mods_cntr_init ();
1296 let line = ref 1 in
1297 if use_html_mods o then dc_filelist_files_print_html_header buf;
1298 let rec parse x dir ndirs = (* iterate this with all xml elements (one node with tag and attributes *)
1299 let tag = Xml.tag x in
1300 let dir =
1301 if tag = "File" then dir (* if tag of element is file, return existing dir name *)
1302 else begin (* else this is "probably" a directory *)
1303 let newdir = Xml.attrib x "Name" in (* get dir name *)
1304 if dir = "" then newdir (* if this is first dir to add, don't add the "/" *)
1305 else dir ^ "/" ^ newdir (* else add this new dir path name to existing dir *)
1308 (match tag with
1309 | "File" -> (* is this xml element is a file element, add its line to http-page *)
1310 let fname = Xml.attrib x "Name" in
1311 let fname = parse_xml_chars fname in
1312 let fsize = Xml.attrib x "Size" in
1313 let ftth = Xml.attrib x "TTH" in
1314 let dir = dir in
1315 filelist_file_print true (spaces ndirs) username dir fname fsize ftth !line o;
1316 | "Directory" -> (* or if it is a directory element, add its line to http-page *)
1317 let fname = Xml.attrib x "Name" in
1318 let fname = parse_xml_chars fname in
1319 let fsize = empty_string in
1320 let ftth = empty_string in
1321 let dir = dir in
1322 filelist_file_print false (spaces ndirs) username dir fname fsize ftth !line o;
1323 | _ -> failwith "Tag not File or Directory" );
1324 incr line;
1325 Xml.iter (fun x -> parse x dir (ndirs+1)) x
1327 Xml.iter (fun x -> parse x empty_string 0) xml;
1328 if use_html_mods o then Printf.bprintf buf "\\</table\\>\\</div\\>";
1329 with
1330 | Error e -> if !verbose_unexpected_messages then lprintf_nl "%s" (Xml.error e)
1331 | e -> if !verbose_unexpected_messages then
1332 lprintf_nl "dcshowfilelist: (%s) in xml parsing" (Printexc2.to_string e) )
1333 with _ -> if !verbose_unexpected_messages then lprintf_nl "Error in bz2 unzipping" )
1334 end else if !verbose_unexpected_messages then lprintf_nl "dcshowfilelist: Filelist extension not valid" );
1335 empty_string
1336 ), "<name> : Show filelist for user";
1338 "dcautoconnect", Arg_two (fun arg1 arg2 o ->
1339 show_dc_buttons o;
1341 let s = Hashtbl.find servers_by_ip arg2 in
1342 let auto = bool_of_string arg1 in
1343 s.server_autoconnect <- auto;
1344 server_must_update s;
1345 "ok"
1346 with exn -> Printf.sprintf "Failed : %s" (Printexc2.to_string exn)
1347 ), "<true/false> <ip> : Set/unset the server autoconnection state";
1349 ] (* end of let commands = *)
1351 module P = GuiTypes
1353 (* register user operations *)
1354 let _ =
1355 register_commands commands;
1357 user_ops.op_user_info <- (fun user -> (* CHECK *)
1359 P.user_num = user.user_user.impl_user_num;
1360 P.user_md4 = Md4.null;
1361 P.user_name = user.user_nick;
1362 P.user_ip = Ip.null;
1363 P.user_port = 0;
1364 P.user_tags =
1365 (let list =
1366 if user.user_data > 1. then
1367 [ { tag_name = Field_UNKNOWN "link"; tag_value = String user.user_link };
1368 { tag_name = Field_UNKNOWN "shared"; tag_value = String (
1369 Printf.sprintf "%12.0f" user.user_data) } ]
1370 else []
1372 (match user.user_type with
1373 | Normal | Vip -> list
1374 | Op -> { tag_name = Field_UNKNOWN "admin"; tag_value = String "admin" } :: list ) );
1375 P.user_server =
1376 (match user.user_servers with
1377 | [] -> 0
1378 | s :: _ -> s.server_server.impl_server_num );
1381 user_ops.op_user_remove <- (fun user -> () )
1383 user_ops.op_user_browse_files <- (fun user ->
1384 let c = client_of_user user in
1385 contact_add (as_client c.client_client)
1387 user_ops.op_user_set_friend <- (fun user ->
1388 let c = client_of_user user in
1389 friend_add (as_client c.client_client)
1392 mutable op_user_network : network;
1393 mutable op_user_set_friend : ('a -> unit);
1394 mutable op_user_browse_files : ('a -> unit);
1399 (*module C = CommonTypes
1400 let _ =
1401 result_ops.op_result_info <- (fun r ->
1403 C.result_num = r.result_result.impl_result_num;
1404 C.result_network = network.network_num;
1406 C.result_names = [r.result_name];
1407 C.result_md4 = Md4.null;
1408 C.result_size = r.result_size;
1409 C.result_format = result_format_of_name r.result_name;
1410 C.result_type = result_media_of_name r.result_name;
1411 C.result_tags = [];
1412 C.result_comment = "";
1413 C.result_done = false;
1419 (* register file operations *)
1420 let _ =
1421 file_ops.op_file_info <- (fun file ->
1423 P.file_fields = Fields_file_info.all;
1424 P.file_comment = empty_string;
1425 P.file_name = file.file_name;
1426 P.file_num = (file_num file);
1427 P.file_network = network.network_num;
1428 P.file_names = [file.file_name];
1429 P.file_md4 = Md4.null;
1430 P.file_size = file_size file;
1431 P.file_downloaded = file_downloaded file;
1432 P.file_all_sources = 0;
1433 P.file_active_sources = 0;
1434 P.file_state = file_state file;
1435 P.file_sources = None;
1436 P.file_download_rate = file_download_rate file.file_file;
1437 P.file_chunks = None;
1438 P.file_chunk_size = None;
1439 P.file_availability = [network.network_num, "0"];
1440 P.file_format = FormatUnknown;
1441 P.file_chunks_age = [|0|];
1442 P.file_age = file_age file;
1443 P.file_last_seen = BasicSocket.last_time ();
1444 P.file_priority = file_priority (as_file file.file_file);
1445 P.file_uids = [];
1446 P.file_sub_files = [];
1447 P.file_comments = [];
1448 P.file_magic = None;
1449 P.file_user = empty_string;
1450 P.file_group = empty_string;
1451 P.file_release = false;
1454 file_ops.op_file_all_sources <- (fun file ->
1455 List2.tail_map (fun c -> as_client c.client_client)
1456 file.file_clients
1458 file_ops.op_file_active_sources <- file_ops.op_file_all_sources;
1459 file_ops.op_file_cancel <- (fun file ->
1460 let remove_files_clients_not_downloading () =
1461 List.iter (fun c ->
1462 (match c.client_state with
1463 | DcDownload f -> () (* only one client should be in this state *)
1464 | _ ->
1465 remove_client c )
1466 ) file.file_clients;
1468 (try
1469 List.iter (fun c -> (* find one files client that is currently downloading *)
1470 (match c.client_state with
1471 | DcDownload _ -> raise (Found_client c)
1472 | _ -> () )
1473 ) file.file_clients;
1474 raise Not_found
1475 with
1476 | Found_client c -> (* found a download slot tried to continue *)
1477 (match (DcClients.find_next_client c) with (* try to continue slot *)
1478 | Some cl ->
1479 (match c.client_sock with
1480 | Connection sock ->
1481 remove_files_clients_not_downloading ();
1482 remove_file_not_clients file;
1483 c.client_receiving <- Int64.zero;
1484 c.client_pos <- Int64.zero;
1485 TcpBufferedSocket.set_rtimeout sock 30.;
1486 DcClients.next_download false c sock cl (* try to change downloading *)
1487 | _ -> () )
1488 | None ->
1489 remove_files_clients_not_downloading ();
1490 remove_file_not_clients file;
1491 c.client_state <- DcIdle;
1492 (match c.client_sock with
1493 | Connection sock -> TcpBufferedSocket.close sock (Closed_for_error "File cancelled")
1494 | _ -> () )
1496 | Not_found ->
1497 remove_file_with_clients file );
1498 if !verbose_download then lprintf_nl "File %s cancelled" file.file_name;
1500 file_ops.op_file_commit <- (fun file name ->
1501 remove_file_with_clients file;
1503 file_ops.op_file_pause <- (fun file ->
1504 (try
1505 List.iter (fun c ->
1506 (match c.client_state with
1507 | DcDownload f -> if file == f then raise (Found_client c)
1508 | _ -> () )
1509 ) file.file_clients;
1510 raise Not_found
1511 with
1512 | Found_client c ->
1513 (match (DcClients.find_next_client c) with (* try to continue slot *)
1514 | Some cl ->
1515 (match c.client_sock with
1516 | Connection sock ->
1517 DcClients.next_download true c sock cl (* try to change downloading *)
1518 | _ -> () )
1519 | None -> c.client_state <- DcIdle (* DcPaused ? *))
1520 | Not_found -> () ) (* Should not happen *)
1524 file_ops.op_file_files <- (fun file impl ->
1525 match file.file_swarmer with
1526 None -> [CommonFile.as_file impl]
1527 | Some swarmer ->
1528 CommonSwarming.subfiles swarmer)
1529 ; *)
1531 file_ops.op_file_save_as <- (fun file new_name ->
1532 match file_state file with
1533 FileDownloaded | FileShared ->
1534 DcClients.save_file_as file new_name
1535 | _ -> ()
1539 (*let client_of_user user =
1540 let c = new_client user.user_nick in
1546 mutable op_file_network : network; *)
1547 file_ops.op_file_save_as <- (fun _ _ -> ());
1548 file_ops.op_file_resume <- (fun _ -> ());
1549 file_ops.op_file_set_format <- (fun _ _ -> ());
1550 file_ops.op_file_check <- (fun _ -> ());
1551 file_ops.op_file_recover <- (fun _ -> ());
1552 file_ops.op_file_print <- (fun file o ->
1553 let buf = o.conn_buf in
1554 if use_html_mods o then
1555 begin
1556 let td l =
1557 Printf.bprintf buf "\\</tr\\>\\<tr class=\\\"dl-%d\\\"\\>" (html_mods_cntr ());
1558 html_mods_td buf l
1560 td [
1561 ("Directory", "sr br", "Directory");
1562 ("", "sr", file.file_directory) ];
1563 td [
1564 ("Filename", "sr br", "Filename");
1565 ("", "sr", file.file_name) ];
1566 td [
1567 ("Tiger tree hash and magnet url", "sr", "TTH and magnet");
1568 ("", "sr", html_show_file file) ];
1569 td [
1570 ("Automatic TTH searches performed", "sr", "Autosearches");
1571 ("", "sr", string_of_int file.file_autosearch_count) ];
1573 else
1575 (*file_ops.op_file_print_html <- (fun _ _ -> lprintf_nl "Received (op_file_print_html)"; ());*)
1576 (*file_ops.op_file_print_sources_html <- (fun _ _ -> lprintf_nl "Received (op_file_print_sources_html)"; ())*)
1577 (* mutable op_file_files : ('a -> 'a file_impl -> file list);
1578 mutable op_file_debug : ('a -> string);
1579 mutable op_file_proposed_filenames : ('a -> string list);
1582 let _ =
1583 CommonWeb.add_web_kind "hublist" "DirectConnect hublist"
1584 (fun url filename ->
1585 if !!enable_directconnect then
1586 begin
1588 dc_hublist := (
1589 match List.rev (String2.split filename '.') with
1590 | "bz2"::"xml"::_ -> DcServers.make_hublist_from_xml (Xml.parse_file (Misc2.bz2_extract filename))
1591 | "xml"::_ -> DcServers.make_hublist_from_xml (Xml.parse_file filename)
1592 | "bz2"::_ -> DcServers.make_hublist_from_file (Misc2.bz2_extract filename)
1593 | _ -> DcServers.make_hublist_from_file filename);
1594 lprintf_nl "loaded dc++ hublist, %d entries" (List.length !dc_hublist)
1595 with e ->
1596 if !verbose_msg_servers then
1597 lprintf_nl "(%s) in loading/parsing serverlist" (Printexc2.to_string e);
1598 raise Not_found
1600 else
1601 begin
1602 lprintf_nl "DirectConnect module is disabled, ignoring...";
1603 raise Not_found