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