dlc 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/README.rdoc +5 -0
  2. data/Rakefile +42 -0
  3. data/VERSION +1 -0
  4. data/dlc_settings.yml +8 -0
  5. data/lib/dlc.rb +317 -0
  6. metadata +84 -0
@@ -0,0 +1,5 @@
1
+ = Ruby DLC
2
+
3
+ Take a look at the documentation for more information: http://rdoc.info/jphastings/ruby-dlc/
4
+
5
+ There's a gist about using the library for the first time here: http://gist.github.com/106978
@@ -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
@@ -0,0 +1,8 @@
1
+ ---
2
+ :keycache:
3
+ :encoded_key: Sm91aTRQUXpQWkxUK3c1NjFKeDNvcGR4V09LdWdtbVQ4ZGFST1Nja2Q4RFQ0dFU2c2c3M1J2N2FCUzBPU2pKdw==
4
+ :key: 7aac8b326e4ca53e
5
+ :expires: 1276026954
6
+ :url: http://google.com
7
+ :email: jphastings@gmail.com
8
+ :name: JP Hastings-Spital
@@ -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
+