megar 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. data/.gitignore +19 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +11 -0
  5. data/CHANGELOG +5 -0
  6. data/Gemfile +4 -0
  7. data/Guardfile +11 -0
  8. data/LICENSE +22 -0
  9. data/README.rdoc +218 -0
  10. data/Rakefile +33 -0
  11. data/bin/megar +16 -0
  12. data/lib/extensions/math.rb +13 -0
  13. data/lib/js_ref_impl/_README +9 -0
  14. data/lib/js_ref_impl/base64_1.js +83 -0
  15. data/lib/js_ref_impl/crypto_5.js +1795 -0
  16. data/lib/js_ref_impl/download_8.js +867 -0
  17. data/lib/js_ref_impl/hex_1.js +76 -0
  18. data/lib/js_ref_impl/index_9.js +666 -0
  19. data/lib/js_ref_impl/js.manifest +115 -0
  20. data/lib/js_ref_impl/rsa_1.js +456 -0
  21. data/lib/js_ref_impl/sjcl_1.js +1 -0
  22. data/lib/js_ref_impl/upload_10.js +691 -0
  23. data/lib/megar.rb +11 -0
  24. data/lib/megar/catalog.rb +5 -0
  25. data/lib/megar/catalog/catalog_item.rb +90 -0
  26. data/lib/megar/catalog/file.rb +14 -0
  27. data/lib/megar/catalog/files.rb +13 -0
  28. data/lib/megar/catalog/folder.rb +20 -0
  29. data/lib/megar/catalog/folders.rb +28 -0
  30. data/lib/megar/connection.rb +84 -0
  31. data/lib/megar/crypto.rb +399 -0
  32. data/lib/megar/exception.rb +55 -0
  33. data/lib/megar/session.rb +157 -0
  34. data/lib/megar/shell.rb +87 -0
  35. data/lib/megar/version.rb +3 -0
  36. data/megar.gemspec +30 -0
  37. data/spec/fixtures/crypto_expectations/sample_user.json +109 -0
  38. data/spec/spec_helper.rb +24 -0
  39. data/spec/support/crypto_expectations_helper.rb +44 -0
  40. data/spec/support/mocks_helper.rb +22 -0
  41. data/spec/unit/catalog/file_spec.rb +31 -0
  42. data/spec/unit/catalog/files_spec.rb +26 -0
  43. data/spec/unit/catalog/folder_spec.rb +28 -0
  44. data/spec/unit/catalog/folders_spec.rb +49 -0
  45. data/spec/unit/connection_spec.rb +50 -0
  46. data/spec/unit/crypto_spec.rb +476 -0
  47. data/spec/unit/exception_spec.rb +35 -0
  48. data/spec/unit/extensions/math_spec.rb +21 -0
  49. data/spec/unit/session_spec.rb +146 -0
  50. data/spec/unit/shell_spec.rb +18 -0
  51. metadata +238 -0
