rmega 0.2.6 → 0.3.1

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
2
  SHA256:
3
- metadata.gz: 5ed425ea5b0da557c00e5e2abd80b00ddb72f7d32e4a12c6ee3e79d65a0e4a34
4
- data.tar.gz: d4ddf7dcb5e5b40433da1e03eec12ccf91c3b400009493158c2b5055c5bc6119
3
+ metadata.gz: ee612b39a7d211f89deb0c30044ec6d75ae231da61314e73432389f96e7f7a0b
4
+ data.tar.gz: 1742781c48533cea360740dc14f9b3d67bec0933e671605e3df4e0d4d2608a68
5
5
  SHA512:
6
- metadata.gz: 466832e6ceedaf3df21715c17bec1a507dde658db7821aae46f6427bda5e1031ed34d454f97b0ef90b4a5ee9f86da877af51cfbfd78f0b64cb36fd93be957b2a
7
- data.tar.gz: f3928e47b107ab5693c3daa73c9d6725e94f622e539e4d30e96b3fff8c2af0b4394e38068b5f7508c6dc4b3802c45a5a3c8014ba2a967ba3e8da6b56fa07fa2f
6
+ metadata.gz: 5cf7d33fe73f7626160d4045f27e48b546943fc492dca38c8f528f7ec0b3c07ed341527a8c17305b8448c08efd2aea3d44ecf517a1aaf9684f172830b6586c4a
7
+ data.tar.gz: f919c67bd100e4e94c7f3dd4c319d0df784173a3a7f90d45b24914b4b8fa474196b4a858f89e414abfaa3301eac2e9c0bf3602d1f3d870010ec1c8c8a1d1511f
@@ -0,0 +1,52 @@
1
+ # Use the latest 2.1 version of CircleCI pipeline process engine.
2
+ # See: https://circleci.com/docs/2.0/configuration-reference
3
+ version: 2.1
4
+
5
+ # Orbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects.
6
+ # See: https://circleci.com/docs/2.0/orb-intro/
7
+ orbs:
8
+ ruby: circleci/ruby@0.1.2
9
+
10
+ # Define a job to be invoked later in a workflow.
11
+ # See: https://circleci.com/docs/2.0/configuration-reference/#jobs
12
+ jobs:
13
+ test_on_ruby_latest:
14
+ docker:
15
+ - image: circleci/ruby
16
+ executor: ruby/default
17
+ steps:
18
+ - checkout
19
+ - run: 'ruby -v'
20
+ - run: 'bundle install'
21
+ - run: 'bundle exec rspec'
22
+ test_on_ruby25:
23
+ docker:
24
+ - image: 'circleci/ruby:2.5'
25
+ executor: ruby/default
26
+ steps:
27
+ - checkout
28
+ - run: 'ruby -v'
29
+ - run: 'bundle install'
30
+ - run: 'bundle exec rspec'
31
+ test_on_ruby21:
32
+ docker:
33
+ - image: 'circleci/ruby:2.1'
34
+ executor: ruby/default
35
+ steps:
36
+ - checkout
37
+ - run: 'ruby -v'
38
+ - run: 'bundle install'
39
+ - run: 'bundle exec rspec'
40
+
41
+ # Invoke jobs via workflows
42
+ # See: https://circleci.com/docs/2.0/configuration-reference/#workflows
43
+ workflows:
44
+ test: # This is the name of the workflow, feel free to change it to better match your workflow.
45
+ # Inside the workflow, you define the jobs you want to run.
46
+ jobs:
47
+ - test_on_ruby21:
48
+ context: rmega_secrets
49
+ - test_on_ruby25:
50
+ context: rmega_secrets
51
+ - test_on_ruby_latest:
52
+ context: rmega_secrets
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
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
+
1
8
  ## 0.2.6
2
9
 
3
10
  ### New Features
