Megar (“megaargh!” in pirate-speak) is a Ruby wrapper and command-line (CLI) client for the Mega API.
Megar has coverage of the basic file/folder operations: connect, get file/folder listings and details, upload and download files. Read on - the following sections provide cookbook examples for using Megar within Ruby and from the command-line.
Your help is greatly appreciated on two fronts:
-
Use: It needs hammering to make sure we don’t have the edge-case crypto bugs.
-
Code: there’s more interesting stuff in the API it would be nice to support. And a lot it would be nice to refactor (the priority so far has been to make it work correctly and testably, not fast or elegantly). Tuck in!
NOTE: megar no longer requires OpenSSL 1.0.1+. It is quite happy with any version that implements AES-128-CBC (commonly OpenSSL 0.9.8 or greater, which you most likely already have installed).
Requirements and Known Limitations¶ ↑
Consider this totally alpha at this point, especially since the Mega API has yet to be formally and fully documented.
-
Currently tested with MRI 1.9.3 with OpenSSL 0.9.8+
-
MRI 1.9.2 and 2.x, Rubinius and JRuby 1.9 modes not yet tested. No plans to support 1.8 Ruby modes.
Mega API coverage is far from complete at this point (help appreciated!). Here’s the run-down:
Supported API functions:
-
User/session management: Complete accounts
-
Login session challenge/response
-
Retrieve file and folder nodes
-
Request download URL
-
Download files
-
Download file message authentication codes (MAC)
-
Request upload URL
-
Upload files
Currently unsupported API functions:
-
User/session management: Ephemeral accounts
-
User/session management: Confirming accounts
-
Retrieve user nodes
-
Add/copy nodes
-
Delete node
-
Move node
-
Set node attributes
-
Create/delete public handle
-
Create/modify/delete outgoing share
-
Key handling - Set or request share/node keys
-
Add/update user
-
Get user
-
Retrieve user’s public key
-
Add/update/delete contact
-
Invite user
-
Send confirmation e-mail
-
Obtain user details by invitation code
-
Verify e-mailed confirmation code
-
List user sessions
-
User quota details
-
… and any other API features not explicitly listed above
The Megar Cookbook¶ ↑
How do I install it for normal use?¶ ↑
Add this line to your application’s Gemfile:
gem 'megar'
And then execute:
$ bundle
Or install it yourself as:
$ gem install megar
If you want to do a little hacking around, you can also use megar_rt to bootstrap an isolated environment and be up and running super fast.
How to start an authenticated session¶ ↑
The Megar::Session object is the main entry point for the library. All requests on the Mega API are made in the context of an established session.
# create a session with immediate authentication session = Megar::Session.new(email: 'my@email.com', password: 'my_password')
It’s possible to delay the authentication
# create a session with deferred authentication session = Megar::Session.new(email: 'my@email.com', password: 'my_password', autoconnect: false ) # then explicitly login: session.connect!
How to get a listing of all folders¶ ↑
Folders are accessed through an authenticated Megar::Session
object.
all_folders = session.folders
folders
is an Enumerable collection, so you can iterate as you would expect:
puts session.folders.count session.folders.each do |folder| puts folder.name if parent_folder = folder.parent_folder puts parent_folder.name end end
The find_all_by_type
can be used to find specific node types (1 == normal folder), and find_by_id
method will find the first id match:
session.folders.find_all_by_type(1).first.id => "jtwkAQaK" session.folders.find_by_id("jtwkAQaK").id => "74ZTXbyR"
How to get a handle to the special Mega folders¶ ↑
In addition to any folders you create yourself, Mega provides three standard folders: root (“Cloud Drive”), inbox and trash. Shortcuts are available on the folders collection to grab a handle on these directly:
my_folders = session.folders my_folders.root my_folders.inbox my_folders.trash
What are all the attributes of a folder?¶ ↑
Once you have a folder object (e.g. session.folders.first
), the following are the primary attributes it exposes:
-
id: the unique ID, corresponds to the Mega node handle
-
name: the name of the folder
-
parent_folder: handle to the parent folder (if one is assigned)
-
folders: collection of any child folders available
-
files: collection of any child files available
How to get a listing of all sub-folders of a folder¶ ↑
Folders are hierarchical, and a given folder may have a collection of sub-folders
a_folder = session.folders.first a_folder.folders.each do |folder| puts folder.name folder.parent_folder == a_folder # true! end
How to get a listing of all files in a folder¶ ↑
A folder node provides a files collection
session.folders.first.files.each do |file| puts file.name end
How to get a listing of all files¶ ↑
All files can be accessed directly without needing to navigate via a folder object.
all_files = session.files
files
is an Enumerable collection, so you can iterate as you would expect:
puts session.files.count session.files.each do |file| puts file.id puts file.name puts file.size puts file.parent_folder.name end
The find_by_id
method will find the first id match:
session.files.first.id => "74ZTXbyR" session.files.find_by_id("74ZTXbyR").id => "74ZTXbyR"
What are all the attributes of a file?¶ ↑
Once you have a file object (e.g. session.files.first
), the following are the primary attributes it exposes:
-
id: the unique ID, corresponds to the Mega node handle
-
name: the name of the file
-
size: the size of the file content (in bytes)
-
parent_folder: handle to the parent folder
-
body: get (download) the body content of the file
How to download a file¶ ↑
Once you have a file object (e.g. session.files.first
):
file = session.files.first file.body # returns the full decrypted body content
How to upload a file¶ ↑
Uploading a new file is performed using the create
method of a folder of the files collection. For example:
# get a handle to the file you want to upload e.g. file_handle = Pathname.new('../path/to/my_file.png') # create the file in the root (Cloud Drive) folder: session.root.create( body: file_handle ) # or this also implicitly creates the file in the root folder: session.files.create( body: file_handle ) # or to create the file in a specific folder (e.g. 'My Images'): session.folders.find_by_name('My Images').create( body: file_handle )
The file_handle
can actually be any suitable file name, Pathname or File object.
If you want to change the name of the file as stored in Mega, you can also provide a name
parameter:
session.files.create( name: 'with a new name.png', body: file_handle )
Once a file has been uploaded successfully, it will also be available in the local files catalog:
session.files.create( body: file_handle, name: 'new_file.png' ) => true session.files.find_by_name('new_file.png') => returns new file catalog entry
CLI: How to use the command-line client¶ ↑
The gem installation includes a command-line tool. Call it without parameters and it will show help:
$ megar
All operations require credentials to ba passed on the command line (email and password arguments):
$ megar --email=my@email.com --password=mypassword Connecting to mega as my@email.com.. Connected!
Note: If you just want to do a little hacking around, megar_rt is an easy way to bootstrap an isolated environment and be up and running super fast.
CLI: How to get a file listing¶ ↑
Use the ls
command:
$ megar --email=my@email.com --password=mypassword ls Connecting to mega as my@email.com.. 39 bytes 74ZTXbyR hello_mega.txt 137080 bytes L0xHwayA mega.png
CLI: How to download a file¶ ↑
Use the get
command:
$ megar --email=my@email.com --password=mypassword get L0xHwayA Connecting to mega as my@email.com.. Downloading L0xHwayA to mega.png..
Note: this is very simple interface at the moment:
-
you can only identify the file by the ID (not name)
-
it downloads and saves using the same name as on the server
-
and it doesn’t check if you are overwriting a local file
CLI: How to upload a file¶ ↑
Use the put
command:
$ megar --email=my@email.com --password=mypassword put ../path/to/my_file.png Connecting to mega as my@email.com.. Uploading my_file.png..
This uploads a file into the root folder by default, and keeps the same file name.
Standard command-line expansions work, so to upload multiple files, you can do this:
$ megar --email=my@email.com --password=mypassword put *.png Connecting to mega as my@email.com.. Uploading my_file_1.png.. Uploading my_file_2.png.. (etc for all files matching *.png)
How to run tests¶ ↑
Test are implemented using rspec. Run tests with rake
or rake spec
.
Guard is installed as part of the development dependencies, so to start continuous test execution on file change, start it up with bundle exec guard
.
How to refresh unit test expectations¶ ↑
Unit tests make liberal use of expected responses from the actual Mega API. In order to enable good unit tests without hitting the Mega API for real, test expectations are stored as JSON under ‘spec/fixtures/crypto_expectations’.
A rake task is available to re-generate the expectations:
$ rake gen:crypto_expectations email=my@email.com password=mypassword Generating crypto_expectations for my@email.com... Done! New crypto_expectations for unit test written to: /.../megar/spec/fixtures/crypto_expectations/sample_user.json
NOTE: the email and password are stored in the expectations file, so be careful not to commit and distribute credentials. Change the password on the account first. The tests will still run as intended with the snapshot of crypto details stored in the expectations file.
Checking my OpenSSL installation¶ ↑
Megar will fail with a warning if suitable OpenSSL support is not available. To manually check the OpenSSL version installed:
$ irb -r openssl 1.9.3p327 :001 > OpenSSL::VERSION => "1.1.0" 1.9.3p327 :002 > OpenSSL::OPENSSL_VERSION => "OpenSSL 0.9.8r 8 Feb 2011"
OK, that’s fine. Note that earlier versions of megar required 1.0.1+ but that is no longer the case.
If you do need or want to install or update OpenSSL, you can install from source or rvm. On MacOSX, it is probably not a good idea to upgrade the OS-installed version (too many unintended consequences), but you can also use brew or macports to install a version in parallel.
Installing OpenSSL Using RVM:¶ ↑
See the rvm openssl page for details..
$ rvm pkg install openssl Fetching openssl-1.0.1c.tar.gz to ... $ rvm reinstall 1.9.3 --with-openssl-dir=$rvm_path/usr $ irb -r openssl irb(main):001:0> OpenSSL::VERSION => "1.1.0" irb(main):002:0> OpenSSL::OPENSSL_VERSION => "OpenSSL 1.0.1c 10 May 2012"
Sweet.
What do I do if I get an SSL certificate verification error?¶ ↑
Megar uses SSL to connect to mega.co.nz, and it possible you may see an SSL error like the following when you try to use it:
SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
There are lots of discussion and workarounds for this problem ( google it! ) but fundamentally it is telling you that the CA certificates available to your Ruby installation are out of date. How you fix it depends on the operating system.
Here’s an example of updating the CA certs for CentOS that fixes this problem for megar:
$ ls /etc/pki/tls/certs $ sudo cp /etc/pki/tls/certs/ca-bundle.crt /etc/pki/tls/certs/ca-bundle.crt.backup $ sudo curl http://curl.haxx.se/ca/cacert.pem -o /etc/pki/tls/certs/ca-bundle.crt $ sudo cp /etc/pki/tls/certs/ca-bundle.crt $rvm_path/usr/ssl/cert.pem # required location for rvm ruby
References¶ ↑
-
SpiderOak’s Analysis and Recommendations for the Crypto in Kim Dotcom’s Mega
-
rmega - another Ruby implementation - it seems rmega came to life on a similar timeline to megar. Some different implementation decisions have been made under the covers, so I don’t think we’ll see these projects merge anytime soon. But that means you have more choice;-) My thanks to the rmega folks for inspiring the switch in megar from OpenSSL 1.0.1+ AES CTR implementation to a native version that works with OpenSSL 0.9.8.
Contributing¶ ↑
-
Check out the latest master to make sure the feature hasn’t been implemented or the bug hasn’t been fixed yet
-
Check out the issue tracker to make sure someone already hasn’t requested it and/or contributed it
-
Fork the project
-
Start a feature/bugfix branch
-
Commit and push until you are happy with your contribution
-
Make sure to add tests for it. This is important so it doesn’t get broken in a future version unintentionally.
-
Please try not to mess with the gemspec, Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so it can be cherry-picked around.
Copyright¶ ↑
Copyright © 2013 Paul Gallagher. See LICENSE for further details.