ruby-fcp 0.0.5
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.
- checksums.yaml +7 -0
- data/bin/fget +48 -0
- data/bin/fput +62 -0
- data/lib/ruby-fcp/communicator.rb +133 -0
- data/lib/ruby-fcp/fcp_client.rb +290 -0
- data/lib/ruby-fcp/utils.rb +18 -0
- data/lib/ruby-fcp.rb +8 -0
- metadata +52 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8ba6b44aeaf59819305d4f97364ec8693595a876
|
4
|
+
data.tar.gz: 7bac921fc48e3ef3a2668b968b32c053a4ce0346
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 573214e4b8d49f2438c2c5a327a4dd1fcd4f750647f98b4aab55a272a5724be12aa75a68c5bef14c60f867731e25a3ed74476f59774b5ca8bde246aa8bd14f6b
|
7
|
+
data.tar.gz: 64790ef14cc9a85ad2519ba7b68a53a91c0151a75b106a25e8b4d6e6335e397143d5d050d878026c31874bbc6416a7bca1ce6651ff57635816f135a7313d0e49
|
data/bin/fget
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'ruby-fcp'
|
6
|
+
|
7
|
+
options = {}
|
8
|
+
|
9
|
+
OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: fput.rb [options]"
|
11
|
+
|
12
|
+
opts.on("-u", "--uri URI",
|
13
|
+
"CHK@, USK@,SSK@") do |u|
|
14
|
+
options[:uri] = u
|
15
|
+
end
|
16
|
+
|
17
|
+
opts.on("-p", "--path path",
|
18
|
+
"if this is directory make sure you specified -d option") do |p|
|
19
|
+
options[:path] = p
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on("-s", "--server host:port",
|
23
|
+
"Must be a freenet client protocol server") do |s|
|
24
|
+
options[:server] = s
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
28
|
+
puts opts
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
end.parse!
|
32
|
+
|
33
|
+
if options.has_key? :uri and options.has_key? :path
|
34
|
+
|
35
|
+
if options.has_key? :server
|
36
|
+
server = options[:server].chomp.lstrip.split(':')
|
37
|
+
client = FCPClient.new("fput-#{SecureRandom.hex}",server[0],server[1])
|
38
|
+
else
|
39
|
+
client = FCPClient.new("fput-#{SecureRandom.hex}")
|
40
|
+
end
|
41
|
+
|
42
|
+
client.simple_get(options[:uri].chomp.lstrip,options[:path].chomp.chomp('/').lstrip)
|
43
|
+
client.close
|
44
|
+
|
45
|
+
else
|
46
|
+
puts "-u and -p are mandatory type -h or --help for usage information"
|
47
|
+
exit
|
48
|
+
end
|
data/bin/fput
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'pathname'
|
6
|
+
require 'ruby-fcp/fcp_client'
|
7
|
+
|
8
|
+
options = { index: 'index.html' }
|
9
|
+
|
10
|
+
opts = OptionParser.new do |opts|
|
11
|
+
opts.banner = "Usage: #{$0} [options]"
|
12
|
+
|
13
|
+
opts.on("-u", "--uri URI",
|
14
|
+
"CHK@, USK@(requires insert uri),SSK@(requires inserturi)") do |u|
|
15
|
+
options[:uri] = u
|
16
|
+
end
|
17
|
+
opts.on("-p", "--path path",
|
18
|
+
"if this is directory make sure you specified -d option") do |p|
|
19
|
+
options[:path] = p
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on("-i", "--index defaultfile",
|
23
|
+
"default file for directory uploads, I believe it defaults to index.html") do |i|
|
24
|
+
options[:index] = i
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on("-s", "--server host:port",
|
28
|
+
"Must be a freenet client protocol server") do |s|
|
29
|
+
options[:server] = s
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on("-d", "--directory", "You are uploading a directory") do |d|
|
33
|
+
options[:directory] = d
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
37
|
+
puts opts
|
38
|
+
exit
|
39
|
+
end
|
40
|
+
|
41
|
+
end.parse!
|
42
|
+
|
43
|
+
if options.has_key? :uri and options.has_key? :path
|
44
|
+
|
45
|
+
if options.has_key? :server
|
46
|
+
server = options[:server].chomp.lstrip.split(':')
|
47
|
+
client = FCPClient.new("fput-#{SecureRandom.hex}",server[0],server[1])
|
48
|
+
else
|
49
|
+
client = FCPClient.new("fput-#{SecureRandom.hex}")
|
50
|
+
end
|
51
|
+
|
52
|
+
if options[:directory]
|
53
|
+
client.simple_dir_put(options[:uri].chomp.lstrip,options[:path].chomp.chomp('/').lstrip,true,{"DefaultName" => options[:index]})
|
54
|
+
else
|
55
|
+
client.simple_put(options[:uri].chomp.lstrip,options[:path].chomp.lstrip)
|
56
|
+
end
|
57
|
+
client.close
|
58
|
+
|
59
|
+
else
|
60
|
+
puts "-u and -p are mandatory type -h or --help for usage information"
|
61
|
+
exit
|
62
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'thread'
|
3
|
+
require 'ruby-fcp/utils'
|
4
|
+
|
5
|
+
class Communicator
|
6
|
+
attr_reader :ConnectionIdentifier
|
7
|
+
attr_accessor :responses, :heartbeat
|
8
|
+
|
9
|
+
def initialize(client,host, port, version = 2.0)
|
10
|
+
@utils = Utils.new
|
11
|
+
@version = version
|
12
|
+
@ConnectionIdentifier = ""
|
13
|
+
@host = host
|
14
|
+
@port = port
|
15
|
+
@client = client
|
16
|
+
@responses = { peers: [],dda: [], default: [], error: [], datalengths: [] }
|
17
|
+
@tex = Mutex.new
|
18
|
+
@state = false
|
19
|
+
@queue = Queue.new
|
20
|
+
@heartbeat = 300
|
21
|
+
connect
|
22
|
+
end
|
23
|
+
|
24
|
+
def connect
|
25
|
+
@sock = TCPSocket.new @host ,@port
|
26
|
+
@sock.write @utils.packet_mangler({"Name" => @client,"ExpectedVersion" => @version},"ClientHello")
|
27
|
+
response = grab_response
|
28
|
+
unless response[:state] == -1
|
29
|
+
@sock_thrd = Thread.new {sock_thrd}
|
30
|
+
@ConnectionIdentifier = response["ConnectionIdentifier"]
|
31
|
+
@state = true
|
32
|
+
keep_alive
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def sock_thrd
|
37
|
+
@threads = []
|
38
|
+
loop do
|
39
|
+
@threads.each do |thrd|
|
40
|
+
begin
|
41
|
+
@threads.delete thrd if thrd.join(0.5)
|
42
|
+
rescue RequestFinished => req
|
43
|
+
@responses[:error].push req
|
44
|
+
rescue Exception => excpt
|
45
|
+
puts "#{excpt}"
|
46
|
+
@threads.delete thrd
|
47
|
+
end
|
48
|
+
if thrd.status == false
|
49
|
+
@threads.delete thrd
|
50
|
+
elsif thrd.status == nil
|
51
|
+
@threads.delete thrd
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
@sock.close Thread.exit if @state == false
|
56
|
+
|
57
|
+
begin
|
58
|
+
while packet = @queue.pop(true)
|
59
|
+
@tex.synchronize{@sock.write packet}
|
60
|
+
#sort_out(packet)
|
61
|
+
end
|
62
|
+
rescue ThreadError => err
|
63
|
+
end
|
64
|
+
|
65
|
+
begin
|
66
|
+
if select([@sock], nil,nil,2)
|
67
|
+
packet = @tex.synchronize{grab_response}
|
68
|
+
sort_out(packet)
|
69
|
+
end
|
70
|
+
rescue
|
71
|
+
@sock.close Thread.exit
|
72
|
+
state = false
|
73
|
+
connect
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def sort_out(packet)
|
79
|
+
if packet[:head].include? "NodeHello"
|
80
|
+
@ConnectionIdentifier = packet["ConnectionIdentifier"]
|
81
|
+
elsif packet[:head].include? "CloseConnectionDuplicateClientName"
|
82
|
+
@state = false
|
83
|
+
elsif packet.has_key? "Identifier"
|
84
|
+
if @responses.has_key? packet["Identifier"]
|
85
|
+
@responses[packet["Identifier"]].push packet
|
86
|
+
else
|
87
|
+
@responses[packet["Identifier"]] = [packet]
|
88
|
+
end
|
89
|
+
elsif packet[:head].include? "DDA"
|
90
|
+
@responses[:dda].push packet
|
91
|
+
elsif packet[:state] == -1
|
92
|
+
@responses[:error].push packet
|
93
|
+
elsif packet[:head].include? "Peer"
|
94
|
+
@responses[:peers].push packet
|
95
|
+
else packet
|
96
|
+
@responses[:default].push packet
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def send_packet(message)
|
101
|
+
@queue.push message
|
102
|
+
end
|
103
|
+
|
104
|
+
def grab_response
|
105
|
+
response = { state: 1 }
|
106
|
+
line = @sock.readline
|
107
|
+
response[:state] = -1 if line =~ /ClosedConnectionDuplicateClientName|ProtocolError/
|
108
|
+
response[:head] = line.chomp
|
109
|
+
until line =~ /EndMessage|^Data$/
|
110
|
+
response[line.split('=')[0]] = line.split('=')[1].chomp if line.split('=').size == 2
|
111
|
+
line = @sock.readline
|
112
|
+
end
|
113
|
+
@responses[:datalengths] << response["DataLength"].to_i if response.has_key? "DataLength"
|
114
|
+
response[:data] = @sock.read @responses[:datalengths].pop if response[:head] =~ /AllData/
|
115
|
+
response
|
116
|
+
end
|
117
|
+
|
118
|
+
def keep_alive
|
119
|
+
Thread.start do
|
120
|
+
loop do
|
121
|
+
send_packet "Void\nEndMessage\n"
|
122
|
+
sleep @heartbeat
|
123
|
+
break if @state == false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def close
|
129
|
+
@tex.synchronize{@sock.write "Disconnect EndMessage\n"}
|
130
|
+
@sock.close
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
@@ -0,0 +1,290 @@
|
|
1
|
+
# The simple interface to FCP from ruby
|
2
|
+
# Implements raw fcp packets with sane defaults and automates some task
|
3
|
+
|
4
|
+
require 'digest'
|
5
|
+
require 'base64'
|
6
|
+
require 'ruby-fcp/communicator'
|
7
|
+
|
8
|
+
class FCPClient
|
9
|
+
|
10
|
+
attr_accessor :utils, :com
|
11
|
+
|
12
|
+
# clients name must be unique
|
13
|
+
# This performs NodeHello operations upon initialization.
|
14
|
+
# Communicator handles packet sending and recieving and sorting
|
15
|
+
def initialize(client, host = "127.0.0.1", port = 9481)
|
16
|
+
@utils = Utils.new
|
17
|
+
@com = Communicator.new(client,host,port)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Simple attribute reader for you ConnectionIdentifier
|
21
|
+
def identifier
|
22
|
+
@com.ConnectionIdentifier
|
23
|
+
end
|
24
|
+
|
25
|
+
# Simple attribute reader to display all responses recieved
|
26
|
+
def responses
|
27
|
+
@com.responses
|
28
|
+
end
|
29
|
+
|
30
|
+
# return last response from request defined by Identifier
|
31
|
+
def last_response(id)
|
32
|
+
@com.responses[id].last
|
33
|
+
end
|
34
|
+
|
35
|
+
# Simple interface to put a single file onto freenet from your disk uses ClientPut message
|
36
|
+
# ==Possible values of uri:
|
37
|
+
# * CHK@ will generate and return the key your data is acessible at
|
38
|
+
# * SSK@ must have insert key provided by GenerateSSK or the method new_ssk_pair
|
39
|
+
# * KSK@filename
|
40
|
+
def simple_put(uri, filename, wait = true, opts = {})
|
41
|
+
id = @utils.id_generate
|
42
|
+
options = { "URI" => uri, "Identifier" => id, "UploadFrom" => 'disk', "Filename" => filename, "FileHash" => @utils.filehash_maker(id, filename,identifier) }.merge(opts)
|
43
|
+
@com.send_packet @utils.packet_mangler(options,"ClientPut")
|
44
|
+
#@com.fcpackets.client_put uri, id, options
|
45
|
+
if wait
|
46
|
+
wait_for id, /PutFailed|PutSuccessful/
|
47
|
+
else
|
48
|
+
id
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Another interface to ClientPut that allows you to directly put data pnto freenet
|
53
|
+
# data is your data
|
54
|
+
# ==Possible values of uri:
|
55
|
+
# * CHK@ will generate and return the key your data is acessible at
|
56
|
+
# * SSK@ must have insert key provided by GenerateSSK or the method new_ssk_pair
|
57
|
+
# * KSK@filename
|
58
|
+
def direct_put(uri,data, wait = true, opts = {})
|
59
|
+
id = @utils.id_generate
|
60
|
+
options = {"Identifier" => id, "URI" => uri ,"UploadFrom" => "direct", "DataLength" => data.bytesize }.merge(opts)
|
61
|
+
@com.send_packet @utils.packet_mangler(options,"ClientPut").sub! "EndMessage\n", "Data\n#{data}"
|
62
|
+
if wait
|
63
|
+
wait_for id,/PutFailed|PutSuccessful/
|
64
|
+
else
|
65
|
+
id
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Simple directory upload, upload one directory all at once
|
70
|
+
# automates the TestDDA for you just provide uri and directory
|
71
|
+
# Implements ClientPutDiskDir
|
72
|
+
# * CHK@ will generate and return the key your data is acessible at
|
73
|
+
# * SSK@ must have insert key provided by GenerateSSK or the method new_ssk_pair
|
74
|
+
# * KSK@filename
|
75
|
+
def simple_dir_put(uri, dir, wait = true, opts={})
|
76
|
+
id = @utils.id_generate
|
77
|
+
ddarun(dir,true,false)
|
78
|
+
options = {"Identifier" => id,"URI" => uri,"Filename" => dir, "Global" => 'true', "AllowUnreadableFiles" => 'true', "IncludeHiddenValues" => 'false'}.merge(opts)
|
79
|
+
@com.send_packet @utils.packet_mangler(options,"ClientPutDiskDir")
|
80
|
+
if wait
|
81
|
+
wait_for(id,/PutFailed|PutSuccessful/)
|
82
|
+
else
|
83
|
+
id
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# simpler staight forward interface to ClientPutComplexDir, you provide with
|
88
|
+
# ==Possible values of uri:
|
89
|
+
# * CHK@ will generate and return the key your data is acessible at
|
90
|
+
# * SSK@ must have insert key provided by GenerateSSK or the method new_ssk_pair
|
91
|
+
# * KSK@filename
|
92
|
+
# As well as a list of hashes for files you want to put
|
93
|
+
# ==File hashlist format:
|
94
|
+
# * name: lib/hello or index.html, / will interperate as directory nesting
|
95
|
+
# * filename: in case of disk location of file on disk
|
96
|
+
# * uploadfrom: 'direct', 'disk' or 'redirect'
|
97
|
+
# * targeturi: in case of redirect, the location your are redirecting to
|
98
|
+
# * mimetype: not needed, but can be helpful
|
99
|
+
# * data: only nessecery in direct mode
|
100
|
+
def put_complex_dir(uri, files, wait = true,opts = {})
|
101
|
+
dirs = []
|
102
|
+
id = @utils.id_generate
|
103
|
+
files.each{ |f| dirs << f[:filename].split('/')[0...-1].join('/') + '/' if f.has_key? :filename }
|
104
|
+
(dirs.uniq).each { |dir| ddarun(dir,true,false) }
|
105
|
+
options = {"URI" => uri, "Identifier" => id}.merge(opts)
|
106
|
+
files.each_with_index do |file, index|
|
107
|
+
options["Files.#{index}.Name"] = file[:name]
|
108
|
+
options["Files.#{index}.UploadFrom"] = file[:uploadfrom]
|
109
|
+
options["Files.#{index}.DataLength"] = file[:data].bytesize if file.has_key? :data
|
110
|
+
options["Files.#{index}.Filename"] = file[:filename] if file[:uploadfrom].include? 'disk'
|
111
|
+
options["Files.#{index}.TargetURI"] = file[:targeturi] if file[:uploadfrom].include? 'redirect'
|
112
|
+
options["Files.#{index}.Metadata.ContentType"] = file[:mimetype] if file.has_key? :mimetype
|
113
|
+
end
|
114
|
+
message = @utils.packet_mangler(options,"ClientPutComplexDir")
|
115
|
+
files.each { |f| message << f[:data] if f.has_key? :data}
|
116
|
+
puts message
|
117
|
+
@com.send_packet message
|
118
|
+
if wait
|
119
|
+
wait_for(id,/PutFailed|PutSuccessful/)
|
120
|
+
else
|
121
|
+
id
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# performs TestDDARequest and TestDDAResponse automagically
|
126
|
+
# read and write are true or false values
|
127
|
+
def ddarun(directory,read, write)
|
128
|
+
@com.send_packet @utils.packet_mangler({"Directory" => directory,"WantReadDirectory" => read, "WantWriteDirectory" => write} ,"TestDDARequest")
|
129
|
+
res = wait_for(:dda, /TestDDAReply/).pop
|
130
|
+
content = nil
|
131
|
+
if write
|
132
|
+
f = File.open(res["WriteFilename"],'w+')
|
133
|
+
f.write res["ContentToWrite"]
|
134
|
+
f.close
|
135
|
+
elsif read
|
136
|
+
content = File.open(res["ReadFilename"],'r').read
|
137
|
+
end
|
138
|
+
@com.send_packet @utils.packet_mangler({"Directory" => directory,"ReadContent" => content}, "TestDDAResponse")
|
139
|
+
response = wait_for(:dda ,/TestDDAComplete/).pop
|
140
|
+
File.delete(res["WriteFilename"]) if write
|
141
|
+
response
|
142
|
+
end
|
143
|
+
|
144
|
+
# just provide uri and download path/directory
|
145
|
+
# Implements ClientGet
|
146
|
+
def simple_get(uri,directory,wait = true, opts={})
|
147
|
+
id = @utils.id_generate
|
148
|
+
saveloc = File.join directory, uri.split('/')[-1]
|
149
|
+
ddarun(directory,false, true)
|
150
|
+
options = {"URI" => uri, "Identifier" => id, "ReturnType" => 'disk', "Filename" => saveloc, "TempFilename" => saveloc+".tmp" , "Persistence" => 'forever', "Global" => false, "Verbosity" => 1111111}.merge(opts)
|
151
|
+
@com.send_packet @utils.packet_mangler(options,"ClientGet")
|
152
|
+
if wait
|
153
|
+
wait_for(id,/GetFailed|DataFound/)
|
154
|
+
else
|
155
|
+
id
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def direct_get(uri ,wait = true, opts={})
|
160
|
+
id = @utils.id_generate
|
161
|
+
options = {"URI" => uri, "Identifier" => id, "ReturnType" => 'direct', "Global" => false}.merge(opts)
|
162
|
+
@com.send_packet @utils.packet_mangler(options,"ClientGet")
|
163
|
+
if wait
|
164
|
+
wait_for(id,/AllData|GetFailed/)
|
165
|
+
else
|
166
|
+
id
|
167
|
+
end
|
168
|
+
end
|
169
|
+
# returns information on plugin, must be full class name as listed in freenet interface
|
170
|
+
# Implements GetPluginInfo
|
171
|
+
def get_plugin_info(pluginname, detailed = false)
|
172
|
+
id = @utils.id_generate
|
173
|
+
@com.send_packet @utils.packet_mangler({"PluginName" => pluginname, "Identifier" => id, "Detailed" => detailed },"GetPluginInfo")
|
174
|
+
wait_for id, /PluginInfo/
|
175
|
+
end
|
176
|
+
|
177
|
+
# Straigt forward, ListPeers, sometimes it may choke up and give you end list peers before your peers, in that case check the id
|
178
|
+
def listpeers
|
179
|
+
id = @utils.id_generate
|
180
|
+
@com.send_packet @utils.packet_mangler({"Identifier" => id, "WithMetaData" => true, "WithVolatile" => false},"ListPeers")
|
181
|
+
wait_for id, /EndListPeers/
|
182
|
+
end
|
183
|
+
|
184
|
+
#Uses GenerateSSK
|
185
|
+
def new_ssk_pair
|
186
|
+
id = @utils.id_generate
|
187
|
+
@com.send_packet @utils.packet_mangler({"Identifier" => id}, "GenerateSSK")
|
188
|
+
wait_for id, /SSKKeypair/
|
189
|
+
end
|
190
|
+
|
191
|
+
# returns information on a given peer not peers implements ListPeer
|
192
|
+
def peerinfo(peer)
|
193
|
+
@com.send_packet @utils.packet_mangler({"NodeIdentifier" => peer,"WithVolatile" => false,"WithMetadata" => true}, "ListPeer")
|
194
|
+
wait_for :peer, /Peer/
|
195
|
+
end
|
196
|
+
|
197
|
+
# List all persistent request just implements ListPersistentRequest
|
198
|
+
def list_persistent_requests
|
199
|
+
@com.send_packet "ListPersistentRequests\nEndMessage\n"
|
200
|
+
wait_for :default ,/EndListPersistentRequests/
|
201
|
+
end
|
202
|
+
|
203
|
+
def modify_persistent_request(id,clienttoken,priorityclass)
|
204
|
+
@com.send_packet @utils.packet_mangler({"Identifier" => id,"ClientToken" => clienttoken, "PriorityClass" => priorityclass}, "ModifyPersistentRequest")
|
205
|
+
wait_for id, /PersistentRequestModified/
|
206
|
+
end
|
207
|
+
|
208
|
+
#subscirbe to a usk, have to poll it yourself by using responses[id]
|
209
|
+
def subscribe_usk(uri, wait=false ,opts ={})
|
210
|
+
id = @utils.id_generate
|
211
|
+
@com.send_packet @utils.packet_mangler({"URI" => uri, "Identifier" => id} ,"SubscribeUSK")
|
212
|
+
id
|
213
|
+
end
|
214
|
+
|
215
|
+
def unsubscribe_usk(id)
|
216
|
+
@com.send_packet "UnsubscribeUSK\nIdentifier=#{id}\nEndMessage\n"
|
217
|
+
id
|
218
|
+
end
|
219
|
+
|
220
|
+
def proberequest(type,hopstolive=25,wait = true)
|
221
|
+
id = @utils.id_generate
|
222
|
+
@com.send_packet @utils.packet_mangler({"Identifier" => id,"Type" => type,"HopsToLive" => hopstolive}, "ProbeRequest")
|
223
|
+
if wait
|
224
|
+
wait_for id,/Probe/
|
225
|
+
else
|
226
|
+
id
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Waits for a specific pattern in a message identified by ID
|
231
|
+
def wait_for(id, pattern)
|
232
|
+
response = [ ]
|
233
|
+
loop do
|
234
|
+
begin
|
235
|
+
x = @com.responses[id].pop
|
236
|
+
print @com.responses[:error].pop
|
237
|
+
rescue
|
238
|
+
sleep(2)
|
239
|
+
end
|
240
|
+
unless x.nil?
|
241
|
+
if x[:head] =~ pattern
|
242
|
+
response << x
|
243
|
+
x.each { |key, value| puts "#{key}=#{value}" }
|
244
|
+
break
|
245
|
+
elsif x[:head] =~ /ProtocolError/
|
246
|
+
response << x
|
247
|
+
x.each { |key, value| puts "#{key}=#{value}" }
|
248
|
+
break
|
249
|
+
else
|
250
|
+
response << x
|
251
|
+
x.each { |key, value| puts "#{key}=#{value}" }
|
252
|
+
end
|
253
|
+
else
|
254
|
+
sleep(1)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
response
|
258
|
+
end
|
259
|
+
|
260
|
+
# Just wait and wait given a id
|
261
|
+
def wait_for_ever(id)
|
262
|
+
loop do
|
263
|
+
begin
|
264
|
+
x = @com.responses[id].pop
|
265
|
+
rescue
|
266
|
+
print '.'
|
267
|
+
sleep(2)
|
268
|
+
print @com.responses[:error]
|
269
|
+
print @com.responses[:default]
|
270
|
+
end
|
271
|
+
unless x.nil?
|
272
|
+
x.each { |key, value| puts "#{key}=#{value}" }
|
273
|
+
else
|
274
|
+
print '.'
|
275
|
+
sleep(1)
|
276
|
+
puts @com.responses[:error]
|
277
|
+
print @com.responses[:default]
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# Send disconnect message and close the socket
|
283
|
+
def close
|
284
|
+
@com.close
|
285
|
+
end
|
286
|
+
|
287
|
+
def killswitch
|
288
|
+
@com.send_packet "Shutdown\nEndMessage\n"
|
289
|
+
end
|
290
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'digest'
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
class Utils
|
6
|
+
def filehash_maker(ident,filename,conid)
|
7
|
+
content = File.read(filename)
|
8
|
+
(Digest::SHA256.new << conid + "-#{ident}-" + content).base64digest
|
9
|
+
end
|
10
|
+
|
11
|
+
def packet_mangler(sash,header)
|
12
|
+
header +"\n"+ sash.map{|k,v| "#{k}=#{v}"}.join("\n") + "\nEndMessage\n"
|
13
|
+
end
|
14
|
+
|
15
|
+
def id_generate
|
16
|
+
SecureRandom.hex
|
17
|
+
end
|
18
|
+
end
|
data/lib/ruby-fcp.rb
ADDED
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-fcp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- hikiko
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-31 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A gem interface for Freenet Client Protocol
|
14
|
+
email: kerben@i2pmail.org
|
15
|
+
executables:
|
16
|
+
- fget
|
17
|
+
- fput
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- bin/fget
|
22
|
+
- bin/fput
|
23
|
+
- lib/ruby-fcp.rb
|
24
|
+
- lib/ruby-fcp/communicator.rb
|
25
|
+
- lib/ruby-fcp/fcp_client.rb
|
26
|
+
- lib/ruby-fcp/utils.rb
|
27
|
+
homepage: https://github.com/kerben/ruby-fcp
|
28
|
+
licenses:
|
29
|
+
- Unlicense
|
30
|
+
metadata: {}
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 2.4.2
|
48
|
+
signing_key:
|
49
|
+
specification_version: 4
|
50
|
+
summary: FCPClient
|
51
|
+
test_files: []
|
52
|
+
has_rdoc:
|