data/README.md CHANGED
@@ -1,10 +1,11 @@
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)
2
+ [![rmega](https://circleci.com/gh/topac/rmega.svg?style=svg)](https://circleci.com/gh/topac/rmega)
3
+
3
4
 
4
5
  # rmega
5
6
 
6
7
  Pure ruby library for <img src="https://mega.co.nz/favicon.ico" alt=""/> **MEGA** [https://mega.nz/](https://mega.nz/).
7
- Works on Linux and OSX with Ruby 1.9.3+.
8
+ Works on Linux and OSX with Ruby 2.1+.
8
9
 
9
10
  ## Installation
10
11
 
@@ -93,7 +94,7 @@ public_url = 'https://mega.nz/file/MAkg2Iab#bc9Y2U6d93IlRRKVYpcC9hLZjS4G278OPdH6
93
94
  Rmega.download(public_url, '~/Downloads')
94
95
  ```
95
96
 
96
- ### Upload
97
+ ### Upload a file
97
98
 
98
99
  ```ruby
99
100
  # Upload a file to a specific folder
@@ -104,6 +105,17 @@ folder.upload("~/Downloads/my_file.txt")
104
105
  storage.root.upload("~/Downloads/my_other_file.txt")
105
106
  ```
106
107
 
108
+ ### Upload a directory
109
+
110
+ ```ruby
111
+ # Upload a directory to a specific folder
112
+ folder = storage.root.folders[3]
113
+ folder.upload_dir("~/Downloads/my_directory")
114
+
115
+ # Upload a directory to the root folder
116
+ storage.root.upload_dir("~/Downloads/my_other_directory")
117
+ ```
118
+
107
119
  ### Creating a folder
108
120
 
109
121
  ```ruby
@@ -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)
@@ -0,0 +1,98 @@
1
+ module Rmega
2
+ module ConnPool
3
+ include Loggable
4
+ include Options
5
+ extend MonitorMixin
6
+
7
+ # url: URI / String
8
+ # options: any options that Net::HTTP.new accepts
9
+ def self.get(url, options = {})
10
+ uri = url.is_a?(URI) ? url : URI(url)
11
+ connection_manager.get_client(uri, options)
12
+ end
13
+
14
+ def self.connection_manager
15
+ synchronize do
16
+ @connection_managers ||= []
17
+
18
+ # we first clear all old ones
19
+ removed = @connection_managers.reject!(&:stale?)
20
+ (removed || []).each(&:close_connections!)
21
+
22
+ # get the manager
23
+ Thread.current[:http_connection_manager] ||= self.synchronize do
24
+ manager = ConnectionManager.new
25
+ @connection_managers << manager
26
+ manager
27
+ end
28
+ end
29
+ end
30
+
31
+ # @see https://www.dmitry-ishkov.com/2021/05/turbocharge-http-requests-in-ruby.html
32
+ class ConnectionManager
33
+ include MonitorMixin
34
+ # if a client wasn't used within this time range
35
+ # it gets removed from the cache and the connection closed.
36
+ # This helps to make sure there are no memory leaks.
37
+ STALE_AFTER = 180
38
+
39
+ # Seconds to reuse the connection of the previous request.
40
+ # If the idle time is less than this Keep-Alive Timeout, Net::HTTP reuses the TCP/IP socket used by the previous communication. Source: Ruby docs
41
+ KEEP_ALIVE_TIMEOUT = 300
42
+
43
+ # KEEP_ALIVE_TIMEOUT vs STALE_AFTER
44
+ # STALE_AFTER - how long an Net::HTTP client object is cached in ruby
45
+ # KEEP_ALIVE_TIMEOUT - how long that client keeps TCP/IP socket open.
46
+
47
+ attr_accessor :clients_store, :last_used
48
+
49
+ def initialize(*args)
50
+ super
51
+ self.clients_store = {}
52
+ self.last_used = Time.now
53
+ end
54
+
55
+ def get_client(uri, options)
56
+ synchronize do
57
+ # refresh the last time a client was used,
58
+ # this prevents the client from becoming stale
59
+ self.last_used = Time.now
60
+
61
+ # we use params as a cache key for clients.
62
+ # 2 connections to the same host but with different
63
+ # options are going to use different HTTP clients
64
+ params = [uri.host, uri.port, options]
65
+ client = clients_store[params]
66
+
67
+ return client if client
68
+
69
+ client = ::Net::HTTP.new(uri.host, uri.port)
70
+ client.keep_alive_timeout = KEEP_ALIVE_TIMEOUT
71
+
72
+ # set SSL to true if a scheme is https
73
+ client.use_ssl = uri.scheme == "https"
74
+
75
+ # open connection
76
+ client.start
77
+
78
+ # cache the client
79
+ clients_store[params] = client
80
+
81
+ client
82
+ end
83
+ end
84
+
85
+ # close connections for each client
86
+ def close_connections!
87
+ synchronize do
88
+ clients_store.values.each(&:finish)
89
+ self.clients_store = {}
90
+ end
91
+ end
92
+
93
+ def stale?
94
+ Time.now - last_used > STALE_AFTER
95
+ end
96
+ end
97
+ end
98
+ end
data/lib/rmega/net.rb CHANGED
@@ -33,9 +33,6 @@ module Rmega
33
33
  req.body = data
34
34
  logger.debug("REQ POST #{url} #{cut_string(data)}")
35
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
36
  req['Connection'] = 'keep-alive'
40
37
 
41
38
  response = net_http(uri).request(req)
@@ -46,8 +43,7 @@ module Rmega
46
43
  private
47
44
 
48
45
  def net_http(uri)
49
- http = ::Net::HTTP.new(uri.host, uri.port)
50
- http.use_ssl = true if uri.scheme == 'https'
46
+ http = Rmega::ConnPool.get(uri)
51
47
 
52
48
  # apply common http options
53
49
  http.proxy_from_env = false if options.http_proxy_address
@@ -95,7 +95,7 @@ module Rmega
95
95
  end
96
96
 
97
97
  # waits for the last running threads to finish
98
- pool.shutdown
98
+ pool.wait_done
99
99
 
100
100
  if options.file_integrity_check
101
101
  file_mac = aes_cbc_mac(@node_key.aes_key, chunk_macs.sort.map(&:last).join, "\x0"*16)
@@ -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
@@ -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
 
@@ -66,10 +66,10 @@ module Rmega
66
66
  end
67
67
  end
68
68
 
69
- pool.shutdown
69
+ pool.wait_done
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
@@ -85,6 +85,20 @@ module Rmega
85
85
  ensure
86
86
  file.close if file
87
87
  end
88
+
89
+ def upload_dir(dir)
90
+ return create_folder(::File.basename(dir)).upload_dir(dir) unless name == ::File.basename(dir)
91
+
92
+ ::Dir["#{dir}/*"].each do |path|
93
+ name = ::File.basename(path)
94
+
95
+ if ::File.directory?(path)
96
+ create_folder(name).upload_dir(path)
97
+ elsif ::File.size(path) > 0
98
+ upload(path)
99
+ end
100
+ end
101
+ end
88
102
  end
89
103
  end
90
104
  end
data/lib/rmega/pool.rb CHANGED
@@ -1,70 +1,58 @@
1
1
  module Rmega
2
2
  class Pool
3
3
  include Options
4
-
4
+
5
5
  def initialize
6
6
  threads_raises_exceptions
7
7
 
8
- @mutex = Mutex.new
9
- @resource = ConditionVariable.new
10
- @max = options.thread_pool_size
8
+ @queue = Queue.new
9
+ @queue_closed = false
10
+ @threads = []
11
+ @cv = ConditionVariable.new
12
+ @working_threads = 0
13
+
14
+ options.thread_pool_size.times do
15
+ @threads << Thread.new do
16
+ while proc = @queue.pop
17
+ mutex.synchronize do
18
+ @working_threads += 1
19
+ end
20
+
21
+ proc.call
22
+
23
+ mutex.synchronize do
24
+ @working_threads -= 1
25
+
26
+ if @queue_closed and @queue.empty? and @working_threads == 0
27
+ @cv.signal
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
11
34
 
12
- @running = []
13
- @queue = []
35
+ def mutex
36
+ @mutex ||= Mutex.new
14
37
  end
15
38
 
16
39
  def threads_raises_exceptions
17
40
  Thread.abort_on_exception = true
18
41
  end
19
42
 
20
- def defer(&block)
21
- synchronize { @queue << block }
22
- process_queue
43
+ def process(&block)
44
+ @queue << block
23
45
  end
24
-
25
- alias :process :defer
26
-
46
+
27
47
  def wait_done
28
- return if done?
29
- synchronize { @resource.wait(@mutex) }
30
- end
31
-
32
- alias :shutdown :wait_done
33
-
34
- private
35
-
36
- def synchronize(&block)
37
- @mutex.synchronize(&block)
38
- end
48
+ @queue.close if @queue.respond_to?(:close)
49
+ @queue_closed = true
39
50
 
40
- def process_queue
41
- synchronize do
42
- if @running.size < @max
43
- proc = @queue.shift
44
- @running << Thread.new(&thread_proc(&proc)) if proc
45
- end
51
+ mutex.synchronize do
52
+ @cv.wait(mutex)
46
53
  end
47
- end
48
-
49
- def done?
50
- synchronize { @queue.empty? && @running.empty? }
51
- end
52
54
 
53
- def signal_done
54
- synchronize { @resource.signal }
55
- end
56
-
57
- def thread_terminated
58
- synchronize { @running.reject! { |thread| thread == Thread.current } }
59
- end
60
-
61
- def thread_proc(&block)
62
- Proc.new do
63
- block.call
64
- thread_terminated
65
- process_queue
66
- signal_done if done?
67
- end
55
+ @threads.each(&:kill)
68
56
  end
69
57
  end
70
58
  end
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
- 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'])
data/lib/rmega/utils.rb CHANGED
@@ -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
data/lib/rmega/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Rmega
2
- VERSION = "0.2.6"
2
+ VERSION = "0.3.1"
3
3
  HOMEPAGE = "https://github.com/topac/rmega"
4
4
  end
data/lib/rmega.rb CHANGED
@@ -8,6 +8,7 @@ require 'openssl'
8
8
  require 'digest/md5'
9
9
  require 'json'
10
10
  require 'securerandom'
11
+ require 'pbkdf2'
11
12
 
12
13
  # Used only in specs
13
14
  require 'yaml'
@@ -21,6 +22,7 @@ require 'rmega/not_inspectable'
21
22
  require 'rmega/errors'
22
23
  require 'rmega/api_response'
23
24
  require 'rmega/utils'
25
+ require 'rmega/conn_pool'
24
26
  require 'rmega/net'
25
27
  require 'rmega/pool'
26
28
  require 'rmega/progress'
data/rmega.gemspec CHANGED
@@ -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"
@@ -4,7 +4,7 @@ describe 'File download' do
4
4
 
5
5
  context 'given a public mega url (a small file)' do
6
6
 
7
- let(:url) { 'https://mega.nz/#!QQhADCbL!vUY_phwxvkC004t5NKx7vynL16SvFfHYFkiX5vUlgjQ' }
7
+ let(:url) { 'https://mega.nz/file/muAVRRbb#zp9dvPvoVck8-4IwTazqsUqol6yiUK7kwLWOwrD8Jqo' }
8
8
 
9
9
  it 'downloads the related file' do
10
10
  Rmega.download(url, temp_folder)
@@ -15,13 +15,13 @@ describe 'File download' do
15
15
 
16
16
  context 'given a public mega url (a big file)' do
17
17
 
18
- let(:url) { 'https://mega.nz/#!oAhCnBKR!CPeG8X92nBjvFsBF9EprZNW_TqIUwItHMkF9G2IZEIo' }
18
+ let(:url) { 'https://mega.nz/file/3zpE1ToL#B1L4o8POE4tER4h1tyVoGNxaXFhbjwfxhe3Eyp9nrN8' }
19
19
 
20
20
  it 'downloads the related file' do
21
21
  Rmega.download(url, temp_folder)
22
- related_file = File.join(temp_folder, 'testfile_big_15mb.txt')
22
+ related_file = File.join(temp_folder, 'testfile_big_15mb.binary')
23
23
  md5 = Digest::MD5.file(related_file).hexdigest
24
- expect(md5).to eq("0451dc82ac003dbef703342e40a1b8f6")
24
+ expect(md5).to eq("a92ec9994911866e3ea31aa1d914ac23")
25
25
  end
26
26
  end
27
27
  end
@@ -4,14 +4,14 @@ describe 'Folder download' do
4
4
 
5
5
  context 'given a public mega url (folder)' do
6
6
 
7
- let(:url) { 'https://mega.nz/#F!oQYEUaBD!QtYCjQDbBzefFeIM994FIg' }
7
+ let(:url) { 'https://mega.nz/folder/GvgkUIIK#v2hd_5GSvciGKazNeWSa6A' }
8
8
 
9
9
  it 'downloads the related file' do
10
10
  Rmega.download(url, temp_folder)
11
- list = Dir["#{temp_folder}/test_folder/**/*"].map do |p|
12
- p.gsub("#{temp_folder}/test_folder/", "")
11
+ list = Dir["#{temp_folder}/another_test_folder/**/*"].map do |p|
12
+ p.gsub("#{temp_folder}/another_test_folder/", "")
13
13
  end
14
- expect(list.sort).to eq(["a.txt", "b.txt", "c", "c/c.txt"])
14
+ expect(list.sort).to eq(["b.txt", "c", "c/c.txt"])
15
15
  end
16
16
  end
17
17
  end
@@ -0,0 +1,48 @@
1
+ require 'integration_spec_helper'
2
+
3
+ describe 'Folder upload' do
4
+
5
+ if account?
6
+
7
+ before(:all) do
8
+ @storage = login
9
+ end
10
+
11
+ context "when a folder is uploaded" do
12
+
13
+ let(:folder) { SecureRandom.hex(6) }
14
+ let(:subfolder_empty) { SecureRandom.hex(6) }
15
+ let(:subfolder_with_content) { SecureRandom.hex(6) }
16
+ let(:file1) { SecureRandom.hex(6) }
17
+ let(:file2) { SecureRandom.hex(6) }
18
+
19
+ it 'all its content is found' do
20
+ Dir.mkdir("#{temp_folder}/#{folder}")
21
+ Dir.mkdir("#{temp_folder}/#{folder}/#{subfolder_empty}")
22
+ Dir.mkdir("#{temp_folder}/#{folder}/#{subfolder_with_content}")
23
+ File.write("#{temp_folder}/#{folder}/#{subfolder_with_content}/#{file1}", SecureRandom.random_bytes(1000))
24
+ File.write("#{temp_folder}/#{folder}/#{subfolder_with_content}/#{file2}", SecureRandom.random_bytes(2000))
25
+
26
+ @storage.root.upload_dir("#{temp_folder}/#{folder}")
27
+
28
+ uploaded_folder = @storage.root.folders.find { |f| f.name == folder }
29
+
30
+ expect(uploaded_folder.folders.size).to eq 2
31
+ folder1 = uploaded_folder.folders.find {|f| f.name == subfolder_empty}
32
+ folder2 = uploaded_folder.folders.find {|f| f.name == subfolder_with_content}
33
+
34
+ expect(folder1).not_to be_nil
35
+ expect(folder2).not_to be_nil
36
+
37
+ expect(folder1.folders).to be_empty
38
+ expect(folder1.files).to be_empty
39
+
40
+ expect(folder2.folders).to be_empty
41
+ expect(folder2.files.find { |f| f.name == file1 }).not_to be_nil
42
+ expect(folder2.files.find { |f| f.name == file2 }).not_to be_nil
43
+
44
+ uploaded_folder.delete
45
+ end
46
+ end
47
+ end
48
+ end
@@ -3,7 +3,7 @@ require 'integration_spec_helper'
3
3
  module Rmega
4
4
  describe 'Resumable download' do
5
5
 
6
- let(:download_url) { 'https://mega.nz/#!oAhCnBKR!CPeG8X92nBjvFsBF9EprZNW_TqIUwItHMkF9G2IZEIo' }
6
+ let(:download_url) { 'https://mega.nz/file/3zpE1ToL#B1L4o8POE4tER4h1tyVoGNxaXFhbjwfxhe3Eyp9nrN8' }
7
7
 
8
8
  let(:destination_file) { "#{temp_folder}/temp.txt" }
9
9
 
@@ -15,18 +15,17 @@ module Rmega
15
15
 
16
16
  it 'resume a download of a file' do
17
17
  node = Nodes::Factory.build_from_url(download_url)
18
- content = nil
19
-
18
+
20
19
  thread = Thread.new do
21
20
  node.download(destination_file)
22
21
  end
23
-
22
+
24
23
  loop do
25
24
  next unless File.exists?(destination_file)
25
+ content = nil
26
26
  node.file_io_synchronize { content = File.read(destination_file) }
27
- content.strip!
28
- break if content.size > 5_000_000
29
- sleep(0.5)
27
+ break if content.force_encoding("BINARY").strip.size > 2_000_000
28
+ sleep(0.1)
30
29
  end
31
30
 
32
31
  thread.kill
@@ -36,7 +35,7 @@ module Rmega
36
35
  node.download(destination_file)
37
36
 
38
37
  md5 = Digest::MD5.file(destination_file).hexdigest
39
- expect(md5).to eq("0451dc82ac003dbef703342e40a1b8f6")
38
+ expect(md5).to eq("a92ec9994911866e3ea31aa1d914ac23")
40
39
  end
41
40
  end
42
41
  end
@@ -2,7 +2,7 @@ require 'integration_spec_helper'
2
2
 
3
3
  describe "rmega-dl" do
4
4
 
5
- let(:url) { 'https://mega.nz/#!QQhADCbL!vUY_phwxvkC004t5NKx7vynL16SvFfHYFkiX5vUlgjQ' }
5
+ let(:url) { 'https://mega.nz/file/muAVRRbb#zp9dvPvoVck8-4IwTazqsUqol6yiUK7kwLWOwrD8Jqo' }
6
6
 
7
7
  def call(*args)
8
8
  `bundle exec ./bin/rmega-dl #{args.join(' ')}`
@@ -26,9 +26,9 @@ describe "rmega-dl" do
26
26
  if account?
27
27
  context "given an account and a path" do
28
28
  it "downloads a file" do
29
- call("/test_folder/a.txt -u #{account['email']} --pass #{account['password']} -o #{temp_folder}")
30
- downloaded_file = "#{temp_folder}/a.txt"
31
- expect(File.read(downloaded_file)).to eq "hello\n"
29
+ call("/test_folder/b.txt -u #{account['email']} --pass '#{account['password']}' -o #{temp_folder}")
30
+ downloaded_file = "#{temp_folder}/b.txt"
31
+ expect(File.read(downloaded_file)).to eq "foo\n"
32
32
  end
33
33
  end
34
34
  end
@@ -38,14 +38,14 @@ describe "rmega-up" do
38
38
  if account?
39
39
  context "when the remote path is missing" do
40
40
  it "fails" do
41
- resp = call("#{filepath} -u #{account['email']} --pass #{account['password']} -r /foobar")
41
+ resp = call("#{filepath} -u #{account['email']} --pass '#{account['password']}' -r /foobar")
42
42
  expect(resp).to match(/error/i)
43
43
  end
44
44
  end
45
45
 
46
46
  context "without specifying a remote folder" do
47
47
  it "uploads a file to the root node" do
48
- call("#{filepath} -u #{account['email']} --pass #{account['password']}")
48
+ call("#{filepath} -u #{account['email']} --pass '#{account['password']}'")
49
49
  storage = login
50
50
  node = storage.root.files.find { |f| f.name == filename }
51
51
  node.delete if node
@@ -55,7 +55,7 @@ describe "rmega-up" do
55
55
 
56
56
  context "when specifying a remote folder" do
57
57
  it "uploads a file into that folder" do
58
- call("#{filepath} -u #{account['email']} --pass #{account['password']} -r test_folder2")
58
+ call("#{filepath} -u #{account['email']} --pass '#{account['password']}' -r test_folder2")
59
59
  storage = login
60
60
  node = storage.root.folders.find { |f| f.name == "test_folder2" }
61
61
  node = node.files.find { |f| f.name == filename }
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.6
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - topac
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-05 00:00:00.000000000 Z
11
+ date: 2023-02-01 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
@@ -61,8 +75,8 @@ executables:
61
75
  extensions: []
62
76
  extra_rdoc_files: []
63
77
  files:
78
+ - ".circleci/config.yml"
64
79
  - ".gitignore"
65
- - ".travis.yml"
66
80
  - CHANGELOG.md
67
81
  - Dockerfile
68
82
  - Gemfile
@@ -75,6 +89,7 @@ files:
75
89
  - lib/rmega.rb
76
90
  - lib/rmega/api_response.rb
77
91
  - lib/rmega/cli.rb
92
+ - lib/rmega/conn_pool.rb
78
93
  - lib/rmega/crypto.rb
79
94
  - lib/rmega/crypto/aes_cbc.rb
80
95
  - lib/rmega/crypto/aes_ctr.rb
@@ -110,6 +125,7 @@ files:
110
125
  - spec/integration/file_upload_spec.rb
111
126
  - spec/integration/folder_download_spec.rb
112
127
  - spec/integration/folder_operations_spec.rb
128
+ - spec/integration/folder_upload_spec.rb
113
129
  - spec/integration/login_spec.rb
114
130
  - spec/integration/resume_download_spec.rb
115
131
  - spec/integration/rmega-dl_spec.rb
@@ -149,6 +165,7 @@ test_files:
149
165
  - spec/integration/file_upload_spec.rb
150
166
  - spec/integration/folder_download_spec.rb
151
167
  - spec/integration/folder_operations_spec.rb
168
+ - spec/integration/folder_upload_spec.rb
152
169
  - spec/integration/login_spec.rb
153
170
  - spec/integration/resume_download_spec.rb
154
171
  - spec/integration/rmega-dl_spec.rb
data/.travis.yml DELETED
@@ -1,11 +0,0 @@
1
- language: ruby
2
- sudo: false
3
- cache: bundler
4
- rvm:
5
- - 1.9.3
6
- - 2.0
7
- - 2.1
8
- - 2.2
9
- - 2.4
10
- - 2.5
11
- - ruby-head