rmega 0.2.0 → 0.2.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
  SHA1:
3
- metadata.gz: 6a223b72cd508e47946e1420769a7246fbeb0d5d
4
- data.tar.gz: 2ee6197d9a398cb44adce16f0c19aead89bbc4f2
3
+ metadata.gz: 137fdede22cdd2e053dea69f98b227700a8b606f
4
+ data.tar.gz: 49ed20b4c7b0aa5419861c59f842f5a35a2a883d
5
5
  SHA512:
6
- metadata.gz: c40f1ab57edec730541e3fe060f1f5352724a72fcff7f526ae5813ef45e53dffc08fb3d39d8b6ff393fcc233429d10232efae42fbde19c9b8081e061c0789dfc
7
- data.tar.gz: 269f261bb5d5d787540e21094c3252dff07edface5a0d899bc9c4c67a6bc9dc52fe830bd75023b456f5cf697872f77f8c0149922f939e2c9df8083ca55ffa78e
6
+ metadata.gz: 0de9a225767de5aeacb5a82629a10df8e8017bd2f49c2514904a9bb59b451d300c554ae990512644b064aa16e24a085989a16aa16a400094b1d8ef3a3c3ff652
7
+ data.tar.gz: 75d935e07f6957227d87800831efa7e07a5966cb134324798e6f5b37b4780e726f97e094d984ab5f137e002a19475fd167bb8142c5af0b4556ee6152907f7531
data/.gitignore CHANGED
@@ -22,5 +22,6 @@ tmp
22
22
  # RVM
23
23
  .rvmrc
24
24
 
25
- # Required to run integration specs
26
- spec/integration/rmega_account.yml
25
+ # Required to run some specs
26
+ account.yaml
27
+ account.yml
data/.travis.yml CHANGED
@@ -1,6 +1,9 @@
1
1
  language: ruby
2
+ sudo: false
3
+ cache: bundler
2
4
  rvm:
3
5
  - 1.9.3
4
- - 2.0.0
5
- - 2.1.2
6
- - 2.2.0
6
+ - 2.0
7
+ - 2.1
8
+ - 2.2
9
+ - ruby-head
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## 0.2.1
2
+
3
+ ### New Features
4
+ * \#14 Files and folders can now be renamed with `Node#rename`
5
+
6
+ ### Changes
7
+ * `rmega-dl`, `rmega-up` commands now properly traverse the cloud storage when searching for a file/folder to download/upload
8
+ * `rmega-dl`: fixed scan for Mega links in a webpage
9
+ * The configuration file (~/.rmega) format is changed (from JSON to YAML)
10
+ * Dropped dependency to ActiveSupport
11
+
1
12
  ## 0.2.0
2
13
 
3
14
  ### New Features
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Daniele Molteni
1
+ Copyright (c) 2015 topac
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,27 +1,36 @@
1
1
  # Rmega
2
2
 
