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.
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
+