dlc 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|