3
- A ruby library for MEGA ([https://mega.co.nz/](https://mega.co.nz/))
4
- Requirements: Ruby 1.9.3+ and OpenSSL 0.9.8r+
3
+ Pure ruby library for <img src="https://mega.co.nz/favicon.ico" alt=""/> **MEGA** [https://mega.nz/](https://mega.nz/).
4
+ Works on Linux and OSX with Ruby 1.9.3+.
5
5
 
6
+ ## Installation
6
7
 
7
- This is the result of a reverse engineering of the MEGA javascript code.
8
+ ```
9
+ gem install rmega
10
+ ```
8
11
 
12
+ ## Command line usage
9
13
 
10
- ## Installation
14
+ Since version 0.2.0 you can use the commands `rmega-dl` and `rmega-up` to easily download and upload files to MEGA.
11
15
 
12
- **Rmega** is distributed via [rubygems.org](https://rubygems.org/).
13
- If you have ruby installed system wide, just type `gem install rmega`.
16
+ * Downloads are resumable
17
+ * HTTP proxy support
18
+ * See the CHANGELOG file for more info
14
19
 
15
- ## Usage
20
+ <img src="https://i.imgur.com/VVl55wj.gif"/>
16
21
 
17
- ```ruby
18
- require 'rmega'
19
- ```
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).
26
+
27
+ ## DSL usage
20
28
 
21
29
  ### Login
22
30
 
23
31
  ```ruby
24
- storage = Rmega.login('your@email.com', 'your_p4ssw0rd')
32
+ require "rmega"
33
+ storage = Rmega.login("your@email.com", "your_password")
25
34
  ```
26
35
 
27
36
  ### Browsing
@@ -139,9 +148,3 @@ end
139
148
  3. Commit your changes (`git commit -am 'Added some feature'`)
140
149
  4. Push to the branch (`git push origin my-new-feature`)
141
150
  5. Create new Pull Request
142
-
143
-
144
- ## Copyright
145
-
146
- Copyright (c) 2013 Daniele Molteni
147
- MIT License
data/TODO.md CHANGED
@@ -1,5 +1,3 @@
1
1
  ## TODO
2
2
 
3
3
  * Add an option to skip mac verification and calculation (upon downloads)
4
-
5
- * Show this gif (https://i.imgur.com/VVl55wj.gif) in the readme?
data/bin/rmega-dl CHANGED
@@ -14,6 +14,11 @@ end
14
14
  OptionParser.new do |opts|
15
15
  opts.banner = "Usage:\n"
16
16
  opts.banner << "\t#{File.basename(__FILE__)} url [options]\n"
17
+ opts.banner << "\t#{File.basename(__FILE__)} path [options]\n"
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"
21
+ opts.banner << "\t#{File.basename(__FILE__)} /remote/docs/myfile.txt -u email@localhost\n"
17
22
  opts.banner << "Options:"
18
23
 
19
24
  opts.on("-o PATH", "--output", "Local destination path") { |path|
@@ -23,25 +28,26 @@ OptionParser.new do |opts|
23
28
  apply_opt_parser_options(opts)
24
29
  end.parse!
25
30
 
26
- rescue_errors_and_inerrupt do
27
- urls = [cli_options[:url]]
28
-
29
- unless mega_url?(cli_options[:url])
30
- urls = scan_mega_urls(Session.new.http_get_content(cli_options[:url])).uniq
31
- raise("Nothing to download") if urls.empty?
32
- end
33
-
34
- urls.each_with_index do |url, index|
35
- node = Rmega::Nodes::Factory.build_from_url(url)
36
-
37
- info = if node.type == :folder
38
- stats = node.storage.stats
39
- "(#{stats[:files]} file#{'s' if stats[:files] > 1}, #{humanize_bytes(stats[:size])})"
31
+ cli_rescue do
32
+ if cli_options[:user]
33
+ session = Rmega::Session.new.login(cli_options[:user], cli_options[:pass] || cli_prompt_password)
34
+ root = session.storage.root
35
+ node = traverse_storage(root, cli_options[:url].dup)
36
+ raise("Node not found - #{cli_options[:url]}") unless node
37
+ node.download(cli_options[:output] || Dir.pwd)
38
+ else
39
+ urls = [cli_options[:url]]
40
+
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?
40
45
  end
41
46
 
42
- puts "[#{index+1}/#{urls.count}] #{node.name} #{info}"
43
-
44
- path = cli_options[:output] || Dir.pwd
45
- node.download(path)
47
+ urls.each_with_index do |url, index|
48
+ node = Rmega::Nodes::Factory.build_from_url(url)
49
+ print "[#{index+1}/#{urls.size}] " if urls.size > 1
50
+ node.download(cli_options[:output] || Dir.pwd)
51
+ end
46
52
  end
47
53
  end
data/bin/rmega-up CHANGED
@@ -14,18 +14,28 @@ end
14
14
  OptionParser.new do |opts|
15
15
  opts.banner = "Usage:\n"
16
16
  opts.banner << "\t#{File.basename(__FILE__)} path [options]\n"
17
+ opts.banner << "Examples:\n"
18
+ opts.banner << "\t#{File.basename(__FILE__)} /local/file.txt -u email@localhost -r /remote/docs\n"
17
19
  opts.banner << "Options:"
18
20
 
21
+ opts.on("-r PATH", "--remote-path", "Remote path") { |path|
22
+ cli_options[:remote_path] = path
23
+ }
24
+
19
25
  apply_opt_parser_options(opts)
20
26
  end.parse!
21
27
 
22
- rescue_errors_and_inerrupt do
28
+ cli_rescue do
23
29
  raise("File not found - #{cli_options[:path]}") unless File.exists?(cli_options[:path])
24
30
 
25
31
  user = cli_options[:user] || raise("User email is required")
26
- pass = cli_options[:pass] ||= cli_prompt_password
32
+ session = Rmega::Session.new.login(user, cli_options[:pass] ||= cli_prompt_password)
27
33
 
28
- session = Rmega::Session.new.login(user, pass)
29
34
  root = session.storage.root
30
- root.upload(cli_options[:path])
35
+ node = traverse_storage(root, cli_options[:remote_path].to_s.dup, :only_folders => true)
36
+
37
+ raise("Node not found - #{cli_options[:remote_path]}") unless node
38
+ raise("Node cannot be a file - #{cli_options[:remote_path]}") if node.type == :file
39
+
40
+ node.upload(cli_options[:path])
31
41
  end
data/lib/rmega/cli.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  require 'optparse'
2
2
  require 'io/console'
3
- require 'active_support/core_ext/hash'
3
+ require 'yaml'
4
4
 
5
5
  module Rmega
6
6
  module CLI
7
7
  module Helpers
8
8
  def cli_options
9
- $cli_options ||= {options: {}}
9
+ $cli_options ||= {}
10
10
  end
11
11
 
12
12
  def cli_prompt_password
@@ -18,10 +18,6 @@ module Rmega
18
18
  return password
19
19
  end
20
20
 
21
- def scan_mega_urls(text)
22
- text.to_s.scan(Nodes::Factory::URL_REGEXP).flatten.map { |s| "https://mega.co.nz/##{s}" }
23
- end
24
-
25
21
  def mega_url?(url)
26
22
  Nodes::Factory.url?(url)
27
23
  end
@@ -30,46 +26,34 @@ module Rmega
30
26
  File.expand_path('~/.rmega')
31
27
  end
32
28
 
33
- def write_configuration_file
34
- opts = {options: cli_options[:options]}
35
- if cli_options[:user]
36
- opts[:user] = cli_options[:user]
37
- opts[:pass] = cli_options[:pass] || cli_prompt_password
38
- end
39
- File.open(configuration_filepath, 'wb') { |file| file.write(opts.to_json) }
40
- FileUtils.chmod(0600, configuration_filepath)
41
- puts "Options saved into #{configuration_filepath}"
42
- end
43
-
44
29
  def read_configuration_file
45
- if File.exists?(configuration_filepath)
46
- opts = JSON.parse(File.read(configuration_filepath))
47
- $cli_options = opts.deep_symbolize_keys.deep_merge(cli_options)
48
- puts "Loaded configuration file #{configuration_filepath}" if cli_options[:debug]
49
- end
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]
50
34
  rescue Exception => ex
51
35
  raise(ex) if cli_options[:debug]
52
36
  end
53
37
 
54
38
  def apply_cli_options
55
- Rmega.logger.level = ::Logger::DEBUG if cli_options[:debug]
56
-
57
- cli_options[:options].each do |key, value|
39
+ cli_options.each do |key, value|
58
40
  Rmega.options.__send__("#{key}=", value)
59
41
  end
42
+ Rmega.logger.level = ::Logger::DEBUG if cli_options[:debug]
43
+ Rmega.options.show_progress = true
60
44
  end
61
45
 
62
46
  def apply_opt_parser_options(opts)
63
47
  opts.on("-t NUM", "--thread_pool_size", "Number of threads to use") { |n|
64
- cli_options[:options][:thread_pool_size] = n.to_i
48
+ cli_options[:thread_pool_size] = n.to_i
65
49
  }
66
50
 
67
51
  opts.on("--proxy-addr ADDRESS", "Http proxy address") { |value|
68
- cli_options[:options][:http_proxy_address] = value
52
+ cli_options[:http_proxy_address] = value
69
53
  }
70
54
 
71
55
  opts.on("--proxy-port PORT", "Http proxy port") { |value|
72
- cli_options[:options][:http_proxy_port] = value.to_i
56
+ cli_options[:http_proxy_port] = value.to_i
73
57
  }
74
58
 
75
59
  opts.on("-u", "--user USER_EMAIL", "User email address") { |value|
@@ -80,10 +64,6 @@ module Rmega
80
64
  cli_options[:pass] = value
81
65
  }
82
66
 
83
- opts.on("--write-cfg", "Write a configuration file with the given options") {
84
- cli_options[:write_cfg] = true
85
- }
86
-
87
67
  opts.on("--debug", "Debug mode") {
88
68
  cli_options[:debug] = true
89
69
  }
@@ -91,29 +71,44 @@ module Rmega
91
71
  opts.on("-v", "--version", "Print the version number") {
92
72
  puts Rmega::VERSION
93
73
  puts Rmega::HOMEPAGE
94
- exit
74
+ exit!(0)
95
75
  }
96
76
  end
97
77
 
98
- def humanize_bytes(*args)
99
- Progress.humanize_bytes(*args)
100
- end
101
-
102
- def rescue_errors_and_inerrupt(&block)
103
- if cli_options[:write_cfg]
104
- write_configuration_file
78
+ def traverse_storage(node, path, opts = {})
79
+ path.gsub!(/^\/|\/$/, "")
80
+ curr_part = path.split("/")[0] || ""
81
+ last_part = (path.split("/")[1..-1] || []).join("/")
82
+
83
+ if curr_part.empty?
84
+ if node.type == :root or node.type == :folder
85
+ return node
86
+ else
87
+ return nil
88
+ end
105
89
  else
106
- read_configuration_file
107
- apply_cli_options
108
- yield
90
+ n = node.folders.find { |n| n.name.casecmp(curr_part).zero? }
91
+ n ||= node.files.find { |n| n.name.casecmp(curr_part).zero? } unless opts[:only_folders]
92
+
93
+ if last_part.empty?
94
+ return n
95
+ else
96
+ return traverse_storage(n, last_part)
97
+ end
109
98
  end
99
+ end
100
+
101
+ def cli_rescue
102
+ read_configuration_file
103
+ apply_cli_options
104
+ yield
110
105
  rescue Interrupt
111
106
  puts "\nInterrupted"
112
107
  rescue Exception => ex
113
108
  if cli_options[:debug]
114
109
  raise(ex)
115
110
  else
116
- puts "\nError: #{ex.message}"
111
+ $stderr.puts "\nERROR: #{ex.message}"
117
112
  end
118
113
  end
119
114
  end
@@ -8,7 +8,9 @@ module Rmega
8
8
  end
9
9
 
10
10
  module Loggable
11
- extend ActiveSupport::Concern
11
+ def self.included(base)
12
+ base.__send__(:extend, ClassMethods)
13
+ end
12
14
 
13
15
  def logger
14
16
  Rmega.logger
@@ -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')
72
+ progress = Progress.new(filesize, caption: 'Download', filename: self.name)
73
73
  pool = Pool.new
74
74
 
75
75
  @resumed_download = allocated?(path)
@@ -7,15 +7,13 @@ module Rmega
7
7
  node_key = NodeKey.random
8
8
 
9
9
  # encrypt attributes
10
- attributes_str = "MEGA"
11
- attributes_str << {n: name.strip}.to_json
12
- attributes_str << ("\x00" * (16 - (attributes_str.size % 16)))
13
- encrypted_attributes = aes_cbc_encrypt(node_key.aes_key, attributes_str)
10
+ _attr = serialize_attributes(:n => name.strip)
11
+ _attr = aes_cbc_encrypt(node_key.aes_key, _attr)
14
12
 
15
13
  # Encrypt node key
16
14
  encrypted_key = aes_ecb_encrypt(session.master_key, node_key.aes_key)
17
15
 
18
- n = [{h: 'xxxxxxxx', t: 1, a: Utils.base64urlencode(encrypted_attributes), k: Utils.base64urlencode(encrypted_key)}]
16
+ n = [{h: 'xxxxxxxx', t: 1, a: Utils.base64urlencode(_attr), k: Utils.base64urlencode(encrypted_key)}]
19
17
  data = session.request(a: 'p', t: handle, n: n)
20
18
  return Folder.new(session, data['f'][0])
21
19
  end
@@ -16,9 +16,9 @@ module Rmega
16
16
  module Factory
17
17
  extend self
18
18
 
19
- URL_REGEXP = /mega\..+\/\#([A-Z0-9\_\-\!\=]+)/i
19
+ URL_REGEXP = /(http.:\/\/[w\.]*mega\.[a-z\.]+\/\#[A-Z0-9\_\-\!\=]+)/i
20
20
 
21
- FOLDER_URL_REGEXP = /mega\..+\/\#\F([A-Z0-9\_\-\!\=]+)/i
21
+ FOLDER_URL_REGEXP = /\#\F/
22
22
 
23
23
  def url?(string)
24
24
  string.to_s =~ URL_REGEXP
@@ -8,7 +8,10 @@ module Rmega
8
8
 
9
9
  attr_reader :data, :session
10
10
 
11
- delegate :request, :shared_keys, :rsa_privk, :master_key, :storage, :to => :session
11
+ # Delegate to :session
12
+ [:request, :shared_keys, :rsa_privk, :master_key, :storage].each do |name|
13
+ __send__(:define_method, name) { |*args| session.__send__(name, *args) }
14
+ end
12
15
 
13
16
  TYPES = {0 => :file, 1 => :folder, 2 => :root, 3 => :inbox, 4 => :trash}
14
17
 
@@ -25,6 +28,30 @@ module Rmega
25
28
  @public_handle ||= request(a: 'l', n: handle)
26
29
  end
27
30
 
31
+ def serialize_attributes(hash)
32
+ str = "MEGA"
33
+ str << hash.to_json
34
+ str << ("\x00" * (16 - (str.size % 16)))
35
+ return str
36
+ end
37
+
38
+ def rename(new_name)
39
+ node_key = NodeKey.load(decrypted_file_key)
40
+
41
+ _attr = serialize_attributes(attributes.merge("n" => new_name))
42
+ _attr = aes_cbc_encrypt(node_key.aes_key, _attr)
43
+ _attr = Utils.base64urlencode(_attr)
44
+
45
+ resp = request(:a => "a", :attr => _attr, :key => Utils.base64urlencode(node_key.aes_key), :n => handle)
46
+
47
+ if resp != 0
48
+ raise("Rename failed")
49
+ else
50
+ @data['a'] = _attr
51
+ return self
52
+ end
53
+ end
54
+
28
55
  def handle
29
56
  data['h']
30
57
  end
@@ -122,7 +149,10 @@ module Rmega
122
149
  encrypted = Utils.base64urldecode(encrypted)
123
150
  encrypted.strip! if encrypted.size % 16 != 0 # Fix possible errors
124
151
  json = aes_cbc_decrypt(node_key.aes_key, encrypted)
125
- JSON.parse json.gsub(/^MEGA/, '').rstrip
152
+ # Remove invalid bytes at the end of the string
153
+ json.strip!
154
+ json.gsub!(/^MEGA\{(.+)\}.*/, '{\1}')
155
+ return JSON.parse(json)
126
156
  end
127
157
 
128
158
  def type
@@ -3,6 +3,12 @@ module Rmega
3
3
  class Root < Node
4
4
  include Expandable
5
5
  include Traversable
6
+
7
+ def download(path)
8
+ children.each do |node|
9
+ node.download(path)
10
+ end
11
+ end
6
12
  end
7
13
  end
8
14
  end
@@ -46,7 +46,7 @@ module Rmega
46
46
  pool = Pool.new
47
47
  read_mutex = Mutex.new
48
48
 
49
- progress = Progress.new(filesize, caption: 'Upload')
49
+ progress = Progress.new(filesize, caption: 'Upload', filename: ::File.basename(path))
50
50
 
51
51
  chunk_macs = {}
52
52
 
@@ -69,10 +69,8 @@ module Rmega
69
69
  pool.shutdown
70
70
 
71
71
  # encrypt attributes
72
- attributes_str = "MEGA"
73
- attributes_str << {n: ::File.basename(path)}.to_json
74
- attributes_str << ("\x00" * (16 - (attributes_str.size % 16)))
75
- encrypted_attributes = aes_cbc_encrypt(rnd_node_key.aes_key, attributes_str)
72
+ _attr = serialize_attributes(:n => ::File.basename(path))
73
+ _attr = aes_cbc_encrypt(rnd_node_key.aes_key, _attr)
76
74
 
77
75
  # Calculate meta_mac
78
76
  file_mac = aes_cbc_mac(rnd_node_key.aes_key, chunk_macs.sort.map(&:last).join, "\x0"*16)
@@ -80,7 +78,7 @@ module Rmega
80
78
  encrypted_key = aes_ecb_encrypt(session.master_key, rnd_node_key.generate)
81
79
 
82
80
  resp = request(a: 'p', t: handle, n: [
83
- {h: file_handle, t: 0, a: Utils.base64urlencode(encrypted_attributes), k: Utils.base64urlencode(encrypted_key)}
81
+ {h: file_handle, t: 0, a: Utils.base64urlencode(_attr), k: Utils.base64urlencode(encrypted_key)}
84
82
  ])
85
83
 
86
84
  return Nodes::Factory.build(session, resp['f'][0])
data/lib/rmega/options.rb CHANGED
@@ -8,7 +8,7 @@ module Rmega
8
8
  http_read_timeout: 180,
9
9
  # http_proxy_address: '127.0.0.1',
10
10
  # http_proxy_port: 8080,
11
- show_progress: true,
11
+ show_progress: false,
12
12
  file_integrity_check: true,
13
13
  api_url: 'https://eu.api.mega.co.nz/cs'
14
14
  }
@@ -19,7 +19,9 @@ module Rmega
19
19
  end
20
20
 
21
21
  module Options
22
- extend ActiveSupport::Concern
22
+ def self.included(base)
23
+ base.__send__(:extend, ClassMethods)
24
+ end
23
25
 
24
26
  def options
25
27
  Rmega.options
@@ -10,6 +10,10 @@ module Rmega
10
10
  @mutex = Mutex.new
11
11
  @start_time = Time.now
12
12
 
13
+ if show? and options[:filename]
14
+ puts options[:filename]
15
+ end
16
+
13
17
  show
14
18
  end
15
19
 
data/lib/rmega/storage.rb CHANGED
@@ -6,7 +6,10 @@ module Rmega
6
6
 
7
7
  attr_reader :session
8
8
 
9
- delegate :master_key, :shared_keys, to: :session
9
+ # Delegate to :session
10
+ [:master_key, :shared_keys].each do |name|
11
+ __send__(:define_method, name) { |*args| session.__send__(name, *args) }
12
+ end
10
13
 
11
14
  def initialize(session)
12
15
  @session = session
data/lib/rmega/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Rmega
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  HOMEPAGE = "https://github.com/topac/rmega"
4
4
  end
data/lib/rmega.rb CHANGED
@@ -6,10 +6,12 @@ require 'net/http'
6
6
  require 'base64'
7
7
  require 'openssl'
8
8
  require 'digest/md5'
9
+ require 'json'
9
10
 
10
- require 'active_support/json'
11
- require 'active_support/concern'
12
- require 'active_support/core_ext/module/delegation'
11
+ # Used only in specs
12
+ require 'yaml'
13
+ require 'tmpdir'
14
+ require 'fileutils'
13
15
 
14
16
  require 'rmega/version'
15
17
  require 'rmega/loggable'
@@ -26,11 +28,6 @@ require 'rmega/session'
26
28
  require 'rmega/storage'
27
29
  require 'rmega/nodes/factory'
28
30
 
29
- # Used only in specs
30
- require 'yaml'
31
- require 'tmpdir'
32
- require 'fileutils'
33
-
34
31
  module Rmega
35
32
  def self.login(email, password)
36
33
  Session.new.login(email, password).storage
data/rmega.gemspec CHANGED
@@ -20,6 +20,4 @@ Gem::Specification.new do |gem|
20
20
  gem.add_development_dependency "pry"
21
21
  gem.add_development_dependency "rspec"
22
22
  gem.add_development_dependency "rake"
23
-
24
- gem.add_dependency "activesupport"
25
23
  end
@@ -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.co.nz/#!MAkg2Iab!bc9Y2U6d93IlRRKVYpcC9hLZjS4G278OPdH6nTFPDNQ' }
7
+ let(:url) { 'https://mega.nz/#!QQhADCbL!vUY_phwxvkC004t5NKx7vynL16SvFfHYFkiX5vUlgjQ' }
8
8
 
9
9
  it 'downloads the related file' do
10
10
  Rmega.download(url, temp_folder)
@@ -15,7 +15,7 @@ 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.co.nz/#!NYVkDaLD!BKyN5SRpOaEtGnTcwiAqcxmJc7p-k0IPWKAW-471KRE' }
18
+ let(:url) { 'https://mega.nz/#!oAhCnBKR!CPeG8X92nBjvFsBF9EprZNW_TqIUwItHMkF9G2IZEIo' }
19
19
 
20
20
  it 'downloads the related file' do
21
21
  Rmega.download(url, temp_folder)
@@ -1,39 +1,46 @@
1
1
  require 'integration_spec_helper'
2
2
 
3
- describe 'File integrity over upload/download operations' do
3
+ describe 'File integrity' do
4
4
 
5
- if account_file_exists?
5
+ if account?
6
6
 
7
7
  before(:all) do
8
8
  @storage = login
9
9
  end
10
10
 
11
- let(:name) { "test_file" }
11
+ context "when a file is renamed" do
12
12
 
13
- let(:path) { File.join(temp_folder, name)}
13
+ let(:path) { "#{temp_folder}/testfile_#{SecureRandom.hex(6)}" }
14
14
 
15
- [12, 1_024_000].each do |size|
15
+ let(:new_name) { "testfile_#{SecureRandom.hex(6)}" }
16
+
17
+ it 'it does not get corrupted' do
18
+ File.write(path, SecureRandom.hex(24))
19
+ file = @storage.root.upload(path)
20
+ file.rename(new_name)
21
+ expect(file.name).to eq(new_name)
22
+ file = @storage.nodes.find { |n| n.handle == file.handle }
23
+ file.delete
24
+ expect(file.name).to eq(new_name)
25
+ end
26
+ end
27
+
28
+ [12, 6_000].each do |size|
16
29
 
17
30
  context "when a file (#{size} bytes) is uploaded and then downloaded" do
18
31
 
19
- let(:content) { OpenSSL::Random.random_bytes(size) }
32
+ let(:path) { "#{temp_folder}/testfile_#{SecureRandom.hex(6)}" }
20
33
 
21
- let(:content_hash) { Digest::MD5.hexdigest(content) }
34
+ let(:content) { SecureRandom.random_bytes(size) }
22
35
 
23
- before do
24
- File.open(path, 'wb') { |f| f.write(content) }
25
- file = @storage.root.upload(path)
26
- @file = @storage.nodes.find { |n| n.handle == file.handle }
27
- expect(@file.name).to eq(name)
28
- @file.download(path+".downloaded")
29
- end
36
+ let(:content_hash) { Digest::MD5.hexdigest(content) }
30
37
 
31
38
  it 'it does not get corrupted' do
32
- expect(Digest::MD5.file(path+".downloaded").hexdigest).to eq(content_hash)
33
- end
34
-
35
- after do
36
- @file.delete if @file
39
+ File.write(path, content)
40
+ file = @storage.root.upload(path)
41
+ file.download("#{path}.downloaded")
42
+ file.delete
43
+ expect(Digest::MD5.file("#{path}.downloaded").hexdigest).to eq(content_hash)
37
44
  end
38
45
  end
39
46
  end
@@ -2,29 +2,26 @@ require 'integration_spec_helper'
2
2
 
3
3
  describe 'File upload' do
4
4
 
5
- if account_file_exists?
5
+ if account?
6
6
 
7
- before(:all) { @storage = login }
8
-
9
- let(:name) { "test_file" }
7
+ before(:all) do
8
+ @storage = login
9
+ end
10
10
 
11
- let(:path) { File.join(temp_folder, name)}
11
+ [12, 6_000].each do |size|
12
12
 
13
- [12, 1_024_000].each do |size|
14
13
  context "when a file (#{size} bytes) is uploaded" do
15
14
 
16
- before do
17
- File.open(path, 'wb') { |f| f.write(OpenSSL::Random.random_bytes(size)) }
18
- @file = @storage.root.upload(path)
19
- end
15
+ let(:path) { "#{temp_folder}/testfile_#{SecureRandom.hex(6)}" }
20
16
 
21
- it 'it can be found as a file node' do
22
- found_node = @storage.root.files.find { |f| f.handle == @file.handle }
23
- expect(found_node).not_to be_nil
24
- end
17
+ let(:content) { SecureRandom.random_bytes(size) }
25
18
 
26
- after do
27
- @file.delete if @file
19
+ it 'is found' do
20
+ File.write(path, content)
21
+ file = @storage.root.upload(path)
22
+ file = @storage.root.files.find { |f| f.handle == file.handle }
23
+ file.delete
24
+ expect(file).not_to be_nil
28
25
  end
29
26
  end
30
27
  end
@@ -4,7 +4,7 @@ describe 'Folder download' do
4
4
 
5
5
  context 'given a public mega url (folder)' do
6
6
 
7
- let(:url) { 'https://mega.co.nz/#F!IYERlQqa!pvqkX7UUsRGKBs3FWKXzUQ' }
7
+ let(:url) { 'https://mega.nz/#F!oQYEUaBD!QtYCjQDbBzefFeIM994FIg' }
8
8
 
9
9
  it 'downloads the related file' do
10
10
  Rmega.download(url, temp_folder)
@@ -2,16 +2,16 @@ require 'integration_spec_helper'
2
2
 
3
3
  describe 'Folders operations' do
4
4
 
5
- if account_file_exists?
5
+ if account?
6
6
 
7
7
  before(:all) do
8
8
  @storage = login
9
9
  end
10
10
 
11
- let(:name) { "test_folder" }
12
-
13
11
  context 'when #create_folder is called on a node' do
14
12
 
13
+ let(:name) { "testfolder_#{SecureRandom.hex(5)}" }
14
+
15
15
  before do
16
16
  @folder = @storage.root.create_folder(name)
17
17
  end
@@ -27,6 +27,9 @@ describe 'Folders operations' do
27
27
  end
28
28
 
29
29
  context 'searching for a folder by its handle' do
30
+
31
+ let(:name) { "testfolder_#{SecureRandom.hex(5)}" }
32
+
30
33
  before do
31
34
  @folder = @storage.root.create_folder(name)
32
35
  end
@@ -42,6 +45,9 @@ describe 'Folders operations' do
42
45
  end
43
46
 
44
47
  context 'when #create_folder under created folder' do
48
+
49
+ let(:name) { "testfolder_#{SecureRandom.hex(5)}" }
50
+
45
51
  before do
46
52
  @folder = @storage.root.create_folder(name)
47
53
  @sub_folder = @folder.create_folder(name)
@@ -2,7 +2,7 @@ require 'integration_spec_helper'
2
2
 
3
3
  describe 'Login' do
4
4
 
5
- if account_file_exists?
5
+ if account?
6
6
 
7
7
  context 'when email and password are correct' do
8
8
  it 'does not raise erorrs' do
@@ -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.co.nz/#!NYVkDaLD!BKyN5SRpOaEtGnTcwiAqcxmJc7p-k0IPWKAW-471KRE' }
6
+ let(:download_url) { 'https://mega.nz/#!oAhCnBKR!CPeG8X92nBjvFsBF9EprZNW_TqIUwItHMkF9G2IZEIo' }
7
7
 
8
8
  let(:destination_file) { "#{temp_folder}/temp.txt" }
9
9
 
@@ -25,26 +25,14 @@ module Rmega
25
25
  node.file_io_synchronize { content = File.read(destination_file) }
26
26
  content.strip!
27
27
  break if content.size > 5_000_000
28
- sleep(1)
28
+ sleep(0.5)
29
29
  end
30
30
 
31
31
  thread.kill
32
- sleep(1)
33
32
 
34
- thread = Thread.new do
35
- node.download(destination_file)
36
- end
37
-
38
- loop do
39
- # todo: i saw this failing becausa destination_file was missing :/
40
- node.file_io_synchronize { content = File.read(destination_file) }
41
- content.strip!
42
- expect(content.size).to be > 5_000_000
43
- break if content.size >= 15_728_640
44
- sleep(1)
45
- end
33
+ sleep(2)
46
34
 
47
- thread.join
35
+ node.download(destination_file)
48
36
 
49
37
  md5 = Digest::MD5.file(destination_file).hexdigest
50
38
  expect(md5).to eq("0451dc82ac003dbef703342e40a1b8f6")
@@ -0,0 +1,35 @@
1
+ require 'integration_spec_helper'
2
+
3
+ describe "rmega-dl" do
4
+
5
+ let(:url) { 'https://mega.nz/#!QQhADCbL!vUY_phwxvkC004t5NKx7vynL16SvFfHYFkiX5vUlgjQ' }
6
+
7
+ def call(*args)
8
+ `bundle exec ./bin/rmega-dl #{args.join(' ')}`
9
+ end
10
+
11
+ context "without args" do
12
+
13
+ it "shows the help" do
14
+ expect(call).to match(/usage/i)
15
+ end
16
+ end
17
+
18
+ context "given a public link" do
19
+ it "downloads a file" do
20
+ call("'#{url}' -o #{temp_folder}")
21
+ downloaded_file = "#{temp_folder}/testfile.txt"
22
+ expect(File.read(downloaded_file)).to eq "helloworld!\n"
23
+ end
24
+ end
25
+
26
+ if account?
27
+ context "given an account and a path" do
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"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,67 @@
1
+ require 'integration_spec_helper'
2
+ require 'open3'
3
+
4
+ describe "rmega-up" do
5
+ def call(*args)
6
+ Open3.capture2e("bundle exec ./bin/rmega-up #{args.join(' ')}").join("\n")
7
+ end
8
+
9
+ let(:file_content) { SecureRandom.hex(10) }
10
+
11
+ let(:filename) { "testfile_"+SecureRandom.hex(5)+".txt" }
12
+
13
+ let(:filepath) { "#{temp_folder}/#{filename}" }
14
+
15
+ before do
16
+ File.write(filepath, file_content)
17
+ end
18
+
19
+ context "without args" do
20
+
21
+ it "shows the help" do
22
+ expect(call).to match(/usage/i)
23
+ end
24
+ end
25
+
26
+ context "without username" do
27
+ it "fails" do
28
+ expect(call(filepath)).to match(/require/i)
29
+ end
30
+ end
31
+
32
+ context "when the local file is missing" do
33
+ it "fails" do
34
+ expect(call("foobar.txt")).to match(/missing|not found/i)
35
+ end
36
+ end
37
+
38
+ if account?
39
+ context "when the remote path is missing" do
40
+ it "fails" do
41
+ resp = call("#{filepath} -u #{account['email']} --pass #{account['password']} -r /foobar")
42
+ expect(resp).to match(/error/i)
43
+ end
44
+ end
45
+
46
+ context "without specifying a remote folder" do
47
+ it "uploads a file to the root node" do
48
+ call("#{filepath} -u #{account['email']} --pass #{account['password']}")
49
+ storage = login
50
+ node = storage.root.files.find { |f| f.name == filename }
51
+ node.delete if node
52
+ expect(node).not_to be_nil
53
+ end
54
+ end
55
+
56
+ context "when specifying a remote folder" do
57
+ it "uploads a file into that folder" do
58
+ call("#{filepath} -u #{account['email']} --pass #{account['password']} -r test_folder2")
59
+ storage = login
60
+ node = storage.root.folders.find { |f| f.name == "test_folder2" }
61
+ node = node.files.find { |f| f.name == filename }
62
+ node.delete if node
63
+ expect(node).not_to be_nil
64
+ end
65
+ end
66
+ end
67
+ end
@@ -1,15 +1,21 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  def account_file_path
4
- File.join File.dirname(__FILE__), 'integration/rmega_account.yml'
4
+ File.join(File.dirname(__FILE__), "account.yaml")
5
5
  end
6
6
 
7
- def account_file_exists?
8
- File.exists? account_file_path
7
+ def account?
8
+ account
9
9
  end
10
10
 
11
11
  def account
12
- @account ||= YAML.load_file account_file_path
12
+ if ENV["MEGA_EMAIL"] and ENV["MEGA_PASSWORD"]
13
+ {'email' => ENV["MEGA_EMAIL"], 'password' => ENV["MEGA_PASSWORD"]}
14
+ elsif File.exists?(account_file_path)
15
+ YAML.load_file(account_file_path)
16
+ else
17
+ nil
18
+ end
13
19
  end
14
20
 
15
21
  def login
@@ -22,7 +28,6 @@ end
22
28
 
23
29
  RSpec.configure do |config|
24
30
  config.before(:all) do
25
- Rmega.options.show_progress = false
26
31
  FileUtils.mkdir_p(temp_folder)
27
32
  end
28
33
 
metadata CHANGED
@@ -1,69 +1,55 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rmega
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - topac
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-08 00:00:00.000000000 Z
11
+ date: 2015-09-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: activesupport
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - '>='
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - '>='
52
+ - - ">="
67
53
  - !ruby/object:Gem::Version
68
54
  version: '0'
69
55
  description: mega.co.nz ruby api
@@ -75,8 +61,8 @@ executables:
75
61
  extensions: []
76
62
  extra_rdoc_files: []
77
63
  files:
78
- - .gitignore
79
- - .travis.yml
64
+ - ".gitignore"
65
+ - ".travis.yml"
80
66
  - CHANGELOG.md
81
67
  - Gemfile
82
68
  - LICENSE
@@ -125,7 +111,8 @@ files:
125
111
  - spec/integration/folder_operations_spec.rb
126
112
  - spec/integration/login_spec.rb
127
113
  - spec/integration/resume_download_spec.rb
128
- - spec/integration/rmega_account.yml.example
114
+ - spec/integration/rmega-dl_spec.rb
115
+ - spec/integration/rmega-up_spec.rb
129
116
  - spec/integration_spec_helper.rb
130
117
  - spec/rmega/lib/cli_spec.rb
131
118
  - spec/rmega/lib/session_spec.rb
@@ -142,17 +129,17 @@ require_paths:
142
129
  - lib
143
130
  required_ruby_version: !ruby/object:Gem::Requirement
144
131
  requirements:
145
- - - '>='
132
+ - - ">="
146
133
  - !ruby/object:Gem::Version
147
134
  version: 1.9.3
148
135
  required_rubygems_version: !ruby/object:Gem::Requirement
149
136
  requirements:
150
- - - '>='
137
+ - - ">="
151
138
  - !ruby/object:Gem::Version
152
139
  version: '0'
153
140
  requirements: []
154
141
  rubyforge_project:
155
- rubygems_version: 2.4.5
142
+ rubygems_version: 2.4.6
156
143
  signing_key:
157
144
  specification_version: 4
158
145
  summary: mega.co.nz ruby api
@@ -164,7 +151,8 @@ test_files:
164
151
  - spec/integration/folder_operations_spec.rb
165
152
  - spec/integration/login_spec.rb
166
153
  - spec/integration/resume_download_spec.rb
167
- - spec/integration/rmega_account.yml.example
154
+ - spec/integration/rmega-dl_spec.rb
155
+ - spec/integration/rmega-up_spec.rb
168
156
  - spec/integration_spec_helper.rb
169
157
  - spec/rmega/lib/cli_spec.rb
170
158
  - spec/rmega/lib/session_spec.rb
@@ -1,2 +0,0 @@
1
- email: insert_email_here
2
- password: insert_pass_here