dlc 1.1.0
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.rdoc +5 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/dlc_settings.yml +8 -0
- data/lib/dlc.rb +317 -0
- metadata +84 -0
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "dlc"
|
8
|
+
gem.description = "Allows the generation of DLC container files (of JDownloader fame) from ruby"
|
9
|
+
gem.summary = "Allows the generation of DLC container files (of JDownloader fame) from ruby"
|
10
|
+
gem.email = "jphastings@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/jphastings/ruby-DLC"
|
12
|
+
gem.authors = ["JP Hastings-Spital"]
|
13
|
+
gem.add_dependency "builder"
|
14
|
+
end
|
15
|
+
Jeweler::GemcutterTasks.new
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rake/testtask'
|
21
|
+
Rake::TestTask.new(:test) do |test|
|
22
|
+
test.libs << 'lib' << 'test'
|
23
|
+
test.pattern = 'test/**/test_*.rb'
|
24
|
+
test.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
begin
|
28
|
+
require 'rcov/rcovtask'
|
29
|
+
Rcov::RcovTask.new do |test|
|
30
|
+
test.libs << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
rescue LoadError
|
35
|
+
task :rcov do
|
36
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
task :test => :check_dependencies
|
41
|
+
|
42
|
+
task :default => :test
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.1.0
|
data/dlc_settings.yml
ADDED
data/lib/dlc.rb
ADDED
@@ -0,0 +1,317 @@
|
|
1
|
+
# = The DLC API in ruby!
|
2
|
+
# Visit http://jdownloader.org for more information on why this might be useful!
|
3
|
+
#
|
4
|
+
# My thanks go out to the JDownloader staff for making such an excellent application! I've spoken with them and this script is now available for download at the github page (http://github.com/jphastings/ruby-DLC) or you can install the gem with the usual:
|
5
|
+
# gem install dlc
|
6
|
+
#
|
7
|
+
# == How to Use
|
8
|
+
# You can use this library from the irb, or in any script you like. Examples below are for irb, I'm sure people using this as a
|
9
|
+
# library will be able to figure out how to use it in that way.
|
10
|
+
#
|
11
|
+
# === Set Up
|
12
|
+
# You'll need to set the settings file before you do anything else. Open up irb in the directory where this file is located:
|
13
|
+
# require 'dlc'
|
14
|
+
# # => true
|
15
|
+
# s = DLC::Settings.new
|
16
|
+
# # No settings file exists.
|
17
|
+
# s.name = "Your Name"
|
18
|
+
# s.url = "http://yourdomain.com"
|
19
|
+
# s.email = "your.name@yourdomain.com"
|
20
|
+
#
|
21
|
+
# Now your settings file has been created you can go about making DLCs!
|
22
|
+
#
|
23
|
+
# === Creating a Package and DLC
|
24
|
+
# The following irb example shows the variety of options you have while creating a package
|
25
|
+
# pkg = DLC::Package.new
|
26
|
+
# # => Unnamed package (0 links, 0 passwords)
|
27
|
+
# pkg.name = "My Package"
|
28
|
+
# # => "My Package"
|
29
|
+
# pkg.comment = "An exciting package!"
|
30
|
+
# # => "An exciting package!"
|
31
|
+
# pkg.category = "Nothing useful"
|
32
|
+
# # => "Nothing useful"
|
33
|
+
# pkg.add_link("http://google.com/")
|
34
|
+
# # => true
|
35
|
+
# pkg.add_link(["http://bbc.co.uk","http://slashdot.org"])
|
36
|
+
# # => true
|
37
|
+
# pkg.add_password("I don't really need one of these")
|
38
|
+
# # => true
|
39
|
+
# pkg
|
40
|
+
# # "My Package" (3 links, 1 passwords)
|
41
|
+
# # # An exciting package!
|
42
|
+
#
|
43
|
+
# If you want to put the DLC data into a file you should do:
|
44
|
+
# open("my_dlc.dlc","w") do |f|
|
45
|
+
# f.write pkg.dlc
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# This will ensure the file gets closed after you've written your DLC to it.
|
49
|
+
#
|
50
|
+
# == Problems?
|
51
|
+
# Found a bug? Leave me an issue report on the githib page: http://github.com/jphastings/ruby-DLC/issues -
|
52
|
+
# I'll get onto it as soon as I can and see if I can fix it.
|
53
|
+
#
|
54
|
+
# == More Information
|
55
|
+
# I'm JP, you can find my things at http://byJP.me. Any questions can be sent to me via twitter: @jphastings.
|
56
|
+
#
|
57
|
+
# I did this entirely for fun, please take that into account if/when you ask for help or before you get in touch. If you have any code improvements
|
58
|
+
# please do let me know! I hope you enjoy this!
|
59
|
+
|
60
|
+
require 'yaml'
|
61
|
+
require 'time'
|
62
|
+
require 'net/http'
|
63
|
+
require 'digest/md5'
|
64
|
+
|
65
|
+
require 'rubygems'
|
66
|
+
require 'builder'
|
67
|
+
require 'ruby-aes'
|
68
|
+
|
69
|
+
# A hack to make the AES module accept string keys when they look like hex!
|
70
|
+
def Aes.check_iv(iv)
|
71
|
+
return iv
|
72
|
+
end
|
73
|
+
|
74
|
+
# The DLC module, this is the container for the Settings and Package classes. It also contains some private helper functions
|
75
|
+
module DLC
|
76
|
+
Api = {
|
77
|
+
:version => 1.0,
|
78
|
+
:pair_expires_after => 3600,
|
79
|
+
:service_urls => ["http://service.jdownloader.org/dlcrypt/service.php"],
|
80
|
+
}
|
81
|
+
|
82
|
+
# Settings is a class that deals with information about the group using the DLC api.
|
83
|
+
#
|
84
|
+
# The class contains code to write its own settings file. Before using this ruby DLC api
|
85
|
+
# you should require it in irb and set the details:
|
86
|
+
# require 'dlc'
|
87
|
+
# s = DLC::Settings.new
|
88
|
+
# s.name = "Your name"
|
89
|
+
# s.email = "you@yourdomain.com"
|
90
|
+
# s.url = "http://yourdomain.com/why_i_use_dlc.html"
|
91
|
+
class Settings
|
92
|
+
attr_accessor :email,:name,:url
|
93
|
+
|
94
|
+
# I may allow this to be changed in later versions
|
95
|
+
Settings = "dlc_settings.yml"
|
96
|
+
|
97
|
+
def initialize
|
98
|
+
if File.exists? Settings
|
99
|
+
begin
|
100
|
+
s = YAML.load(open(Settings))
|
101
|
+
@email = s[:email]
|
102
|
+
@name = s[:name]
|
103
|
+
@url = s[:url]
|
104
|
+
@keycache = (s[:keycache][:expires].nil? or s[:keycache][:expires] < Time.now.to_i) ? {} : s[:keycache]
|
105
|
+
rescue
|
106
|
+
raise SettingsNotValidError, "Your settings file is not valid. Please remove it."
|
107
|
+
end
|
108
|
+
else
|
109
|
+
$stderr.puts "No settings file exists. Read the documentation to find out how to make one."
|
110
|
+
@keycache = {}
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Validate email address entry
|
115
|
+
def email=(email)
|
116
|
+
if email =~ /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b/i
|
117
|
+
@email = email
|
118
|
+
write_settings
|
119
|
+
else
|
120
|
+
$stderr.puts "That is an invalid email address"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
# Must have a Name to make DLCs
|
124
|
+
def name=(name)
|
125
|
+
if not name.nil? and name.length != 0
|
126
|
+
@name = name
|
127
|
+
write_settings
|
128
|
+
else
|
129
|
+
$stderr.puts "You must use a full name"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
# Must have a URL (starting in http:// or https://) to make DLCs
|
133
|
+
def url=(url)
|
134
|
+
if url =~ /^http(s?)\:\/\//i
|
135
|
+
@url = url
|
136
|
+
write_settings
|
137
|
+
else
|
138
|
+
$stderr.puts "Please include a leading http://"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Allows the cache of the key/encoded key pairs
|
143
|
+
def set_keycache(key,encoded_key,expires = 3600)
|
144
|
+
@keycache = {
|
145
|
+
:expires => Time.now.to_i + expires,
|
146
|
+
:key => key,
|
147
|
+
:encoded_key => encoded_key
|
148
|
+
}
|
149
|
+
write_settings
|
150
|
+
end
|
151
|
+
|
152
|
+
# Retrieve the key from the cache, if there is one there.
|
153
|
+
# This will raise a +NoKeyCachedError+ if there is no key.
|
154
|
+
def get_keycache
|
155
|
+
if @keycache[:expires].nil? or @keycache[:expires] < Time.now.to_i
|
156
|
+
@keycache = {}
|
157
|
+
raise NoKeyCachedError, "There is no key in the cache"
|
158
|
+
else
|
159
|
+
return @keycache[:key],@keycache[:encoded_key]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# A helper for irb people, and hands-on developlers
|
164
|
+
def inspect
|
165
|
+
if @name.nil? or @email.nil? or @url.nil?
|
166
|
+
$stderr.puts "You need to specify a name, url and email for the DLC generator. See the documentation"
|
167
|
+
else
|
168
|
+
"DLC API owner: #{@name} <#{@email}> (#{@url})"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
def write_settings
|
174
|
+
open(Settings,"w") do |f|
|
175
|
+
f.write YAML.dump({:name => @name, :email => @email, :url => @url,:keycache => @keycache})
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# The DLC package handler class. Make a new one of these for each package you want to create.
|
181
|
+
class Package
|
182
|
+
attr_reader :links,:passwords
|
183
|
+
attr_accessor :comment, :name, :category
|
184
|
+
|
185
|
+
# Makes sure all the defaults are set (this would make a valid, if useless, package)
|
186
|
+
def initialize
|
187
|
+
@links = []
|
188
|
+
@passwords = []
|
189
|
+
@category = "various"
|
190
|
+
@comment = ""
|
191
|
+
end
|
192
|
+
|
193
|
+
# Adds a link to the package
|
194
|
+
# Will take an array of links too
|
195
|
+
# I will, at some point, include the ability to specify filename and size.
|
196
|
+
def add_link(url)
|
197
|
+
if url.is_a?(Array)
|
198
|
+
url.each do |u|
|
199
|
+
self.add_link(u)
|
200
|
+
end
|
201
|
+
return @links
|
202
|
+
end
|
203
|
+
if url.is_a?(String) and url =~ /^http(s)?\:\/\//
|
204
|
+
@links.push({:url=>url,:filename=>nil,:size=>0})
|
205
|
+
return @links
|
206
|
+
end
|
207
|
+
raise RuntimeError, "Invalid URL: #{url}"
|
208
|
+
end
|
209
|
+
|
210
|
+
alias :add_links :add_link
|
211
|
+
|
212
|
+
# Adds a password to the package
|
213
|
+
# Also accepts an array of passwords
|
214
|
+
def add_password(password)
|
215
|
+
if password.is_a?(Array)
|
216
|
+
password.each do |p|
|
217
|
+
self.add_password(p)
|
218
|
+
end
|
219
|
+
return @passwords
|
220
|
+
end
|
221
|
+
if password.is_a?(String)
|
222
|
+
@passwords.push(password)
|
223
|
+
return @passwords
|
224
|
+
end
|
225
|
+
raise RuntimeError, "Invalid password: #{password}"
|
226
|
+
end
|
227
|
+
|
228
|
+
alias :add_passwords :add_password
|
229
|
+
|
230
|
+
# Gives you the DLC of the package you've created. First run (every hour) will take longer than the others while
|
231
|
+
# the jdownloader service is queried for information.
|
232
|
+
def dlc
|
233
|
+
settings = DLC::Settings.new
|
234
|
+
if settings.inspect.nil?
|
235
|
+
raise NoGeneratorDetailsError, "You must enter a name, url and email for the generator. See the documentation."
|
236
|
+
end
|
237
|
+
|
238
|
+
xml = Builder::XmlMarkup.new(:indent=>0)
|
239
|
+
xml.dlc do
|
240
|
+
xml.header do
|
241
|
+
xml.generator do
|
242
|
+
xml.app(DLC.encode("Ruby DLC API (kedakai)"))
|
243
|
+
xml.version(DLC.encode(DLC::Api[:version]))
|
244
|
+
xml.url(DLC.encode(settings.url))
|
245
|
+
end
|
246
|
+
xml.tribute do
|
247
|
+
xml.name(DLC.encode(settings.name))
|
248
|
+
end
|
249
|
+
xml.dlcxmlversion(DLC.encode('20_02_2008'))
|
250
|
+
end
|
251
|
+
xml.content do
|
252
|
+
package = {:name => DLC.encode(@name)}
|
253
|
+
package[:passwords] = DLC.encode(@passwords.collect{|pw| "\"#{pw}\""}.join(",")) if @passwords.length != 0
|
254
|
+
package[:comment] = DLC.encode(@comment) if @comment != ""
|
255
|
+
package[:category] = DLC.encode(@category) if @category != ""
|
256
|
+
xml.package(package) do
|
257
|
+
@links.each do |link|
|
258
|
+
xml.file do
|
259
|
+
xml.url(DLC.encode(link[:url]))
|
260
|
+
xml.filename(DLC.encode(link[:filename]))
|
261
|
+
xml.size(DLC.encode(link[:size]))
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Lets get a key/encoded key pair
|
269
|
+
begin
|
270
|
+
key, encoded_key = settings.get_keycache
|
271
|
+
rescue NoKeyCachedError
|
272
|
+
# Generate a key
|
273
|
+
expires = 3600
|
274
|
+
key = Digest::MD5.hexdigest(Time.now.to_i.to_s+"salty salty"+rand(100000).to_s)[0..15]
|
275
|
+
begin
|
276
|
+
if Net::HTTP.post_form(URI.parse(DLC::Api[:service_urls][rand(DLC::Api[:service_urls].length)]),{
|
277
|
+
:data => key, # A random key
|
278
|
+
:lid => DLC.encode([settings.url,settings.email,expires].join("_")), # Details about the generator of the DLC
|
279
|
+
:version => DLC::Api[:version],
|
280
|
+
:client => "rubydlc"
|
281
|
+
}).body =~ /^<rc>(.+)<\/rc><rcp>(.+)<\/rcp>$/
|
282
|
+
encoded_key = $1
|
283
|
+
# What is the second part?!
|
284
|
+
settings.set_keycache(key, encoded_key, expires)
|
285
|
+
else
|
286
|
+
raise ServerNotRespondingError
|
287
|
+
end
|
288
|
+
rescue
|
289
|
+
raise ServerNotRespondingError, "The DLC service is not responding in the expected way. Try again later."
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
b64 = DLC.encode(xml.target!)
|
294
|
+
DLC.encode(Aes.encrypt_buffer(128,"CBC",key,key,b64.ljust((b64.length/16).ceil*16,"\000")))+encoded_key
|
295
|
+
end
|
296
|
+
|
297
|
+
# Gives some useful information when people use the library from irb
|
298
|
+
def inspect
|
299
|
+
((@name.nil?) ? "Unnamed package" : "\"#{@name}\"" )+" (#{@links.length} links, #{@passwords.length} passwords)"+((@comment == "") ? "" : "\n# #{@comment}")
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
# For when the settings file is invalid
|
304
|
+
class SettingsNotValidError < StandardError; end
|
305
|
+
# For when a DLC is requested without settings set
|
306
|
+
class NoGeneratorDetailsError < StandardError; end
|
307
|
+
# For when the keycache is accessed and no valid key is available
|
308
|
+
class NoKeyCachedError < StandardError; end
|
309
|
+
# For when the service is not responding in the expected manner
|
310
|
+
class ServerNotRespondingError < StandardError; end
|
311
|
+
|
312
|
+
private
|
313
|
+
def self.encode(string)
|
314
|
+
string = "n.A." if string.nil?
|
315
|
+
return [string.to_s].pack("m").gsub("\n","")
|
316
|
+
end
|
317
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dlc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 1.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- JP Hastings-Spital
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-06-08 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: builder
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: Allows the generation of DLC container files (of JDownloader fame) from ruby
|
36
|
+
email: jphastings@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- README.rdoc
|
45
|
+
- Rakefile
|
46
|
+
- VERSION
|
47
|
+
- dlc_settings.yml
|
48
|
+
- lib/dlc.rb
|
49
|
+
has_rdoc: true
|
50
|
+
homepage: http://github.com/jphastings/ruby-DLC
|
51
|
+
licenses: []
|
52
|
+
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --charset=UTF-8
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
hash: 3
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.3.7
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: Allows the generation of DLC container files (of JDownloader fame) from ruby
|
83
|
+
test_files: []
|
84
|
+
|