Megar <img src=“https://secure.travis-ci.org/tardate/megar.png” />

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: '[email protected]', password: 'my_password')

It’s possible to delay the authentication

# create a session with deferred authentication
session = Megar::Session.new(email: '[email protected]', 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 protected] --password=mypassword
Connecting to mega as [email protected]..
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 protected] --password=mypassword ls
Connecting to mega as [email protected]..
              39 bytes  74ZTXbyR    hello_mega.txt
          137080 bytes  L0xHwayA    mega.png

CLI: How to download a file

Use the get command:

$ megar [email protected] --password=mypassword get L0xHwayA
Connecting to mega as [email protected]..
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 protected] --password=mypassword put ../path/to/my_file.png
Connecting to mega as [email protected]..
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 protected] --password=mypassword put *.png
Connecting to mega as [email protected]..
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 protected] password=mypassword

Generating crypto_expectations for [email protected]...

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.

References

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 © 2013 Paul Gallagher. See LICENSE for further details.