@@ -0,0 +1,55 @@
1
+ module Megar
2
+
3
+ # A general Megar exception
4
+ class Error < StandardError; end
5
+
6
+ class MegaRequestError < Error
7
+
8
+ # Initialise with +error_code+ returned from Mega
9
+ def initialize(error_code)
10
+ msg = case error_code
11
+ when -1
12
+ "EINTERNAL (-1): An internal error has occurred. Please submit a bug report, detailing the exact circumstances in which this error occurred."
13
+ when -2
14
+ "EARGS (-2): You have passed invalid arguments to this command."
15
+ when -3
16
+ "EAGAIN (-3) (always at the request level): A temporary congestion or server malfunction prevented your request from being processed. No data was altered. Retry. Retries must be spaced with exponential backoff."
17
+ when -4
18
+ "ERATELIMIT (-4): You have exceeded your command weight per time quota. Please wait a few seconds, then try again (this should never happen in sane real-life applications)."
19
+ when -5
20
+ "EFAILED (-5): The upload failed. Please restart it from scratch."
21
+ when -6
22
+ "ETOOMANY (-6): Too many concurrent IP addresses are accessing this upload target URL."
23
+ when -7
24
+ "ERANGE (-7): The upload file packet is out of range or not starting and ending on a chunk boundary."
25
+ when -8
26
+ "EEXPIRED (-8): The upload target URL you are trying to access has expired. Please request a fresh one."
27
+ when -9
28
+ "ENOENT (-9): Object (typically, node or user) not found"
29
+ when -10
30
+ "ECIRCULAR (-10): Circular linkage attempted"
31
+ when -11
32
+ "EACCESS (-11): Access violation (e.g., trying to write to a read-only share)"
33
+ when -12
34
+ "EEXIST (-12): Trying to create an object that already exists"
35
+ when -13
36
+ "EINCOMPLETE (-13): Trying to access an incomplete resource"
37
+ when -14
38
+ "EKEY (-14): A decryption operation failed (never returned by the API)"
39
+ when -15
40
+ "ESID (-15): Invalid or expired user session, please relogin"
41
+ when -16
42
+ "EBLOCKED (-16): User blocked"
43
+ when -17
44
+ "EOVERQUOTA (-17): Request over quota"
45
+ when -18
46
+ "ETEMPUNAVAIL (-18): Resource temporarily not available, please try again later"
47
+ else
48
+ "UNDEFINED Mega error #{error_code}"
49
+ end
50
+ super(msg)
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,157 @@
1
+ class Megar::Session
2
+
3
+ include Megar::Crypto
4
+ include Megar::Connection
5
+
6
+ attr_accessor :options
7
+ attr_accessor :email
8
+ attr_accessor :password
9
+ attr_accessor :master_key
10
+ attr_accessor :rsa_private_key # binary string
11
+ attr_accessor :decomposed_rsa_private_key # 4 part array of a32
12
+
13
+ # Start a new session, given +options+ hash.
14
+ #
15
+ # Required +options+ parameters:
16
+ # email: 'your email address' -- email for authentication
17
+ # password: 'your password' -- password for authentication
18
+ #
19
+ # Optional +options+ parameters:
20
+ # api_endpoint: 'url' -- talk to an alternative API endpoint
21
+ # autoconnect: true/false -- performs immediate login if true (default)
22
+ #
23
+ def initialize(options={})
24
+ default_options = { autoconnect: true }
25
+ @options = default_options.merge(options.symbolize_keys)
26
+ self.api_endpoint = @options[:api_endpoint] if @options[:api_endpoint]
27
+ connect! if @options[:autoconnect]
28
+ end
29
+
30
+ # Returns authenticated/connected status
31
+ def connected?
32
+ !sid.nil?
33
+ end
34
+
35
+ # Returns a pretty representation of the session object
36
+ def to_s
37
+ if connected?
38
+ "#{self.class.name}: connected as #{email}"
39
+ else
40
+ "#{self.class.name}: not connected"
41
+ end
42
+ end
43
+
44
+ # Command: perform login session challenge/response.
45
+ # Establishes a user session based on the response to a cryptographic challenge.
46
+ #
47
+ def connect!
48
+ handle_login_challenge_response(get_login_response)
49
+ end
50
+
51
+ # Returns the user email (convenience method)
52
+ def email
53
+ @email ||= options[:email]
54
+ end
55
+
56
+ # Returns the user password (convenience method)
57
+ def password
58
+ @password ||= options[:password]
59
+ end
60
+
61
+ # Returns the rsa_private_key base64-encoded
62
+ def rsa_private_key_b64
63
+ base64urlencode(rsa_private_key)
64
+ end
65
+
66
+ # Returns the folder collection
67
+ def folders
68
+ refresh_files! if @folders.nil?
69
+ @folders
70
+ end
71
+
72
+ # Returns the files collection
73
+ def files
74
+ refresh_files! if @files.nil?
75
+ @files
76
+ end
77
+
78
+ def refresh_files!
79
+ handle_files_response(get_files_response)
80
+ end
81
+
82
+ def reset_files!
83
+ @folders = Megar::Folders.new
84
+ @files = Megar::Files.new
85
+ end
86
+
87
+ protected
88
+
89
+ # Command: enforces guard condition requiring authenticated connection to proceed
90
+ def ensure_connected!
91
+ raise "Not connected" unless connected?
92
+ end
93
+
94
+ def get_login_response
95
+ api_request({'a' => 'us', 'user' => email, 'uh' => uh})
96
+ end
97
+
98
+ # Command: decrypt/decode the login +response_data+ received from Mega
99
+ #
100
+ # Javascript reference implementation: function api_getsid2(res,ctx)
101
+ #
102
+ def handle_login_challenge_response(response_data)
103
+ if k = response_data['k']
104
+ enc_master_key = base64_to_a32(k)
105
+ self.master_key = decrypt_key(enc_master_key, password_key)
106
+ end
107
+ if privk = response_data['privk']
108
+ self.rsa_private_key = decrypt_base64_to_str(privk, master_key)
109
+ self.decomposed_rsa_private_key = decompose_rsa_private_key(rsa_private_key)
110
+ if csid = response_data['csid']
111
+ self.sid = decrypt_session_id(csid,decomposed_rsa_private_key)
112
+ end
113
+ end
114
+ end
115
+
116
+ def get_files_response
117
+ ensure_connected!
118
+ api_request({'a' => 'f', 'c' => 1})
119
+ end
120
+
121
+ # Command: decrypt/decode the login +response_data+ received from Mega
122
+ #
123
+ def handle_files_response(response_data)
124
+ reset_files!
125
+ response_data['f'].each do |f|
126
+ item_attributes = {id: f['h'], payload: f.dup, type: f['t'] }
127
+ case f['t']
128
+ when 0 # File
129
+ item_attributes[:key] = k = decrypt_file_key(f)
130
+ item_attributes[:attributes] = decrypt_file_attributes(f,k)
131
+ files.add(item_attributes)
132
+ when 1 # Folder
133
+ item_attributes[:key] = k = decrypt_file_key(f)
134
+ item_attributes[:attributes] = decrypt_file_attributes(f,k)
135
+ folders.add(item_attributes)
136
+ when 2,3,4 # Root, Inbox, Trash Bin
137
+ folders.add(item_attributes)
138
+ end
139
+ end
140
+ true
141
+ end
142
+
143
+ # Returns the encoded user password key
144
+ def password_key
145
+ prepare_key_pw(password)
146
+ end
147
+
148
+ # Returns the calculated uh parameter based on email and password
149
+ #
150
+ # Javascript reference implementation: function stringhash(s,aes)
151
+ #
152
+ def uh
153
+ stringhash(email.downcase, password_key)
154
+ end
155
+
156
+
157
+ end
@@ -0,0 +1,87 @@
1
+ require 'uri'
2
+
3
+ # class that groks the megar command line options and invokes the required task
4
+ class Megar::Shell
5
+
6
+ # holds the parsed options
7
+ attr_reader :options
8
+
9
+ # holds the remaining command line arguments
10
+ attr_reader :args
11
+
12
+ # initializes the shell with command line argments:
13
+ #
14
+ # +options+ is expected to be the hash structure as provided by GetOptions.new(..)
15
+ #
16
+ # +args+ is the remaining command line arguments
17
+ #
18
+ def initialize(options,args)
19
+ @options = (options||{}).each{|k,v| {k => v} }
20
+ @args = args
21
+ end
22
+
23
+ # Command: execute the megar task according to the options provided on initialisation
24
+ def run
25
+ if email && password
26
+ $stderr.puts "Connecting to mega as #{email}.."
27
+ raise "Failed to connect!" unless session.connected?
28
+ case args.first
29
+ when /ls/i
30
+ session.files.each do |file|
31
+ puts file
32
+ end
33
+ else
34
+ $stderr.puts "Connected!"
35
+ end
36
+ else
37
+ usage
38
+ end
39
+ end
40
+
41
+ # defines the valid command line options
42
+ OPTIONS = %w(help verbose email=s password=s)
43
+
44
+ class << self
45
+
46
+ # prints usage/help information
47
+ def usage
48
+ $stderr.puts <<-EOS
49
+
50
+ Megar v#{Megar::VERSION}
51
+ ===================================
52
+
53
+ Usage:
54
+ megar [options] [commands]
55
+
56
+ Options:
57
+ -h | --help : shows command help
58
+ -v | --verbose : run with verbose
59
+ -e= | --email=value : email address for login
60
+ -p= | --password=value : password for login
61
+
62
+ Commands:
63
+ (none) : will perform a basic connection test only
64
+ ls : returns a full file listing
65
+
66
+ Examples:
67
+ megar --email=my@mail.com --password=MyPassword ls
68
+ megar -e my@mail.com -p MyPassword ls
69
+
70
+ EOS
71
+ end
72
+ end
73
+
74
+ # prints usage/help information
75
+ def usage
76
+ self.class.usage
77
+ end
78
+
79
+ def session
80
+ @session ||= Megar::Session.new(email: email, password: password)
81
+ end
82
+
83
+ # Option shortcuts
84
+ def email ; options[:email] ; end
85
+ def password ; options[:password] ; end
86
+
87
+ end
@@ -0,0 +1,3 @@
1
+ module Megar
2
+ VERSION = "0.0.1"
3
+ end
data/megar.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'megar/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "megar"
8
+ gem.version = Megar::VERSION
9
+ gem.authors = ["Paul Gallagher"]
10
+ gem.email = ["gallagher.paul@gmail.com"]
11
+ gem.description = %q{Megaargh! A Ruby wrapper for the Mega API (mega.co.nz)}
12
+ gem.summary = %q{A Ruby wrapper and command-line tool for the Mega API (mega.co.nz)}
13
+ gem.homepage = "https://github.com/tardate/megar"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_runtime_dependency(%q<getoptions>, [">= 0.3"])
21
+ gem.add_runtime_dependency(%q<activesupport>, [">= 3.0.3"])
22
+
23
+ gem.add_development_dependency(%q<bundler>, ["> 1.1.0"])
24
+ gem.add_development_dependency(%q<rake>, ["~> 0.9.2.2"])
25
+ gem.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
26
+ gem.add_development_dependency(%q<rdoc>, ["~> 3.11"])
27
+ gem.add_development_dependency(%q<guard-rspec>, ["~> 1.2.0"])
28
+ gem.add_development_dependency(%q<rb-fsevent>, ["~> 0.9.1"])
29
+
30
+ end
@@ -0,0 +1,109 @@
1
+ {
2
+ "email": "megartest@gmail.com",
3
+ "email_mixed_case": "Megartest@gmail.com",
4
+ "password": "15kMW4GiHVWFmqJJ",
5
+ "autoconnect": false,
6
+ "login_response_data": {
7
+ "csid": "CAA4by3fJFnderfxuUgwXDhQJkaxKTREaVIA4QuyKmE2CnVDP2w5pU-tftPcHvfdCoV9Gi-rTKsB11rBcc1yK1I8IVOzAwST9yUwSmZI-9Euq-FEUOCsnHYLDlykTOSisU9OU0GausJZbCCHrSLI_Q49Ndc-BBQc361W-DkZUl8ZgKCl3qleBP5aepJbR-bSTR5bJ-IxRmdf2jYUE0dz3lLMGSC4XjGBXgydFlGNgJqURpik9FrW1gPFyKESRNfxudteoSWO-f2bgD-4eEek4kHAaZZQW2K5Yb2GBXCOVQ0_7jpcRd9-gwhUHpTJETNxtEPxMEBtOtAhEm1hxXSxWZgr",
8
+ "privk": "4Iurk4vQ0BlLEEvEyaRUX8QnYzJFIedm0RVKe2pLh4wTzBBtgmafnszD_dP3Y40P-DSNLEGfWghL-0_BIW4XP8tfGNFkMg7mnYgQYC91ccz58BbhGEDf7-j97dstckf3OSSsT9D9H-cocXzyt1m231w9d7YLX23c3KAGz3vxdnl66F4jsFGO9AdIyb1KTEEt7NHkljtbz3WgWlk6W6wBawuEbJvLPHPx2L16i0iacbbkQi8ZF4pmddswMa9yWOSXwUribGDbdgx_jDGjBoKJUJI-7Aa-aOaRPEoVUTft2R2lNAVErvYwD_GIsIhQiCcJWul-KqPhI3vquvzUHd2rHHvKyTLyvXKyIDeVdBGXTaqFA2ni9xsjUNMOX14ftBZ4_fgDce0jvrZzYRs5IuOMt3K9gKMpBpUWtnlaBYbnYdL2zJuFlrM77PlcYab2uLLqlSe4hh-FxMAc_p_8tDAoU4dPgpgOpU3GRR8i7oszu_MKibzNpRBeAX1UtwKwQepjPnipu9aQlPy9OtgB-5KmGi34EcMKBPg8BN6vHRYg0Za_lk_TtTQ4WbkllAqR3Fla5-RBjzB7f3dWae4ES3VX4f-dI-UYrrKDQkqaS0R_xLaMPNG6_GQMDM7L5Fsa8I3hFjdgfs9rV4Xf_uIFrhUjm25EuT5JCdobNmwBi9VU5nCbk0LQJdrXDNYV3fmD-Y_p_KV8x_-opRnXUV03SCopOD6DhitsFtpv065QXcG1SNDu0mM-kgDh9E-QddtQQtXLLPuKFyX5o1ts08EqXTxHsZo5tIsevbExZoz9hxSRrr-V5swEDcJXoV78_k6sR3iniZ_m77JVo9QU572av2MvNhyu5YeTXyc0m6Me7cPGiO0",
9
+ "k": "GmSWkhwjjn38w8OHkD_S-A"
10
+ },
11
+ "master_key": [
12
+ 384287193,
13
+ 302859698,
14
+ 554881366,
15
+ 530403344
16
+ ],
17
+ "expected_uh": "mBdCzS2yFgo",
18
+ "sid": "yfMNTyTPzUKOuWYWRO-qhXpBOEN6Q2Y5dFp3JAKn0WNBqLBEzgbaMaILNg",
19
+ "rsa_private_key_b64": "BACOQ7VCjwkiwYCKhqRRvPTMBaQ5Yx2_sszj6ILrxrTBOJ2bu-9LsOSvaGz0HpnM2lXpJg3h6QWJdlQBOhwvfeafHg7rcEaaDuUWONifv5ABpQgeoAij6cgARMvFiOdjarMvu954Eduup8PHcgfjsBSu76qpu3UqkZCpBpByAYU1oQQA1i5oxuA9i7qsXxhJRsBeceI7hZSnNYAQzsWfFD0kDW8GsJBuK-CST6oNNp0z7MQ5Qof6DZ8x9wt5ZJR43HFmD2VtTQUZLvIeemC4URuR74UWn25CETyzQdIhdNSEibaCRvi0sNwb-ikCZVxxo4-O7ZBQPw0egrQOJW06HlCQUykH_00EIoiBRcbJLS6W3nJ8KhddDf_4glZYPz6fBpqu1jIdIBGOGQf4Ttn_DmdAgkLxWF7AF6k59D45J9kxfANRJIBGSWUsI1bQBlrwFtois4O14uQkQ40Jh_d5BCrLPe599zrliGH1SsECx_xYGWcWY_GPsPa99d_St56BB9oUtDW3A1clEx2uVS8OWqRPgVimu6MXs_TEEnjWGRFofHpl_8qGeAoPbNkXsTMQb6uUK4NCYq3UzVfreByEnOqxuQcu2jlF4Ey0UH9w_KjkRyT8xu--kyZAbktYaw4EZmGUxZIlkZLk_OAGaW8KWPZhdGxjfi8K7WeZK2HtLe4At48DC_ED_20pNtkTCrB6-FQlMj6a4O2C1sE6pSdrIPofcK-HpaefOXqYKWpRM-Wnp_qHnuJgEtgodQ-pw74nXkOsVFZoJzVGCepZKoa2xhevAu3CfXMfR97LIski17PPwWV9n7yQ7onSKDmJP-q4mO7PG7LICE22WkgJkk_T1W4UkDTkCxZE_t64F6JOryg",
20
+ "decomposed_rsa_private_key": [
21
+ 99901518447147034909201847419097315968297147437151469508360346463543063455874100243913856315732601845631262435262260706834732781343251117295631282035669718956849603873760427510697419513879951677293141811802298231979984193675329310799949319780256427743430868153430092107435691279045586737323088097814348379553,
22
+ 150403214039042969422234539598081448727095065760827625117291927993312435808658801596490175435022631344068300286691567969167599002686861320953369584858579175145292571827741537678953851066650767421880724569960757205263834584408256907364362335186143689470541020576121430290451811639884653381769242114589063140137,
23
+ 9722388475302835659981770144245473802727189238482784140772185098454692776534140965636906536278050956583642123384956999145748370323609525303581225997179041811165866787665739142915159339213277652697292051951101524711140179706280769338283140995188236373535772131640760223532777113050011892350517809489041748221890761908227035155427120665309649346176680716768795083108884918500667128372337390742180547015864208380672823661707652326881334341297176353315311938916714461230279773342086788397843164325182504213999351119600183937196301188144973390771200970801617167449547771853642668679963596445734355177856606187012189522929,
24
+ 76655456251690686310715773467748134538235257990884898225079665817629234781830040448768066408365824785604130747777597240274232562744683362773774949177985094198657737800269908016421544875604264806165580741547472576851314301645952126968540259498362204011872412045782028541860398434400028332528808362023863391812
25
+ ],
26
+ "files_response_data": {
27
+ "f": [
28
+ {
29
+ "h": "ewZ1GAqT",
30
+ "p": "",
31
+ "u": "zA8CzCf9tZw",
32
+ "t": 4,
33
+ "a": "",
34
+ "k": "",
35
+ "ts": 1361694312
36
+ },
37
+ {
38
+ "h": "qtBjmBTR",
39
+ "p": "",
40
+ "u": "zA8CzCf9tZw",
41
+ "t": 3,
42
+ "a": "",
43
+ "k": "",
44
+ "ts": 1361694312
45
+ },
46
+ {
47
+ "h": "ywJVFBKB",
48
+ "p": "",
49
+ "u": "zA8CzCf9tZw",
50
+ "t": 2,
51
+ "a": "",
52
+ "k": "",
53
+ "ts": 1361694312
54
+ },
55
+ {
56
+ "h": "jtwkAQaK",
57
+ "p": "ywJVFBKB",
58
+ "u": "zA8CzCf9tZw",
59
+ "t": 1,
60
+ "a": "US0wKXcni_p8dnqRvhR_Otafji3ioNJ5IsgSHB5zhOw",
61
+ "k": "zA8CzCf9tZw:4LMrqo0U-VwPGhhXV2fjQw",
62
+ "ts": 1361696928
63
+ },
64
+ {
65
+ "h": "atY1xRAb",
66
+ "p": "ywJVFBKB",
67
+ "u": "zA8CzCf9tZw",
68
+ "t": 1,
69
+ "a": "XOtY9s3EU2DypiRM2I74SS79lZaaQYQcmO4-Lekk0rw",
70
+ "k": "zA8CzCf9tZw:56VbHKn14ww22T-Xuwyjvg",
71
+ "ts": 1361696936
72
+ },
73
+ {
74
+ "h": "74ZTXbyR",
75
+ "p": "ywJVFBKB",
76
+ "u": "zA8CzCf9tZw",
77
+ "t": 0,
78
+ "a": "m9IIApZejMhZnsRy63HvhrkYh_AalPjfRYzsMpkq5-Q",
79
+ "k": "zA8CzCf9tZw:fJjggPPQGf_5MzqCuvyUGxuxVnDJLo6qBGAOwJAkOm4",
80
+ "s": 39,
81
+ "ts": 1361697187
82
+ },
83
+ {
84
+ "h": "L0xHwayA",
85
+ "p": "ywJVFBKB",
86
+ "u": "zA8CzCf9tZw",
87
+ "t": 0,
88
+ "a": "n4CazRegf4aLA4BNrdoEsqRLGLQ244NjJUJi53Zz-J4",
89
+ "k": "zA8CzCf9tZw:Iz6fsJ_qIgmS4uC4eirPnfaZOcLOoQA2t_5wTtfqp7Y",
90
+ "s": 137080,
91
+ "ts": 1361697194
92
+ }
93
+ ],
94
+ "ok": [
95
+
96
+ ],
97
+ "s": [
98
+
99
+ ],
100
+ "u": [
101
+ {
102
+ "u": "zA8CzCf9tZw",
103
+ "c": 2,
104
+ "m": "megartest@gmail.com"
105
+ }
106
+ ],
107
+ "sn": "pQ-WDyYPBHU"
108
+ }
109
+ }