rmega 0.2.1 → 0.2.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 137fdede22cdd2e053dea69f98b227700a8b606f
4
- data.tar.gz: 49ed20b4c7b0aa5419861c59f842f5a35a2a883d
2
+ SHA256:
3
+ metadata.gz: 48d1cd2037a5511fdc9adc693fbd1b6e74331327a3e46a93e2326a3c9aef7f42
4
+ data.tar.gz: 159cd701d79d5fdb8d903a562f01fff6a439fedb2d6efff926acf8df772182f0
5
5
  SHA512:
6
- metadata.gz: 0de9a225767de5aeacb5a82629a10df8e8017bd2f49c2514904a9bb59b451d300c554ae990512644b064aa16e24a085989a16aa16a400094b1d8ef3a3c3ff652
7
- data.tar.gz: 75d935e07f6957227d87800831efa7e07a5966cb134324798e6f5b37b4780e726f97e094d984ab5f137e002a19475fd167bb8142c5af0b4556ee6152907f7531
6
+ metadata.gz: 74c4ac1a15aeafeea21aed3dce4f17e8bdc7f82bf50608d776bc3c9df27e57ece398e8b8cf1b77d6dcd34114a020b7b1bcbcf3bdeca398da80b0ab80c3e72f39
7
+ data.tar.gz: 48a51f07da3be0c88619068afa1fc93a42d4f77dc008ddf6a5504a66aa536584e46af9e3c799fcf8f70fec44c6d31c5aabdaf74bd13257fa4910df9418c793b9
@@ -6,4 +6,6 @@ rvm:
6
6
  - 2.0
7
7
  - 2.1
8
8
  - 2.2
9
+ - 2.4
10
+ - 2.5
9
11
  - ruby-head
@@ -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
@@ -0,0 +1,14 @@
1
+ FROM ruby
2
+
3
+ RUN apt-get install -y
4
+
5
+ RUN mkdir app
6
+
7
+ COPY . /app
8
+
9
+ RUN cd /app && \
10
+ gem install bundler && \
11
+ bundle install
12
+
13
+ WORKDIR /app
14
+ ENTRYPOINT ["rspec", "-f", "d"]
data/README.md CHANGED
@@ -1,4 +1,7 @@
1
- # Rmega
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 line usage
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 easily download and upload files to MEGA.
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
- * See the CHANGELOG file for more info
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 usage
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
- publid_url = 'https://mega.co.nz/#!MAkg2Iab!bc9Y2U6d93IlRRKVYpcC9hLZjS4G278OPdH6nTFPDNQ'
92
+ public_url = 'https://mega.nz/file/MAkg2Iab#bc9Y2U6d93IlRRKVYpcC9hLZjS4G278OPdH6nTFPDNQ'
92
93
  Rmega.download(public_url, '~/Downloads')
