ml-ruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +0 -0
- data/lib/ml-ruby.rb +1 -0
- data/lib/ml-ruby/ml_client.rb +192 -0
- data/lib/ml-ruby/ml_data_types.rb +339 -0
- data/lib/ml-ruby/ml_protocol.rb +161 -0
- data/rakefile.rb +49 -0
- data/test/test_datatypes.rb +42 -0
- metadata +60 -0
data/README
ADDED
File without changes
|
data/lib/ml-ruby.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'ml-ruby/ml_client'
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# ml_ruby: Ruby Bindings for the ML-Donkey GUI protocol
|
2
|
+
# Copyright (C) 2008 Jens Herrmann
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
require 'socket'
|
19
|
+
include Socket::Constants
|
20
|
+
require File.dirname(__FILE__) + '/ml_protocol'
|
21
|
+
|
22
|
+
module ML_ruby
|
23
|
+
class ML_client
|
24
|
+
|
25
|
+
PROTOCOL_VERSION= 41
|
26
|
+
MAX_OPCODE_SENT= 59
|
27
|
+
MAX_OPCODE_RCVD= 68
|
28
|
+
|
29
|
+
TIME_OUT= 10
|
30
|
+
|
31
|
+
# public methods
|
32
|
+
|
33
|
+
def initialize host='localhost', port=4001, user=nil, password=nil
|
34
|
+
@listener= []
|
35
|
+
@send_queue= []
|
36
|
+
# ML_payload_handler::Types.keys.each do |opcode|
|
37
|
+
# add_listener opcode, self, :log
|
38
|
+
# end
|
39
|
+
connect host, port, user, password
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_current_downloads
|
43
|
+
send_payload= ML_S_get_downloading_files.new( {} )
|
44
|
+
recv_payload= ML_R_downloading_files
|
45
|
+
send_and_wait_for send_payload, recv_payload
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_finished_downloads
|
49
|
+
send_payload= ML_S_get_downloaded_files.new( {} )
|
50
|
+
recv_payload= ML_R_downloaded_files
|
51
|
+
send_and_wait_for send_payload, recv_payload
|
52
|
+
end
|
53
|
+
|
54
|
+
def start_download url
|
55
|
+
send_opcode= ML_S_download_url.class.opcode
|
56
|
+
sd= ML_message.new send_opcode, ML_S_download_url.new( :url=> url )
|
57
|
+
add_to_send_queue sd
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
def send_and_wait_for send_payload, recv_payload
|
65
|
+
@data= nil
|
66
|
+
send_opcode=send_payload.class.opcode
|
67
|
+
recv_opcode=recv_payload.opcode
|
68
|
+
msg= ML_message.new send_opcode, send_payload
|
69
|
+
add_listener recv_opcode, self, :handle
|
70
|
+
add_to_send_queue msg
|
71
|
+
timer= Time.now
|
72
|
+
while ! @data && timer > Time.now - TIME_OUT # seconds ago
|
73
|
+
sleep 0.5
|
74
|
+
end
|
75
|
+
remove_listener recv_opcode, self, :handle
|
76
|
+
return @data.payload if @data
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def add_listener opcode, listener, method
|
82
|
+
# puts "Added: #{opcode}, #{listener}, #{method}"
|
83
|
+
@listener[opcode]= [] unless @listener[opcode]
|
84
|
+
@listener[opcode]<<[listener,method]
|
85
|
+
end
|
86
|
+
|
87
|
+
def remove_listener opcode, listener, method
|
88
|
+
# puts "removed: #{opcode}, #{listener}, #{method}"
|
89
|
+
@listener[opcode].delete [listener,method]
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_to_send_queue msg
|
93
|
+
@send_queue<< msg
|
94
|
+
end
|
95
|
+
|
96
|
+
def handle msg
|
97
|
+
@data= msg
|
98
|
+
end
|
99
|
+
|
100
|
+
def log msg
|
101
|
+
# puts msg.inspect
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
def connect host, port, user, password
|
106
|
+
@socket = Socket.new( AF_INET, SOCK_STREAM, 0 )
|
107
|
+
sockaddr = Socket.pack_sockaddr_in( port, host )
|
108
|
+
@socket.connect( sockaddr )
|
109
|
+
if get_core_protocol
|
110
|
+
send_protocol_version
|
111
|
+
send_interested_in_sources
|
112
|
+
first_message= login user, password
|
113
|
+
if first_message.opcode.payload== ML_R_bad_password.opcode
|
114
|
+
raise "Wrong credentials"
|
115
|
+
end
|
116
|
+
do_handle @listener, first_message
|
117
|
+
Thread.abort_on_exception = true
|
118
|
+
@thread= Thread.new(@socket,@send_queue,@listener) do |socket,send_queue,listener|
|
119
|
+
loop do
|
120
|
+
msg_to_send= send_queue.shift
|
121
|
+
send_message msg_to_send if msg_to_send
|
122
|
+
msg_received= receive_message
|
123
|
+
do_handle listener, msg_received if msg_received
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def receive_message
|
130
|
+
selected= select( [@socket], nil, nil, 0.5 )
|
131
|
+
if selected
|
132
|
+
socket= selected[0][0]
|
133
|
+
paket_size= ML_int32.decode( socket.recvfrom( ML_int32.size )[0])
|
134
|
+
end
|
135
|
+
result= nil
|
136
|
+
if paket_size
|
137
|
+
opcode= ML_int16.decode( socket.recvfrom( ML_int16.size )[0])
|
138
|
+
raw_data= socket.recvfrom( paket_size.payload - opcode.size )
|
139
|
+
handler= ML_payload_handler.for( opcode.payload )
|
140
|
+
unless handler
|
141
|
+
result=ML_message.new opcode, ML_R_unknown.new( {:raw_data=> raw_data.inspect } )
|
142
|
+
else
|
143
|
+
payload= ML_payload_handler.for( opcode.payload ).decode( raw_data[0] )
|
144
|
+
result=ML_message.new opcode, payload
|
145
|
+
end
|
146
|
+
end
|
147
|
+
return result
|
148
|
+
end
|
149
|
+
|
150
|
+
def send_message msg
|
151
|
+
@socket.write msg.encode
|
152
|
+
end
|
153
|
+
|
154
|
+
def do_handle listener, msg
|
155
|
+
listener= listener[msg.opcode.payload]
|
156
|
+
listener.each do |l,method|
|
157
|
+
l.send method, msg
|
158
|
+
end if listener
|
159
|
+
end
|
160
|
+
|
161
|
+
# Handling of basic messages for starting connection
|
162
|
+
|
163
|
+
def get_core_protocol
|
164
|
+
cp= receive_message
|
165
|
+
|
166
|
+
return cp.opcode.payload==ML_R_core_protocol.opcode &&
|
167
|
+
cp.payload.max_protocol_version.payload>= PROTOCOL_VERSION &&
|
168
|
+
cp.payload.max_opcode_sent.payload>= MAX_OPCODE_SENT &&
|
169
|
+
cp.payload.max_opcode_rcvd.payload>= MAX_OPCODE_RCVD
|
170
|
+
end
|
171
|
+
|
172
|
+
def send_protocol_version
|
173
|
+
opcode= 0
|
174
|
+
pv= ML_message.new opcode, ML_S_protocol_version.new( :protocol_version=>PROTOCOL_VERSION )
|
175
|
+
send_message pv
|
176
|
+
end
|
177
|
+
|
178
|
+
def send_interested_in_sources
|
179
|
+
opcode= 64
|
180
|
+
pv= ML_message.new opcode, ML_S_interested_in_sources.new( :interested_in_sources=>0 )
|
181
|
+
send_message pv
|
182
|
+
end
|
183
|
+
|
184
|
+
def login user, password
|
185
|
+
opcode= 52
|
186
|
+
login_msg= ML_message.new opcode, ML_S_password.new( {:password=>password, :login_name=>user} )
|
187
|
+
send_message login_msg
|
188
|
+
receive_message
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,339 @@
|
|
1
|
+
# ml_ruby: Ruby Bindings for the ML-Donkey GUI protocol
|
2
|
+
# Copyright (C) 2008 Jens Herrmann
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
module ML_ruby
|
19
|
+
class ML_atom
|
20
|
+
attr_reader :payload
|
21
|
+
def initialize payload
|
22
|
+
@payload= payload
|
23
|
+
end
|
24
|
+
def size
|
25
|
+
self.class.size
|
26
|
+
end
|
27
|
+
def self.size
|
28
|
+
@size
|
29
|
+
end
|
30
|
+
def self.packstring
|
31
|
+
@packstring
|
32
|
+
end
|
33
|
+
def encode
|
34
|
+
[ @payload ].pack self.class.packstring
|
35
|
+
end
|
36
|
+
def self.decode data
|
37
|
+
self.new( data.unpack( self.packstring )[0] )
|
38
|
+
end
|
39
|
+
def inspect
|
40
|
+
"#{self.class.name}: #{@payload}"
|
41
|
+
end
|
42
|
+
def == other
|
43
|
+
false if not other
|
44
|
+
@payload== other.payload
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class ML_int64 < ML_atom; @size= 8; @packstring= 'Q'; end
|
49
|
+
class ML_int32 < ML_atom; @size= 4; @packstring= 'L'; end
|
50
|
+
class ML_uint32 < ML_atom; @size= 4; @packstring= 'L'; end
|
51
|
+
class ML_int16 < ML_atom; @size= 2; @packstring= 'S'; end
|
52
|
+
class ML_int8 < ML_atom; @size= 1; @packstring= 'C'; end
|
53
|
+
class ML_char < ML_atom; @size= 1; @packstring= 'a'; end
|
54
|
+
class ML_char16 < ML_atom; @size= 16; @packstring= 'a16'; end
|
55
|
+
|
56
|
+
class ML_list
|
57
|
+
attr_reader :payload
|
58
|
+
def initialize payload
|
59
|
+
@payload= payload
|
60
|
+
end
|
61
|
+
def size
|
62
|
+
ML_int16.size + @payload.inject( 0 ) {|sum,elem| sum+elem.size}
|
63
|
+
end
|
64
|
+
def encode
|
65
|
+
ML_int16.new( @payload.size ).encode + @payload.inject("") {|sum,elem| sum+elem.encode }
|
66
|
+
end
|
67
|
+
def self.decode data
|
68
|
+
# puts self.name, data.inspect
|
69
|
+
len= ML_int16.decode data
|
70
|
+
data= data[len.size..-1]
|
71
|
+
result=[]
|
72
|
+
len.payload.times do
|
73
|
+
elem= @type.decode( data )
|
74
|
+
# puts self.name, elem.to_yaml
|
75
|
+
result<< elem
|
76
|
+
data= data[elem.size..-1]
|
77
|
+
end
|
78
|
+
self.new result
|
79
|
+
end
|
80
|
+
def == other
|
81
|
+
false unless other
|
82
|
+
payload == other.payload
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class ML_string < ML_list
|
87
|
+
@type= ML_char
|
88
|
+
def initialize payload
|
89
|
+
if payload.is_a?( Array )
|
90
|
+
super payload
|
91
|
+
else
|
92
|
+
@payload= payload.scan(/./).inject([]) {|sum,char| sum<< ML_char.new( char ) }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
def payload
|
96
|
+
@payload.map {|c|c.payload}.join
|
97
|
+
end
|
98
|
+
def inspect
|
99
|
+
"#{self.class.name}: \"#{payload}\""
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class ML_float < ML_string
|
104
|
+
@type= ML_char
|
105
|
+
def initialize payload
|
106
|
+
# puts payload.inspect
|
107
|
+
unless payload.is_a? Array
|
108
|
+
if (payload*100).to_i % 10 == 0
|
109
|
+
payload= payload.to_s + "0"
|
110
|
+
else
|
111
|
+
payload= payload.to_s.gsub(/\.0/,'.')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
super payload
|
115
|
+
end
|
116
|
+
def payload
|
117
|
+
@payload.map {|c|c.payload}.join.gsub(/\.(\d)$/,'.0\1').to_f
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class ML_list_of_int16 < ML_list
|
122
|
+
@type= ML_int16
|
123
|
+
end
|
124
|
+
class ML_list_of_int32 < ML_list
|
125
|
+
@type= ML_int32
|
126
|
+
end
|
127
|
+
class ML_list_of_string < ML_list
|
128
|
+
@type= ML_string
|
129
|
+
end
|
130
|
+
|
131
|
+
class ML_compound
|
132
|
+
def self.attributes
|
133
|
+
@attributes
|
134
|
+
end
|
135
|
+
def self.conditions
|
136
|
+
@conditions
|
137
|
+
end
|
138
|
+
def initialize data
|
139
|
+
self.class.attributes.each do |attr,type|
|
140
|
+
set_compound_attribute( attr, data[attr], type )
|
141
|
+
end
|
142
|
+
if self.class.conditions
|
143
|
+
self.class.conditions.each do |test,extra|
|
144
|
+
key=data[test].payload
|
145
|
+
extra[key].each do |attr,type|
|
146
|
+
set_compound_attribute( attr, data[attr], type )
|
147
|
+
end if extra[key]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
def size
|
152
|
+
self.class.attributes.inject( 0 ) { |sum,attr| sum+send( attr[0] ).size }
|
153
|
+
end
|
154
|
+
def encode
|
155
|
+
result=""
|
156
|
+
self.class.attributes.each do |attr,type|
|
157
|
+
result<<( send( attr ).encode )
|
158
|
+
end
|
159
|
+
if self.class.conditions
|
160
|
+
# puts attributes.inspect
|
161
|
+
self.class.conditions.each do |test,extra|
|
162
|
+
# puts "testing #{[test,extra].inspect}"
|
163
|
+
key= send( test ).payload
|
164
|
+
extra[key].each do |attr,type|
|
165
|
+
result<<( send( attr ).encode )
|
166
|
+
end if extra[key]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
result
|
170
|
+
end
|
171
|
+
def self.decode data
|
172
|
+
# puts data.inspect
|
173
|
+
attributes={}
|
174
|
+
self.attributes.each do |attr,type|
|
175
|
+
attributes[attr]= type.decode data
|
176
|
+
data= data[attributes[attr].size..-1]
|
177
|
+
end
|
178
|
+
if conditions
|
179
|
+
# puts attributes.inspect
|
180
|
+
conditions.each do |test,extra|
|
181
|
+
# puts "testing #{[test,extra].inspect}"
|
182
|
+
key= attributes[test].payload
|
183
|
+
extra[key].each do |attr,type|
|
184
|
+
attributes[attr]= type.decode data
|
185
|
+
data= data[attributes[attr].size..-1]
|
186
|
+
end if extra[key]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
self.new attributes
|
190
|
+
end
|
191
|
+
protected
|
192
|
+
def self.attr_reader *sym
|
193
|
+
super *sym
|
194
|
+
end
|
195
|
+
def set_compound_attribute( attr, value, type )
|
196
|
+
# puts [attr, value, type].inspect
|
197
|
+
self.class.attr_reader attr
|
198
|
+
if value.is_a? type
|
199
|
+
eval "@#{attr}= value"
|
200
|
+
else
|
201
|
+
eval "@#{attr}= #{type}.new value"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class ML_filestate < ML_compound
|
207
|
+
@attributes= [
|
208
|
+
[:state,ML_int8],
|
209
|
+
]
|
210
|
+
@conditions= [[:state,
|
211
|
+
{ 6=>[
|
212
|
+
[:reason,ML_string],
|
213
|
+
]
|
214
|
+
},
|
215
|
+
]]
|
216
|
+
end
|
217
|
+
|
218
|
+
class ML_availability < ML_compound
|
219
|
+
@attributes= [
|
220
|
+
[:network,ML_int32],
|
221
|
+
[:chunks,ML_string],
|
222
|
+
]
|
223
|
+
end
|
224
|
+
class ML_list_of_availability < ML_list
|
225
|
+
@type= ML_availability
|
226
|
+
end
|
227
|
+
|
228
|
+
class ML_ogg_stream_tags < ML_compound
|
229
|
+
@attributes=[
|
230
|
+
# TODO: http://mldonkey.sourceforge.net/GuiOGGStreamTag
|
231
|
+
]
|
232
|
+
end
|
233
|
+
class ML_list_of_ogg_stream_tags < ML_list
|
234
|
+
@type= ML_ogg_stream_tags
|
235
|
+
end
|
236
|
+
class ML_ogg_stream < ML_compound
|
237
|
+
@attributes=[
|
238
|
+
[:number=>ML_int32],
|
239
|
+
[:type=>ML_int8],
|
240
|
+
[:tags=>ML_list_of_ogg_stream_tags]
|
241
|
+
]
|
242
|
+
end
|
243
|
+
|
244
|
+
class ML_list_of_ogg_stream < ML_list
|
245
|
+
@type= ML_ogg_stream
|
246
|
+
end
|
247
|
+
|
248
|
+
class ML_subfile < ML_compound
|
249
|
+
@attributes=[
|
250
|
+
[:name=>ML_string],
|
251
|
+
[:size=>ML_int64],
|
252
|
+
[:format=>ML_string]
|
253
|
+
]
|
254
|
+
end
|
255
|
+
|
256
|
+
class ML_list_of_subfile < ML_list
|
257
|
+
@type= ML_subfile
|
258
|
+
end
|
259
|
+
|
260
|
+
class ML_comment < ML_compound
|
261
|
+
@attributes=[
|
262
|
+
[:ip=>ML_int32],
|
263
|
+
[:geo_ip=>ML_int8],
|
264
|
+
[:name=>ML_string],
|
265
|
+
[:rating=>ML_int64],
|
266
|
+
[:comment=>ML_string]
|
267
|
+
]
|
268
|
+
end
|
269
|
+
|
270
|
+
class ML_list_of_comment < ML_list
|
271
|
+
@type= ML_comment
|
272
|
+
end
|
273
|
+
|
274
|
+
class ML_fileformat < ML_compound
|
275
|
+
@attributes= [
|
276
|
+
[:format,ML_int8],
|
277
|
+
]
|
278
|
+
@conditions= [[:format,
|
279
|
+
{ 1=>[
|
280
|
+
[:extension,ML_string],
|
281
|
+
[:kind,ML_string],
|
282
|
+
],
|
283
|
+
2=>[
|
284
|
+
[:codec=>ML_string],
|
285
|
+
[:width=>ML_int32],
|
286
|
+
[:height=>ML_int32],
|
287
|
+
[:fps=>ML_int32],
|
288
|
+
[:rate=>ML_int32],
|
289
|
+
],
|
290
|
+
3=>[
|
291
|
+
[:mp3_title=>ML_string],
|
292
|
+
[:mp3_artist=>ML_string],
|
293
|
+
[:mp3_album=>ML_string],
|
294
|
+
[:mp3_year=>ML_string],
|
295
|
+
[:mp3_comment=>ML_string],
|
296
|
+
[:mp3_tracknum=>ML_int32],
|
297
|
+
[:mp3_genre=>ML_int32],
|
298
|
+
],
|
299
|
+
4=>[
|
300
|
+
[:stream=>ML_list_of_ogg_stream]
|
301
|
+
]
|
302
|
+
},
|
303
|
+
]]
|
304
|
+
end
|
305
|
+
|
306
|
+
class ML_fileinfo < ML_compound
|
307
|
+
@attributes= [
|
308
|
+
[:id, ML_int32],
|
309
|
+
[:network_id, ML_int32],
|
310
|
+
[:file_names, ML_list_of_string],
|
311
|
+
[:md4, ML_char16],
|
312
|
+
[:file_size,ML_int64],
|
313
|
+
[:downloaded,ML_int64],
|
314
|
+
[:sources,ML_int32],
|
315
|
+
[:clients,ML_int32],
|
316
|
+
[:state,ML_filestate],
|
317
|
+
[:chunks,ML_string],
|
318
|
+
[:availability,ML_list_of_availability],
|
319
|
+
[:speed,ML_float],
|
320
|
+
[:chunks_ages,ML_list_of_int32],
|
321
|
+
[:file_age,ML_int32],
|
322
|
+
[:file_format,ML_fileformat],
|
323
|
+
[:preferred_name,ML_string],
|
324
|
+
[:last_seen,ML_int32],
|
325
|
+
[:priority,ML_uint32],
|
326
|
+
[:comment,ML_string],
|
327
|
+
[:links,ML_list_of_string],
|
328
|
+
[:subfiles,ML_list_of_subfile],
|
329
|
+
[:file_format_im,ML_string],
|
330
|
+
[:comments,ML_list_of_comment],
|
331
|
+
[:file_user,ML_string],
|
332
|
+
[:file_group,ML_string],
|
333
|
+
]
|
334
|
+
end
|
335
|
+
|
336
|
+
class ML_list_of_fileinfo < ML_list
|
337
|
+
@type= ML_fileinfo
|
338
|
+
end
|
339
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# ml_ruby: Ruby Bindings for the ML-Donkey GUI protocol
|
2
|
+
# Copyright (C) 2008 Jens Herrmann
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
require File.dirname(__FILE__) + '/ml_data_types'
|
19
|
+
|
20
|
+
module ML_ruby
|
21
|
+
class ML_payload < ML_compound
|
22
|
+
def self.opcode
|
23
|
+
@opcode
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class ML_message
|
28
|
+
attr_reader :size, :opcode, :payload
|
29
|
+
def initialize opcode=nil, payload=nil
|
30
|
+
if opcode && opcode.is_a?( ML_int16 )
|
31
|
+
@opcode= opcode
|
32
|
+
elsif opcode
|
33
|
+
@opcode= ML_int16.new( opcode )
|
34
|
+
end
|
35
|
+
@payload= payload if payload
|
36
|
+
@size= ML_int32.new( @opcode.size + (payload ? payload.size : 0) ) if opcode
|
37
|
+
end
|
38
|
+
def encode
|
39
|
+
@size.encode + @opcode.encode + @payload.encode
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class ML_S_protocol_version < ML_payload
|
44
|
+
@opcode= 0
|
45
|
+
@attributes= [
|
46
|
+
[:protocol_version,ML_int32]
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
class ML_S_interested_in_sources < ML_payload
|
51
|
+
@opcode= 64
|
52
|
+
@attributes= [
|
53
|
+
[:interested_in_sources,ML_int8]
|
54
|
+
]
|
55
|
+
end
|
56
|
+
|
57
|
+
class ML_S_password < ML_payload
|
58
|
+
@opcode= 52
|
59
|
+
@attributes= [
|
60
|
+
[:password,ML_string],
|
61
|
+
[:login_name,ML_string],
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
class ML_S_get_downloading_files < ML_payload
|
66
|
+
@opcode= 45
|
67
|
+
@attributes= [
|
68
|
+
]
|
69
|
+
end
|
70
|
+
|
71
|
+
class ML_S_get_downloaded_files < ML_payload
|
72
|
+
@opcode= 46
|
73
|
+
@attributes= [
|
74
|
+
]
|
75
|
+
end
|
76
|
+
|
77
|
+
class ML_S_download_url< ML_payload
|
78
|
+
@opcode= 8
|
79
|
+
@attributes= [
|
80
|
+
[:url,ML_string],
|
81
|
+
]
|
82
|
+
end
|
83
|
+
|
84
|
+
class ML_R_core_protocol < ML_payload
|
85
|
+
@opcode= 0
|
86
|
+
@attributes= [
|
87
|
+
[:max_protocol_version,ML_int32],
|
88
|
+
[:max_opcode_sent,ML_int32],
|
89
|
+
[:max_opcode_rcvd,ML_int32],
|
90
|
+
]
|
91
|
+
end
|
92
|
+
|
93
|
+
class ML_R_network_info < ML_payload
|
94
|
+
@opcode= 20
|
95
|
+
@attributes= [
|
96
|
+
[:network_identifier,ML_int32],
|
97
|
+
[:network_name,ML_string],
|
98
|
+
[:enabled,ML_int8],
|
99
|
+
[:config_file,ML_string],
|
100
|
+
[:uploaded,ML_int64],
|
101
|
+
[:downloaded,ML_int64],
|
102
|
+
[:connected_servers,ML_int32],
|
103
|
+
[:network_flags,ML_list_of_int16],
|
104
|
+
]
|
105
|
+
end
|
106
|
+
|
107
|
+
class ML_R_console_message < ML_payload
|
108
|
+
@opcode= 19
|
109
|
+
@attributes= [
|
110
|
+
[:message,ML_string],
|
111
|
+
]
|
112
|
+
end
|
113
|
+
|
114
|
+
class ML_R_bad_password < ML_payload
|
115
|
+
@opcode= 47
|
116
|
+
@attributes= [
|
117
|
+
]
|
118
|
+
end
|
119
|
+
|
120
|
+
class ML_R_fileinfo < ML_payload
|
121
|
+
@opcode= 52
|
122
|
+
@attributes= [
|
123
|
+
[:fileinfo,ML_fileinfo],
|
124
|
+
]
|
125
|
+
end
|
126
|
+
|
127
|
+
class ML_R_downloading_files < ML_payload
|
128
|
+
@opcode= 53
|
129
|
+
@attributes= [
|
130
|
+
[:list,ML_list_of_fileinfo],
|
131
|
+
]
|
132
|
+
end
|
133
|
+
|
134
|
+
class ML_R_downloaded_files < ML_payload
|
135
|
+
@opcode= 54
|
136
|
+
@attributes= [
|
137
|
+
[:list,ML_list_of_fileinfo],
|
138
|
+
]
|
139
|
+
end
|
140
|
+
|
141
|
+
class ML_R_unknown < ML_payload
|
142
|
+
@attributes= [
|
143
|
+
[:raw_data,ML_string],
|
144
|
+
]
|
145
|
+
end
|
146
|
+
|
147
|
+
class ML_payload_handler
|
148
|
+
Types={
|
149
|
+
0 => ML_R_core_protocol,
|
150
|
+
19 => ML_R_console_message,
|
151
|
+
20 => ML_R_network_info,
|
152
|
+
47 => ML_R_bad_password,
|
153
|
+
52 => ML_R_fileinfo,
|
154
|
+
53 => ML_R_downloading_files,
|
155
|
+
54 => ML_R_downloaded_files,
|
156
|
+
}
|
157
|
+
def self.for opcode
|
158
|
+
Types[opcode]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/rakefile.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc "Run the tests"
|
9
|
+
Rake::TestTask::new do |t|
|
10
|
+
t.test_files = FileList['test/test*.rb']
|
11
|
+
t.verbose = true
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Generate the documentation"
|
15
|
+
Rake::RDocTask::new do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'ml-ruby-doc/'
|
17
|
+
rdoc.title = "ML-Ruby Documentation"
|
18
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
19
|
+
rdoc.rdoc_files.include('README')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
spec = Gem::Specification::new do |s|
|
24
|
+
s.platform = Gem::Platform::RUBY
|
25
|
+
|
26
|
+
s.name = 'ml-ruby'
|
27
|
+
s.version = "0.0.1"
|
28
|
+
s.summary = "ML Ruby are the ruby bindings for the open source peer 2 peer software MLDonkey. This is the first part of an alternative to the official web GUI written in Ruby On Rails."
|
29
|
+
s.description = <<EOF
|
30
|
+
EOF
|
31
|
+
s.author = 'Jens Herrmann'
|
32
|
+
s.email = 'interesse@rubyforge.org'
|
33
|
+
s.homepage = "http://rubyforge.org/projects/ml-ruby/"
|
34
|
+
|
35
|
+
s.requirements << 'none'
|
36
|
+
s.require_path = 'lib'
|
37
|
+
s.files = FileList["lib/**/*.rb","test/**/*.rb", "README","rakefile.rb"]
|
38
|
+
s.test_files = FileList['test/test*.rb']
|
39
|
+
|
40
|
+
s.has_rdoc = true
|
41
|
+
s.extra_rdoc_files = ["README"]
|
42
|
+
s.rdoc_options.concat ['--main', 'README']
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Package the library as a gem"
|
46
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
47
|
+
pkg.need_zip = true
|
48
|
+
pkg.need_tar = true
|
49
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'ml-ruby'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
include ML_ruby
|
7
|
+
|
8
|
+
class TestDatatypes <Test::Unit::TestCase
|
9
|
+
|
10
|
+
def test_atom_inspect
|
11
|
+
assert_equal "ML_ruby::ML_atom: 0", ML_atom.new(0).inspect
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_int64
|
15
|
+
assert_equal 0, ML_int64.decode( ML_int64.new(0).encode ).payload
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_simple
|
19
|
+
[ML_int64,ML_int32,ML_uint32,ML_int16,ML_int8,].each do |type|
|
20
|
+
assert_equal 0, type.decode( type.new(0).encode ).payload
|
21
|
+
end
|
22
|
+
assert_equal "T", ML_char.decode( ML_char.new("T").encode ).payload
|
23
|
+
assert_equal "0123456789abcdef", ML_char16.decode( ML_char16.new("0123456789abcdef").encode ).payload
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_string
|
27
|
+
assert_equal "Test", ML_string.decode( ML_string.new("Test").encode ).payload
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_float
|
31
|
+
assert_equal 0.0, ML_float.decode( ML_float.new(0.0).encode ).payload
|
32
|
+
assert_equal 0.1, ML_float.decode( ML_float.new(0.1).encode ).payload
|
33
|
+
assert_equal 0.01, ML_float.decode( ML_float.new(0.01).encode ).payload
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_list_of
|
37
|
+
assert_equal [ML_int16.new(0),ML_int16.new(1)], ML_list_of_int16.decode( ML_list_of_int16.new([ML_int16.new(0),ML_int16.new(1)]).encode ).payload
|
38
|
+
assert_equal [ML_int32.new(0),ML_int32.new(1)], ML_list_of_int32.decode( ML_list_of_int32.new([ML_int32.new(0),ML_int32.new(1)]).encode ).payload
|
39
|
+
assert_equal [ML_string.new('null'),ML_string.new('eins')], ML_list_of_string.decode( ML_list_of_string.new([ML_string.new('null'),ML_string.new('eins')]).encode ).payload
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ml-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jens Herrmann
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-02-20 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: ""
|
17
|
+
email: interesse@rubyforge.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- lib/ml-ruby/ml_client.rb
|
26
|
+
- lib/ml-ruby/ml_data_types.rb
|
27
|
+
- lib/ml-ruby/ml_protocol.rb
|
28
|
+
- lib/ml-ruby.rb
|
29
|
+
- test/test_datatypes.rb
|
30
|
+
- README
|
31
|
+
- rakefile.rb
|
32
|
+
has_rdoc: true
|
33
|
+
homepage: http://rubyforge.org/projects/ml-ruby/
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options:
|
36
|
+
- --main
|
37
|
+
- README
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
version:
|
52
|
+
requirements:
|
53
|
+
- none
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 1.0.1
|
56
|
+
signing_key:
|
57
|
+
specification_version: 2
|
58
|
+
summary: ML Ruby are the ruby bindings for the open source peer 2 peer software MLDonkey. This is the first part of an alternative to the official web GUI written in Ruby On Rails.
|
59
|
+
test_files:
|
60
|
+
- test/test_datatypes.rb
|