patch #7372
[mldonkey.git] / src / networks / donkey / donkeyProtoCom.ml
blobb2747eec310b8d9645a664f921d6f605e6120d9b
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
19 open Options
20 open Printf2
22 open BasicSocket
23 open TcpBufferedSocket
25 open AnyEndian
26 open LittleEndian
28 open CommonOptions
29 open CommonTypes
30 open CommonGlobals
31 open CommonFile
32 open CommonGlobals
34 open DonkeyOptions
35 open DonkeyGlobals
36 open DonkeyTypes
37 open DonkeyMftp
39 let buf = TcpBufferedSocket.internal_buf
41 let client_msg_to_string emule_version msg =
42 Buffer.reset buf;
43 buf_int8 buf 0;
44 buf_int buf 0;
45 let magic = DonkeyProtoClient.write emule_version buf msg in
46 let s = Buffer.contents buf in
47 let len = String.length s - 5 in
48 s.[0] <- char_of_int magic;
49 str_int s 1 len;
52 let server_msg_to_string msg =
53 Buffer.reset buf;
54 buf_int8 buf 227;
55 buf_int buf 0;
56 DonkeyProtoServer.write buf msg;
58 if !verbose_msg_servers then begin
59 lprintf_nl "MESSAGE TO SERVER:";
60 DonkeyProtoServer.print msg;
61 lprint_newline ();
62 end;
64 let s = Buffer.contents buf in
65 let len = String.length s - 5 in
66 str_int s 1 len;
69 let server_send sock m =
71 lprintf "Message to server"; lprint_newline ();
72 DonkeyProtoServer.print m;
74 write_string sock (server_msg_to_string m)
76 let direct_client_sock_send emule_version sock m =
77 write_string sock (client_msg_to_string emule_version m)
79 let client_send c m =
80 let emule_version = c.client_emule_proto in
81 do_if_connected c.client_source.DonkeySources.source_sock (fun sock ->
82 if !verbose_msg_clients || c.client_debug then begin
83 lprintf_nl "Sent to client %s" (full_client_identifier c);
84 DonkeyProtoClient.print m;
85 lprint_newline ();
86 end;
87 direct_client_sock_send emule_version sock m)
89 let servers_send socks m =
90 let m = server_msg_to_string m in
91 List.iter (fun s -> write_string s m) socks
93 let client_handler2 c ff f =
94 let msgs = ref 0 in
95 fun sock nread ->
97 let module M= DonkeyProtoClient in
98 let b = TcpBufferedSocket.buf sock in
99 try
100 while b.len >= 5 do
101 let emule_version = match !c with
102 None -> emule_proto ();
103 | Some c -> c.client_emule_proto
105 let opcode = get_uint8 b.buf b.pos in
106 let msg_len = get_int b.buf (b.pos+1) in
107 if b.len >= 5 + msg_len then
108 begin
109 let s = String.sub b.buf (b.pos+5) msg_len in
110 buf_used b (msg_len + 5);
111 let t = M.parse emule_version opcode s in
112 (* M.print t;
113 lprint_newline (); *)
114 incr msgs;
115 match !c with
116 None ->
117 c := ff t sock
118 | Some c -> f c t sock
120 else raise Not_found
121 done
122 with Not_found -> ()
124 let cut_messages parse f sock nread =
125 let b = TcpBufferedSocket.buf sock in
127 while b.len >= 5 do
128 let opcode = get_uint8 b.buf b.pos in
129 let msg_len = get_int b.buf (b.pos+1) in
130 if b.len >= 5 + msg_len then
131 begin
132 let s = String.sub b.buf (b.pos+5) msg_len in
133 buf_used b (msg_len + 5);
134 let t = parse opcode s in
135 f t sock
137 else raise Not_found
138 done
139 with Not_found -> ()
141 let really_udp_send t ip port msg isping =
143 if !verbose_udp then begin
144 lprintf_nl "Message UDP%s to %s:%d\n%s"
145 (if isping then "(PING)" else "") (Ip.to_string ip)
146 port (DonkeyProtoUdp.print msg);
147 end;
150 Buffer.reset buf;
151 DonkeyProtoUdp.write buf msg;
152 let s = Buffer.contents buf in
153 UdpSocket.write t isping s ip port
154 with e ->
155 lprintf_nl "Exception %s in udp_send" (Printexc2.to_string e)
157 let udp_send t ip port msg =
158 really_udp_send t ip port msg false
160 let udp_handler f sock event =
161 let module M = DonkeyProtoUdp in
162 match event with
163 UdpSocket.READ_DONE ->
164 UdpSocket.read_packets sock (fun p ->
166 let pbuf = p.UdpSocket.udp_content in
167 let len = String.length pbuf in
168 if len > 0 then
169 let t = M.parse (int_of_char pbuf.[0])
170 (String.sub pbuf 1 (len-1)) in
171 (* M.print t; *)
172 f t p
173 with e -> ()
175 | _ -> ()
177 let udp_basic_handler f sock event =
178 match event with
179 UdpSocket.READ_DONE ->
180 UdpSocket.read_packets sock (fun p ->
182 let pbuf = p.UdpSocket.udp_content in
183 let len = String.length pbuf in
184 if len = 0 ||
185 int_of_char pbuf.[0] <> DonkeyOpenProtocol.udp_magic then begin
186 if !verbose_unknown_messages then begin
187 lprintf_nl "Received unknown UDP packet";
188 dump pbuf;
189 end;
190 end else begin
191 let t = String.sub pbuf 1 (len-1) in
192 f t p
194 with e ->
195 lprintf_nl "Error %s in udp_basic_handler"
196 (Printexc2.to_string e)
198 | _ -> ()
201 let new_string msg s =
202 let len = String.length s - 5 in
203 str_int s 1 len
205 let empty_string = ""
207 let tag_file file =
208 (string_tag Field_Filename
210 let name = file_best_name file in
211 let name = if String2.starts_with name "hidden." then
212 String.sub name 7 (String.length name - 7)
213 else name in
214 if !verbose_share then lprintf_nl "tag_file: Sharing %s" name;
215 name
216 ))::
217 (int64_tag Field_Size_Hi (Int64.shift_right_logical file.file_file.impl_file_size 32)) ::
218 (int64_tag Field_Size (Int64.logand file.file_file.impl_file_size 0xffffffffL)) ::
220 (match file.file_format with
221 FormatNotComputed next_time when
222 next_time < last_time () ->
223 (try
224 if !verbose_share then lprintf_nl "Find format %s"
225 (file_disk_name file);
226 file.file_format <- (
227 match
228 CommonMultimedia.get_info
229 (file_disk_name file)
230 with
231 FormatUnknown -> FormatNotComputed (last_time () + 300)
232 | x -> x)
233 with _ -> ())
234 | _ -> ()
237 match file.file_format with
238 FormatNotComputed _ | FormatUnknown -> []
239 | AVI _ ->
241 { tag_name = Field_Type; tag_value = String "Video" };
242 { tag_name = Field_Format; tag_value = String "avi" };
244 | MP3 _ ->
246 { tag_name = Field_Type; tag_value = String "Audio" };
247 { tag_name = Field_Format; tag_value = String "mp3" };
249 | OGG l ->
250 begin
251 let max_st = ref OGG_INDEX_STREAM in
252 List.iter (fun st ->
253 if st.stream_type > !max_st
254 then max_st := st.stream_type;
255 ) l;
256 match !max_st with
257 OGG_AUDIO_STREAM
258 | OGG_VORBIS_STREAM ->
260 { tag_name = Field_Type; tag_value = String "Audio" };
261 { tag_name = Field_Format; tag_value = String "ogg" };
263 | OGG_VIDEO_STREAM
264 | OGG_THEORA_STREAM ->
266 { tag_name = Field_Type; tag_value = String "Video" };
267 { tag_name = Field_Format; tag_value = String "ogg" };
269 | _ -> []
271 | FormatType (format, kind) ->
273 { tag_name = Field_Type; tag_value = String kind };
274 { tag_name = Field_Format; tag_value = String format };
278 (* Computes tags for shared files (for clients) *)
279 let make_tagged sock files =
280 (List2.tail_map (fun file ->
282 f_md4 = file.file_md4;
283 f_ip = client_ip sock;
284 f_port = !!donkey_port;
285 f_tags = tag_file file;
287 ) files)
289 (* Computes tags for shared files with the special ip and
290 port values for newer servers. We should assume that the
291 server is newer if it supports compression, like emule
292 does it. *)
293 let make_tagged_server newer_server sock files =
294 if newer_server then
295 (List2.tail_map (fun file ->
297 f_md4 = file.file_md4;
298 f_ip = (if (file_state file = FileShared) then Ip.of_string "251.251.251.251" else Ip.of_string "252.252.252.252");
299 f_port = (if (file_state file = FileShared) then 0xFBFB else 0xFCFC);
300 f_tags = tag_file file;
302 ) files)
303 else
304 make_tagged sock files
306 let server_send_share compressed sock msg =
307 let max_len =
308 !!client_buffer_size - 100
309 - TcpBufferedSocket.remaining_to_write sock
311 Buffer.reset buf;
312 let s =
313 buf_int buf 0;
314 let nfiles, prev_len =
315 DonkeyProtoServer.Share.write_files_max buf
316 ( make_tagged_server compressed (Some sock) msg )
317 0 max_len
319 let s = Buffer.contents buf in
320 str_int s 0 nfiles;
321 let s = String.sub s 0 prev_len in
322 if !verbose_share || !verbose then
323 lprintf_nl "Sending %d share%s to server %s:%d%s"
324 nfiles (Printf2.print_plural_s nfiles) (Ip.to_string (peer_ip sock)) (peer_port sock)
325 (if compressed then " (zlib)" else "");
326 Buffer.reset buf;
327 let s_c =
328 if compressed then
329 Zlib.compress_string s
330 else
333 (* Emule only sends the string compressed when it
334 is smaller in that state. *)
335 if compressed && ((String.length s_c) < (String.length s)) then
336 begin
337 buf_int8 buf 0xD4;
338 buf_int buf 0;
339 buf_int8 buf 21; (* ShareReq *)
340 Buffer.add_string buf s_c;
341 Buffer.contents buf
343 else
344 begin
345 buf_int8 buf 227;
346 buf_int buf 0;
347 buf_int8 buf 21; (* ShareReq *)
348 Buffer.add_string buf s;
349 Buffer.contents buf
352 let len = String.length s - 5 in
353 str_int s 1 len;
354 write_string sock s
356 let client_send_files sock msg =
357 let max_len = !!client_buffer_size - 100 -
358 TcpBufferedSocket.remaining_to_write sock in
359 Buffer.reset buf;
360 buf_int8 buf 227;
361 buf_int buf 0;
362 buf_int8 buf 75; (* ViewFilesReply *)
363 buf_int buf 0;
364 let nfiles, prev_len = DonkeyProtoClient.ViewFilesReply.write_files_max buf (
365 make_tagged (Some sock) msg)
366 0 max_len in
367 let s = Buffer.contents buf in
368 let s = String.sub s 0 prev_len in
369 let len = String.length s - 5 in
370 str_int s 1 len;
371 str_int s 6 nfiles;
372 write_string sock s
374 let client_send_dir sock dir files =
375 let max_len = !!client_buffer_size - 100 -
376 TcpBufferedSocket.remaining_to_write sock in
377 Buffer.reset buf;
378 buf_int8 buf 227;
379 buf_int buf 0;
380 buf_int8 buf 96; (* ViewFilesDirReply *)
381 buf_string buf dir;
382 buf_int buf 0;
383 let pos = Buffer.length buf in
384 let nfiles, prev_len = DonkeyProtoClient.ViewFilesReply.write_files_max buf (
385 make_tagged (Some sock) files)
386 0 max_len in
387 let s = Buffer.contents buf in
388 let s = String.sub s 0 prev_len in
389 let len = String.length s - 5 in begin
390 str_int s 1 len;
391 str_int s (pos-4) nfiles;
392 end;
393 write_string sock s
395 let udp_server_send s t =
396 udp_send (get_udp_sock ()) s.server_ip (s.server_port+4) t
398 let udp_server_send_ping s t =
399 really_udp_send (get_udp_sock ()) s.server_ip (s.server_port+4) t true