rmega 0.2.1 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +2 -0
- data/CHANGELOG.md +40 -0
- data/Dockerfile +14 -0
- data/README.md +14 -13
- data/bin/rmega-dl +35 -8
- data/bin/rmega-up +9 -1
- data/lib/rmega.rb +2 -0
- data/lib/rmega/api_response.rb +4 -1
- data/lib/rmega/cli.rb +23 -10
- data/lib/rmega/crypto/aes_cbc.rb +13 -10
- data/lib/rmega/errors.rb +10 -0
- data/lib/rmega/net.rb +19 -7
- data/lib/rmega/nodes/downloadable.rb +3 -3
- data/lib/rmega/nodes/expandable.rb +1 -1
- data/lib/rmega/nodes/factory.rb +22 -6
- data/lib/rmega/nodes/file.rb +4 -1
- data/lib/rmega/nodes/node.rb +5 -4
- data/lib/rmega/nodes/uploadable.rb +1 -1
- data/lib/rmega/progress.rb +1 -0
- data/lib/rmega/session.rb +23 -4
- data/lib/rmega/utils.rb +7 -0
- data/lib/rmega/version.rb +1 -1
- data/rmega.gemspec +5 -3
- data/spec/integration/file_upload_spec.rb +1 -1
- data/spec/integration/resume_download_spec.rb +2 -1
- data/spec/integration_spec_helper.rb +1 -1
- metadata +21 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 48d1cd2037a5511fdc9adc693fbd1b6e74331327a3e46a93e2326a3c9aef7f42
|
4
|
+
data.tar.gz: 159cd701d79d5fdb8d903a562f01fff6a439fedb2d6efff926acf8df772182f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74c4ac1a15aeafeea21aed3dce4f17e8bdc7f82bf50608d776bc3c9df27e57ece398e8b8cf1b77d6dcd34114a020b7b1bcbcf3bdeca398da80b0ab80c3e72f39
|
7
|
+
data.tar.gz: 48a51f07da3be0c88619068afa1fc93a42d4f77dc008ddf6a5504a66aa536584e46af9e3c799fcf8f70fec44c6d31c5aabdaf74bd13257fa4910df9418c793b9
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,43 @@
|
|
1
|
+
## 0.2.7
|
2
|
+
|
3
|
+
### New Features
|
4
|
+
* Add support for login ver. 2 (newer accounts only)
|
5
|
+
* Add new error codes
|
6
|
+
* Fixed crash when uploading file with invalid UTF-8 characters
|
7
|
+
|
8
|
+
## 0.2.6
|
9
|
+
|
10
|
+
### New Features
|
11
|
+
* Support new links format, e.g. `https://mega.nz/#!foo!bar` is now `https://mega.nz/file/foo#bar`
|
12
|
+
* Add `--get-link` flag to `rmega-up`: generate and print the sharable link of the new file
|
13
|
+
|
14
|
+
## 0.2.5
|
15
|
+
|
16
|
+
### Changes
|
17
|
+
* \#27 The error raised when the free quota is exceeded is now properly handled
|
18
|
+
* \#27 Improved detection of mega link when a generic url is given to ```rmega-dl``` (see cmd line usage)
|
19
|
+
|
20
|
+
## 0.2.4
|
21
|
+
|
22
|
+
### Changes
|
23
|
+
* \#25 Fix connection reset on file upload
|
24
|
+
* \#24 Speed up aes_cbc_mac func
|
25
|
+
|
26
|
+
## 0.2.3
|
27
|
+
|
28
|
+
### Changes
|
29
|
+
* Fixed reading options from the configuration file (~/.rmega)
|
30
|
+
* The max number of parallel threads is now 8
|
31
|
+
|
32
|
+
### New Features
|
33
|
+
* If `rmega-dl` receive a local file as the main args, that file is treated as a text file that must contains a list of mega links
|
34
|
+
* The download progress bar now distinguishes between allocate, verify and download phase
|
35
|
+
|
36
|
+
## 0.2.2
|
37
|
+
|
38
|
+
### Changes
|
39
|
+
* \#17 Fixed download of shared folders
|
40
|
+
|
1
41
|
## 0.2.1
|
2
42
|
|
3
43
|
### New Features
|
data/Dockerfile
ADDED
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/rmega.svg)](https://badge.fury.io/rb/rmega)
|
2
|
+
[![Build Status](https://travis-ci.org/topac/rmega.svg?branch=master)](https://travis-ci.org/topac/rmega)
|
3
|
+
|
4
|
+
# rmega
|
2
5
|
|
3
6
|
Pure ruby library for <img src="https://mega.co.nz/favicon.ico" alt=""/> **MEGA** [https://mega.nz/](https://mega.nz/).
|
4
7
|
Works on Linux and OSX with Ruby 1.9.3+.
|
@@ -9,22 +12,20 @@ Works on Linux and OSX with Ruby 1.9.3+.
|
|
9
12
|
gem install rmega
|
10
13
|
```
|
11
14
|
|
12
|
-
## Command
|
15
|
+
## Command Line Usage
|
16
|
+
|
17
|
+
<img src="https://i.imgur.com/3cjgp4g.gif"/>
|
13
18
|
|
14
|
-
Since version 0.2.0 you can use the commands `rmega-dl` and `rmega-up` to
|
19
|
+
Since version 0.2.0 you can use the commands `rmega-dl` and `rmega-up` to download and upload files to MEGA.
|
15
20
|
|
16
21
|
* Downloads are resumable
|
22
|
+
* You can download all the links in a given file, e.g. `rmega-dl my_links.txt`
|
23
|
+
* Scrape a web page and download all the MEGA links, e.g. `rmega-dl https://www.reddit.com/r/megalinks/foobar`
|
17
24
|
* HTTP proxy support
|
18
|
-
*
|
19
|
-
|
20
|
-
<img src="https://i.imgur.com/VVl55wj.gif"/>
|
21
|
-
|
22
|
-
*Pro tips:*
|
23
|
-
|
24
|
-
* Streaming: you can use a video player (e.g. VLC) to play videos while downloading them.
|
25
|
-
* Super privacy: you can use it combined with [torsocks](https://github.com/dgoulet/torsocks/) to download and upload files through the Tor network (very slow).
|
25
|
+
* Streaming! :beer: You can use a video player (VLC works perfectly) to play videos while downloading them
|
26
|
+
* You can use it combined with [torsocks](https://github.com/dgoulet/torsocks/) to download and upload files through the Tor network (slower)
|
26
27
|
|
27
|
-
## DSL
|
28
|
+
## DSL Usage
|
28
29
|
|
29
30
|
### Login
|
30
31
|
|
@@ -88,7 +89,7 @@ end
|
|
88
89
|
folder.download("~/Downloads/my_folder")
|
89
90
|
|
90
91
|
# Download a file by url
|
91
|
-
|
92
|
+
public_url = 'https://mega.nz/file/MAkg2Iab#bc9Y2U6d93IlRRKVYpcC9hLZjS4G278OPdH6nTFPDNQ'
|
92
93
|
Rmega.download(public_url, '~/Downloads')
|
93
94
|
```
|
94
95
|
|
data/bin/rmega-dl
CHANGED
@@ -16,8 +16,11 @@ OptionParser.new do |opts|
|
|
16
16
|
opts.banner << "\t#{File.basename(__FILE__)} url [options]\n"
|
17
17
|
opts.banner << "\t#{File.basename(__FILE__)} path [options]\n"
|
18
18
|
opts.banner << "Examples:\n"
|
19
|
-
opts.banner << "\t#{File.basename(__FILE__)} 'https://mega.nz/#!aBkHBKLX!
|
20
|
-
opts.banner << "\t#{File.basename(__FILE__)} https://
|
19
|
+
opts.banner << "\t#{File.basename(__FILE__)} 'https://mega.nz/#!aBkHBKLX!n4kqzbJooqcPZjN1tEJzQ4QQwskHf7YqKa'\n"
|
20
|
+
opts.banner << "\t#{File.basename(__FILE__)} https://mega.nz/file/aBkHBKLX#n4kqzbJooqcPZjN1tEJzQ4QQwskHf7YqKa\n"
|
21
|
+
opts.banner << "\t#{File.basename(__FILE__)} https://mega.nz/folder/aBkHBKLX#n4kqzbJooqcPZjN1tEJzQ4QQwskHf7YqKa\n"
|
22
|
+
opts.banner << "\t#{File.basename(__FILE__)} https://www.reddit.com/r/megalinks3\n"
|
23
|
+
opts.banner << "\t#{File.basename(__FILE__)} mylinks.txt\n"
|
21
24
|
opts.banner << "\t#{File.basename(__FILE__)} /remote/docs/myfile.txt -u email@localhost\n"
|
22
25
|
opts.banner << "Options:"
|
23
26
|
|
@@ -36,17 +39,41 @@ cli_rescue do
|
|
36
39
|
raise("Node not found - #{cli_options[:url]}") unless node
|
37
40
|
node.download(cli_options[:output] || Dir.pwd)
|
38
41
|
else
|
39
|
-
urls = [
|
42
|
+
urls = []
|
40
43
|
|
41
|
-
|
42
|
-
|
43
|
-
urls =
|
44
|
-
|
44
|
+
if mega_url?(cli_options[:url])
|
45
|
+
# a valid MEGA urls
|
46
|
+
urls = [cli_options[:url]]
|
47
|
+
else
|
48
|
+
# A text file with a list of MEGA urls (considering only files < 1 Mb)
|
49
|
+
if File.exists?(cli_options[:url])
|
50
|
+
if File.size(cli_options[:url]) < 1_000_000
|
51
|
+
File.open(cli_options[:url], "rb") do |file|
|
52
|
+
file.each_line do |line|
|
53
|
+
line.strip!
|
54
|
+
urls << line if mega_url?(line)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
else
|
59
|
+
# A link to a web page with some MEGA urls in its body
|
60
|
+
html = Rmega::Session.new.http_get_content(cli_options[:url])
|
61
|
+
|
62
|
+
urls = html.scan(Rmega::Nodes::Factory::URL_REGEXP)
|
63
|
+
urls << html.scan(Rmega::Nodes::Factory::URL_REGEXP_NEW)
|
64
|
+
urls = urls.flatten.select { |u| Rmega::Nodes::Factory.url?(u) }.uniq
|
65
|
+
|
66
|
+
if cli_options[:debug] and urls.any?
|
67
|
+
Rmega.logger.debug("#{urls.size} URL(s) founded:\n" + urls.join("\n"))
|
68
|
+
end
|
69
|
+
end
|
45
70
|
end
|
46
71
|
|
72
|
+
raise("Nothing to download") if urls.empty?
|
73
|
+
|
47
74
|
urls.each_with_index do |url, index|
|
48
75
|
node = Rmega::Nodes::Factory.build_from_url(url)
|
49
|
-
|
76
|
+
puts "[#{index+1}/#{urls.size}] #{url}" if urls.size > 1
|
50
77
|
node.download(cli_options[:output] || Dir.pwd)
|
51
78
|
end
|
52
79
|
end
|
data/bin/rmega-up
CHANGED
@@ -22,6 +22,10 @@ OptionParser.new do |opts|
|
|
22
22
|
cli_options[:remote_path] = path
|
23
23
|
}
|
24
24
|
|
25
|
+
opts.on("-l", "--get-link", "Generate and print the sharable link (witk key)") {
|
26
|
+
cli_options[:get_link] = true
|
27
|
+
}
|
28
|
+
|
25
29
|
apply_opt_parser_options(opts)
|
26
30
|
end.parse!
|
27
31
|
|
@@ -37,5 +41,9 @@ cli_rescue do
|
|
37
41
|
raise("Node not found - #{cli_options[:remote_path]}") unless node
|
38
42
|
raise("Node cannot be a file - #{cli_options[:remote_path]}") if node.type == :file
|
39
43
|
|
40
|
-
node.upload(cli_options[:path])
|
44
|
+
file = node.upload(cli_options[:path])
|
45
|
+
|
46
|
+
if cli_options[:get_link]
|
47
|
+
puts file.public_url
|
48
|
+
end
|
41
49
|
end
|
data/lib/rmega.rb
CHANGED
data/lib/rmega/api_response.rb
CHANGED
@@ -2,7 +2,7 @@ module Rmega
|
|
2
2
|
class APIResponse
|
3
3
|
attr_reader :body, :code
|
4
4
|
|
5
|
-
# Check out the error codes list at https://mega.
|
5
|
+
# Check out the error codes list at https://mega.nz/#doc (section 11)
|
6
6
|
ERRORS = {
|
7
7
|
-1 => 'An internal error has occurred. Please submit a bug report, detailing the exact circumstances in which this error occurred.',
|
8
8
|
-2 => 'You have passed invalid arguments to this command.',
|
@@ -26,6 +26,9 @@ module Rmega
|
|
26
26
|
-20 => 'Write failed',
|
27
27
|
-21 => 'Read failed',
|
28
28
|
-22 => 'Invalid application key; request not processed',
|
29
|
+
-23 => 'SSL verification failed',
|
30
|
+
-24 => 'Not enough quota',
|
31
|
+
-26 => '2FA required',
|
29
32
|
}.freeze
|
30
33
|
|
31
34
|
def initialize(http_response)
|
data/lib/rmega/cli.rb
CHANGED
@@ -6,7 +6,7 @@ module Rmega
|
|
6
6
|
module CLI
|
7
7
|
module Helpers
|
8
8
|
def cli_options
|
9
|
-
$cli_options ||=
|
9
|
+
$cli_options ||= read_configuration_file
|
10
10
|
end
|
11
11
|
|
12
12
|
def cli_prompt_password
|
@@ -27,12 +27,14 @@ module Rmega
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def read_configuration_file
|
30
|
-
return unless File.exists?(configuration_filepath)
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
return {} unless File.exists?(configuration_filepath)
|
31
|
+
|
32
|
+
opts = YAML.load_file(configuration_filepath)
|
33
|
+
opts.keys.each { |k| opts[k.to_sym] = opts.delete(k) } # symbolize_keys!
|
34
|
+
|
35
|
+
return opts
|
34
36
|
rescue Exception => ex
|
35
|
-
raise(ex)
|
37
|
+
raise(ex)
|
36
38
|
end
|
37
39
|
|
38
40
|
def apply_cli_options
|
@@ -41,11 +43,23 @@ module Rmega
|
|
41
43
|
end
|
42
44
|
Rmega.logger.level = ::Logger::DEBUG if cli_options[:debug]
|
43
45
|
Rmega.options.show_progress = true
|
46
|
+
|
47
|
+
if Thread.respond_to?(:report_on_exception) and !cli_options[:debug]
|
48
|
+
Thread.report_on_exception = false
|
49
|
+
end
|
44
50
|
end
|
45
51
|
|
46
52
|
def apply_opt_parser_options(opts)
|
47
|
-
opts.on("-t NUM", "--thread_pool_size", "Number of threads to use") { |
|
48
|
-
|
53
|
+
opts.on("-t NUM", "--thread_pool_size", "Number of threads to use [1-8], default and recommended is #{Rmega.options.thread_pool_size}") { |num|
|
54
|
+
num = num.to_i
|
55
|
+
|
56
|
+
if num <= 0
|
57
|
+
num = 1
|
58
|
+
elsif num > 8
|
59
|
+
num = 8
|
60
|
+
end
|
61
|
+
|
62
|
+
cli_options[:thread_pool_size] = num
|
49
63
|
}
|
50
64
|
|
51
65
|
opts.on("--proxy-addr ADDRESS", "Http proxy address") { |value|
|
@@ -71,7 +85,7 @@ module Rmega
|
|
71
85
|
opts.on("-v", "--version", "Print the version number") {
|
72
86
|
puts Rmega::VERSION
|
73
87
|
puts Rmega::HOMEPAGE
|
74
|
-
exit
|
88
|
+
exit(0)
|
75
89
|
}
|
76
90
|
end
|
77
91
|
|
@@ -99,7 +113,6 @@ module Rmega
|
|
99
113
|
end
|
100
114
|
|
101
115
|
def cli_rescue
|
102
|
-
read_configuration_file
|
103
116
|
apply_cli_options
|
104
117
|
yield
|
105
118
|
rescue Interrupt
|
data/lib/rmega/crypto/aes_cbc.rb
CHANGED
@@ -28,18 +28,21 @@ module Rmega
|
|
28
28
|
cipher.iv = iv if iv
|
29
29
|
cipher.key = key
|
30
30
|
|
31
|
-
n = 0
|
32
|
-
mac = nil
|
31
|
+
# n = 0
|
32
|
+
# mac = nil
|
33
33
|
|
34
|
-
loop do
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
34
|
+
# loop do
|
35
|
+
# block = data[n..n+15]
|
36
|
+
# break if !block or block.empty?
|
37
|
+
# block << "\x0"*(16-block.size) if block.size < 16
|
38
|
+
# n += 16
|
39
|
+
# mac = cipher.update(block)
|
40
|
+
# end
|
41
41
|
|
42
|
-
return mac
|
42
|
+
# return mac
|
43
|
+
|
44
|
+
block = data + "\x0" * ((16 - data.bytesize % 16) % 16)
|
45
|
+
return cipher.update(block)[-16..-1]
|
43
46
|
end
|
44
47
|
end
|
45
48
|
end
|
data/lib/rmega/errors.rb
CHANGED
data/lib/rmega/net.rb
CHANGED
@@ -18,7 +18,13 @@ module Rmega
|
|
18
18
|
def http_get_content(url)
|
19
19
|
uri = URI(url)
|
20
20
|
req = ::Net::HTTP::Get.new(uri.request_uri)
|
21
|
-
|
21
|
+
resp = net_http(uri).request(req)
|
22
|
+
|
23
|
+
if resp.code.to_i == 509 and resp.body.to_s.empty?
|
24
|
+
raise BandwidthLimitExceeded.new
|
25
|
+
end
|
26
|
+
|
27
|
+
return resp.body
|
22
28
|
end
|
23
29
|
|
24
30
|
def http_post(url, data)
|
@@ -26,30 +32,36 @@ module Rmega
|
|
26
32
|
req = ::Net::HTTP::Post.new(uri.request_uri)
|
27
33
|
req.body = data
|
28
34
|
logger.debug("REQ POST #{url} #{cut_string(data)}")
|
29
|
-
|
35
|
+
|
36
|
+
# if you don't use Net::Http#start it will not keep the socket open even if you set
|
37
|
+
# the connection header BUT setting the connection header to 'keep-alive' its enough
|
38
|
+
# to fool MEGA servers and don't let them reset your connection!
|
39
|
+
req['Connection'] = 'keep-alive'
|
40
|
+
|
41
|
+
response = net_http(uri).request(req)
|
30
42
|
logger.debug("REP #{response.code} #{cut_string(response.body)}")
|
31
43
|
return response
|
32
44
|
end
|
33
45
|
|
34
46
|
private
|
35
47
|
|
36
|
-
def
|
48
|
+
def net_http(uri)
|
37
49
|
http = ::Net::HTTP.new(uri.host, uri.port)
|
38
50
|
http.use_ssl = true if uri.scheme == 'https'
|
39
|
-
apply_http_options(http)
|
40
|
-
return http.request(req)
|
41
|
-
end
|
42
51
|
|
43
|
-
|
52
|
+
# apply common http options
|
44
53
|
http.proxy_from_env = false if options.http_proxy_address
|
45
54
|
|
46
55
|
options.marshal_dump.each do |name, value|
|
47
56
|
setter_method = name.to_s.split('http_')[1]
|
48
57
|
http.__send__("#{setter_method}=", value) if setter_method and value
|
49
58
|
end
|
59
|
+
|
60
|
+
return http
|
50
61
|
end
|
51
62
|
|
52
63
|
def cut_string(string, max = 50)
|
64
|
+
return "<binary data, #{string.size} bytes>" if string.encoding == ::Encoding::ASCII_8BIT
|
53
65
|
string.size <= max ? string : string[0..max-1]+"..."
|
54
66
|
end
|
55
67
|
end
|
@@ -69,7 +69,7 @@ module Rmega
|
|
69
69
|
path = ::File.expand_path(path)
|
70
70
|
path = Dir.exists?(path) ? ::File.join(path, name) : path
|
71
71
|
|
72
|
-
progress = Progress.new(filesize, caption: '
|
72
|
+
progress = Progress.new(filesize, caption: 'Allocate', filename: self.name)
|
73
73
|
pool = Pool.new
|
74
74
|
|
75
75
|
@resumed_download = allocated?(path)
|
@@ -84,12 +84,12 @@ module Rmega
|
|
84
84
|
|
85
85
|
if data
|
86
86
|
chunk_macs[start] = calculate_chunck_mac(data) if options.file_integrity_check
|
87
|
-
progress.increment(size, real: false)
|
87
|
+
progress.increment(size, real: false, caption: "Verify")
|
88
88
|
else
|
89
89
|
data = decrypt_chunk(start, download_chunk(start, size))
|
90
90
|
chunk_macs[start] = calculate_chunck_mac(data) if options.file_integrity_check
|
91
91
|
write_chunk(start, data)
|
92
|
-
progress.increment(size)
|
92
|
+
progress.increment(size, caption: "Download")
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
data/lib/rmega/nodes/factory.rb
CHANGED
@@ -16,12 +16,25 @@ module Rmega
|
|
16
16
|
module Factory
|
17
17
|
extend self
|
18
18
|
|
19
|
-
URL_REGEXP = /(
|
19
|
+
URL_REGEXP = /(https{0,1}:\/\/[w\.]*mega\.[a-z\.]+\/\#[A-Z0-9\_\-\!\=]+)/i
|
20
|
+
URL_REGEXP_NEW = /(https{0,1}:\/\/[w\.]*mega\.[a-z\.]+\/[a-z]{4,6}\/[a-z0-9\_\-\=]+\#[a-z0-9\_\-\=]+)/i
|
20
21
|
|
21
|
-
|
22
|
+
def public_handle_and_key_from_url(string)
|
23
|
+
if string.to_s =~ URL_REGEXP
|
24
|
+
public_handle, key = string.strip.split('!')[1, 2]
|
25
|
+
return [] if key and (Utils.base64urldecode(key) rescue nil).nil?
|
26
|
+
return [public_handle, key]
|
27
|
+
elsif string.to_s =~ URL_REGEXP_NEW
|
28
|
+
public_handle, key = *string.scan(/\/([^\/]+)\#(.+)$/).flatten
|
29
|
+
return [] if key and (Utils.base64urldecode(key) rescue nil).nil?
|
30
|
+
return [public_handle, key]
|
31
|
+
else
|
32
|
+
return []
|
33
|
+
end
|
34
|
+
end
|
22
35
|
|
23
36
|
def url?(string)
|
24
|
-
string.
|
37
|
+
public_handle_and_key_from_url(string).any?
|
25
38
|
end
|
26
39
|
|
27
40
|
def build(session, data)
|
@@ -30,14 +43,17 @@ module Rmega
|
|
30
43
|
end
|
31
44
|
|
32
45
|
def build_from_url(url, session = Session.new)
|
33
|
-
public_handle, key = url
|
46
|
+
public_handle, key = *public_handle_and_key_from_url(url)
|
34
47
|
|
35
48
|
raise "Invalid url or missing file key" unless key
|
36
49
|
|
37
|
-
node = if url
|
50
|
+
node = if url.include?("/folder/") or url.include?("/#F!")
|
38
51
|
nodes_data = session.request({a: 'f', c: 1, r: 1}, {n: public_handle})
|
39
52
|
session.master_key = Utils.base64urldecode(key)
|
40
|
-
session.storage.nodes = nodes_data['f'].map
|
53
|
+
session.storage.nodes = nodes_data['f'].map do |data|
|
54
|
+
data["__n"] = public_handle
|
55
|
+
Nodes::Factory.build(session, data)
|
56
|
+
end
|
41
57
|
session.storage.nodes[0]
|
42
58
|
else
|
43
59
|
data = session.request(a: 'g', g: 1, p: public_handle)
|
data/lib/rmega/nodes/file.rb
CHANGED
@@ -5,7 +5,10 @@ module Rmega
|
|
5
5
|
include Downloadable
|
6
6
|
|
7
7
|
def storage_url
|
8
|
-
@storage_url ||=
|
8
|
+
@storage_url ||= begin
|
9
|
+
query_params = data["__n"] ? {n: data["__n"]} : {}
|
10
|
+
data['g'] || request({a: 'g', g: 1, n: handle}, query_params)['g']
|
11
|
+
end
|
9
12
|
end
|
10
13
|
|
11
14
|
def size
|
data/lib/rmega/nodes/node.rb
CHANGED
@@ -21,7 +21,7 @@ module Rmega
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def public_url
|
24
|
-
@public_url ||= "https://mega.
|
24
|
+
@public_url ||= "https://mega.nz/file/#{public_handle}\##{Utils.base64urlencode(decrypted_file_key)}"
|
25
25
|
end
|
26
26
|
|
27
27
|
def public_handle
|
@@ -31,14 +31,14 @@ module Rmega
|
|
31
31
|
def serialize_attributes(hash)
|
32
32
|
str = "MEGA"
|
33
33
|
str << hash.to_json
|
34
|
-
str << ("\x00" * (16 - (str.
|
34
|
+
str << ("\x00" * (16 - (str.bytesize % 16)))
|
35
35
|
return str
|
36
36
|
end
|
37
37
|
|
38
38
|
def rename(new_name)
|
39
39
|
node_key = NodeKey.load(decrypted_file_key)
|
40
40
|
|
41
|
-
_attr = serialize_attributes(attributes.merge("n" => new_name))
|
41
|
+
_attr = serialize_attributes(attributes.merge("n" => Utils.utf8(new_name)))
|
42
42
|
_attr = aes_cbc_encrypt(node_key.aes_key, _attr)
|
43
43
|
_attr = Utils.base64urlencode(_attr)
|
44
44
|
|
@@ -138,7 +138,8 @@ module Rmega
|
|
138
138
|
elsif file_key
|
139
139
|
aes_ecb_decrypt(master_key, file_key)
|
140
140
|
else
|
141
|
-
|
141
|
+
ary = Rmega::Nodes::Factory.public_handle_and_key_from_url(public_url)
|
142
|
+
Utils.base64urldecode(ary[1])
|
142
143
|
end
|
143
144
|
end
|
144
145
|
|
@@ -69,7 +69,7 @@ module Rmega
|
|
69
69
|
pool.shutdown
|
70
70
|
|
71
71
|
# encrypt attributes
|
72
|
-
_attr = serialize_attributes(:n => ::File.basename(path))
|
72
|
+
_attr = serialize_attributes(:n => Utils.utf8(::File.basename(path)))
|
73
73
|
_attr = aes_cbc_encrypt(rnd_node_key.aes_key, _attr)
|
74
74
|
|
75
75
|
# Calculate meta_mac
|
data/lib/rmega/progress.rb
CHANGED
data/lib/rmega/session.rb
CHANGED
@@ -86,12 +86,31 @@ module Rmega
|
|
86
86
|
# * The user master_key (128 bit for AES) encrypted with the password_hash
|
87
87
|
# * The RSA private key ecrypted with the master_key
|
88
88
|
# * A brand new session_id encrypted with the RSA private key
|
89
|
-
def login(email, password)
|
89
|
+
def login(email, password)
|
90
|
+
# discover the version of the account (1: old accounts, >=2: newer accouts)
|
91
|
+
resp = request(a: 'us0', user: email.strip)
|
92
|
+
account_version = resp["v"].to_i
|
93
|
+
|
90
94
|
# Derive an hash from the user password
|
91
|
-
|
92
|
-
|
95
|
+
if account_version == 1
|
96
|
+
password_hash = hash_password(password)
|
97
|
+
u_hash = user_hash(password_hash, email.strip.downcase)
|
98
|
+
else
|
99
|
+
df2 = PBKDF2.new(
|
100
|
+
:password => password,
|
101
|
+
:salt => Utils.base64urldecode(resp['s']),
|
102
|
+
:iterations => 100000,
|
103
|
+
:hash_function => :sha512,
|
104
|
+
:key_length => 16 * 2,
|
105
|
+
).bin_string
|
106
|
+
password_hash = df2[0,16]
|
107
|
+
u_hash = Utils.base64urlencode(df2[16,32])
|
108
|
+
end
|
93
109
|
|
94
|
-
|
110
|
+
# Send the login request
|
111
|
+
req = {a: 'us', user: email.strip, uh: u_hash}
|
112
|
+
req[:sek] = Utils.base64urlencode(SecureRandom.random_bytes(16)) if account_version != 1
|
113
|
+
resp = request(req)
|
95
114
|
|
96
115
|
@master_key = aes_cbc_decrypt(password_hash, Utils.base64urldecode(resp['k']))
|
97
116
|
@rsa_privk = decrypt_rsa_private_key(resp['privk'])
|
data/lib/rmega/utils.rb
CHANGED
data/lib/rmega/version.rb
CHANGED
data/rmega.gemspec
CHANGED
@@ -3,9 +3,9 @@ require File.expand_path('../lib/rmega/version', __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["topac"]
|
6
|
-
gem.email = ["
|
7
|
-
gem.description = %q{mega.
|
8
|
-
gem.summary = %q{mega.
|
6
|
+
gem.email = ["topac@users.noreply.github.com"]
|
7
|
+
gem.description = %q{mega.nz ruby api}
|
8
|
+
gem.summary = %q{mega.nz ruby api}
|
9
9
|
gem.homepage = Rmega::HOMEPAGE
|
10
10
|
|
11
11
|
gem.files = `git ls-files`.split($\)
|
@@ -17,6 +17,8 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.license = 'MIT'
|
18
18
|
gem.required_ruby_version = '>= 1.9.3'
|
19
19
|
|
20
|
+
gem.add_dependency "pbkdf2-ruby"
|
21
|
+
|
20
22
|
gem.add_development_dependency "pry"
|
21
23
|
gem.add_development_dependency "rspec"
|
22
24
|
gem.add_development_dependency "rake"
|
@@ -8,7 +8,8 @@ module Rmega
|
|
8
8
|
let(:destination_file) { "#{temp_folder}/temp.txt" }
|
9
9
|
|
10
10
|
before do
|
11
|
-
Thread.abort_on_exception = false
|
11
|
+
Thread.abort_on_exception = false if Thread.respond_to?(:abort_on_exception)
|
12
|
+
Thread.report_on_exception = false if Thread.respond_to?(:report_on_exception)
|
12
13
|
allow_any_instance_of(Pool).to receive(:threads_raises_exceptions).and_return(nil)
|
13
14
|
end
|
14
15
|
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rmega
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- topac
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pbkdf2-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: pry
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,9 +66,9 @@ dependencies:
|
|
52
66
|
- - ">="
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '0'
|
55
|
-
description: mega.
|
69
|
+
description: mega.nz ruby api
|
56
70
|
email:
|
57
|
-
-
|
71
|
+
- topac@users.noreply.github.com
|
58
72
|
executables:
|
59
73
|
- rmega-dl
|
60
74
|
- rmega-up
|
@@ -64,6 +78,7 @@ files:
|
|
64
78
|
- ".gitignore"
|
65
79
|
- ".travis.yml"
|
66
80
|
- CHANGELOG.md
|
81
|
+
- Dockerfile
|
67
82
|
- Gemfile
|
68
83
|
- LICENSE
|
69
84
|
- README.md
|
@@ -138,11 +153,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
153
|
- !ruby/object:Gem::Version
|
139
154
|
version: '0'
|
140
155
|
requirements: []
|
141
|
-
|
142
|
-
rubygems_version: 2.4.6
|
156
|
+
rubygems_version: 3.1.2
|
143
157
|
signing_key:
|
144
158
|
specification_version: 4
|
145
|
-
summary: mega.
|
159
|
+
summary: mega.nz ruby api
|
146
160
|
test_files:
|
147
161
|
- spec/integration/file_download_spec.rb
|
148
162
|
- spec/integration/file_integrity_spec.rb
|