93
94
  ```
94
95
 
@@ -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!n4kqzbJooqc3o_s96PZjN1tEJzQ4QQwskHf7YqKa'\n"
20
- opts.banner << "\t#{File.basename(__FILE__)} https://www.reddit.com/r/megalinks\n"
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 = [cli_options[:url]]
42
+ urls = []
40
43
 
41
- unless mega_url?(cli_options[:url])
42
- html = Rmega::Session.new.http_get_content(cli_options[:url])
43
- urls = html.scan(Rmega::Nodes::Factory::URL_REGEXP).flatten.uniq
44
- raise("Nothing to download") if urls.empty?
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
- print "[#{index+1}/#{urls.size}] " if urls.size > 1
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
@@ -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
@@ -7,6 +7,8 @@ require 'base64'
7
7
  require 'openssl'
8
8
  require 'digest/md5'
9
9
  require 'json'
10
+ require 'securerandom'
11
+ require 'pbkdf2'
10
12
 
11
13
  # Used only in specs
12
14
  require 'yaml'
@@ -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.co.nz/#doc (section 11)
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)
@@ -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
- cli_options = YAML.load_file(configuration_filepath)
32
- cli_options.keys.each { |k| cli_options[k.to_sym] = cli_options.delete(k) }
33
- puts "Loaded configuration file #{configuration_filepath}" if cli_options[:debug]
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) if cli_options[:debug]
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") { |n|
48
- cli_options[:thread_pool_size] = n.to_i
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!(0)
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
@@ -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
- 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
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
@@ -4,4 +4,14 @@ module Rmega
4
4
 
5
5
  class TemporaryServerError < StandardError
6
6
  end
7
+
8
+ class BandwidthLimitExceeded < StandardError
9
+ def initialize(*args)
10
+ if args.any?
11
+ super
12
+ else
13
+ super("Transfer quota exceeded")
14
+ end
15
+ end
16
+ end
7
17
  end
@@ -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
- return send_http_request(uri, req).body
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
- response = send_http_request(uri, req)
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 send_http_request(uri, req)
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
- def apply_http_options(http)
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: 'Download', filename: self.name)
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
@@ -7,7 +7,7 @@ module Rmega
7
7
  node_key = NodeKey.random
8
8
 
9
9
  # encrypt attributes
10
- _attr = serialize_attributes(:n => name.strip)
10
+ _attr = serialize_attributes(:n => Utils.utf8(name).strip)
11
11
  _attr = aes_cbc_encrypt(node_key.aes_key, _attr)
12
12
 
13
13
  # Encrypt node key
@@ -16,12 +16,25 @@ module Rmega
16
16
  module Factory
17
17
  extend self
18
18
 
19
- URL_REGEXP = /(http.:\/\/[w\.]*mega\.[a-z\.]+\/\#[A-Z0-9\_\-\!\=]+)/i
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
- FOLDER_URL_REGEXP = /\#\F/
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.to_s =~ URL_REGEXP
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.strip.split('!')[1, 2]
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 =~ FOLDER_URL_REGEXP
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 { |data| Nodes::Factory.build(session, data) }
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)
@@ -5,7 +5,10 @@ module Rmega
5
5
  include Downloadable
6
6
 
7
7
  def storage_url
8
- @storage_url ||= data['g'] || request(a: 'g', g: 1, n: handle)['g']
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
@@ -21,7 +21,7 @@ module Rmega
21
21
  end
22
22
 
23
23
  def public_url
24
- @public_url ||= "https://mega.co.nz/#!#{public_handle}!#{Utils.base64urlencode(decrypted_file_key)}"
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.size % 16)))
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
- Utils.base64urldecode(public_url.split('!').last)
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
@@ -72,6 +72,7 @@ module Rmega
72
72
 
73
73
  def increment(bytes, options = {})
74
74
  @mutex.synchronize do
75
+ @caption = options[:caption] if options[:caption]
75
76
  @bytes += bytes
76
77
  @real_bytes += bytes unless options[:real] == false
77
78
  show
@@ -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
- password_hash = hash_password(password)
92
- u_hash = user_hash(password_hash, email.strip.downcase)
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
- resp = request(a: 'us', user: email.strip, uh: u_hash)
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'])
@@ -40,5 +40,12 @@ module Rmega
40
40
  ary
41
41
  end.map(&:chr).join
42
42
  end
43
+
44
+ def utf8(s)
45
+ str = s.dup
46
+ str.force_encoding("UTF-8")
47
+ str.encode!("UTF-8", invalid: :replace, replace: "-")
48
+ return str
49
+ end
43
50
  end
44
51
  end
@@ -1,4 +1,4 @@
1
1
  module Rmega
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.7"
3
3
  HOMEPAGE = "https://github.com/topac/rmega"
4
4
  end
@@ -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 = ["dani.m.mobile@gmail.com"]
7
- gem.description = %q{mega.co.nz ruby api}
8
- gem.summary = %q{mega.co.nz ruby api}
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,7 @@ describe 'File upload' do
8
8
  @storage = login
9
9
  end
10
10
 
11
- [12, 6_000].each do |size|
11
+ [12, 6_000, 2_000_000].each do |size|
12
12
 
13
13
  context "when a file (#{size} bytes) is uploaded" do
14
14
 
@@ -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
 
@@ -23,7 +23,7 @@ def login
23
23
  end
24
24
 
25
25
  def temp_folder
26
- Dir.tmpdir
26
+ $temp_folder ||= "#{Dir.tmpdir}/#{SecureRandom.hex(10)}"
27
27
  end
28
28
 
29
29
  RSpec.configure do |config|
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.1
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: 2015-09-20 00:00:00.000000000 Z
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.co.nz ruby api
69
+ description: mega.nz ruby api
56
70
  email:
57
- - dani.m.mobile@gmail.com
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
- rubyforge_project:
142
- rubygems_version: 2.4.6
156
+ rubygems_version: 3.1.2
143
157
  signing_key:
144
158
  specification_version: 4
145
- summary: mega.co.nz ruby api
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