ml-ruby 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|