rnote 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/rnote ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+ require 'gli'
3
+ require 'rnote'
4
+
5
+ include GLI::App
6
+
7
+ program_desc 'Evernote Command Line'
8
+
9
+ # version Rnote::VERSION
10
+
11
+ desc 'prompt for input and run an editor when necessary'
12
+ default_value true
13
+ switch :interactive
14
+
15
+ pre do |global,command,options,args|
16
+ # Pre logic here
17
+ # Return true to proceed; false to abort and not call the
18
+ # chosen command
19
+ # Use skips_pre before a command to skip this block
20
+ # on that command only
21
+ true
22
+ end
23
+
24
+ post do |global,command,options,args|
25
+ # Post logic here
26
+ # Use skips_post before a command to skip this
27
+ # block on that command only
28
+ end
29
+
30
+ def error_code_to_name(error_code)
31
+ Evernote::EDAM::Error::EDAMErrorCode.constants.select { |constant| Evernote::EDAM::Error::EDAMErrorCode.const_get(constant) == error_code }.first.to_s
32
+ end
33
+
34
+ on_error do |exception|
35
+ # Error logic here
36
+ # return false to skip default error handling
37
+
38
+ if exception.instance_of?(Evernote::EDAM::Error::EDAMUserException)
39
+ if exception.errorCode == Evernote::EDAM::Error::EDAMErrorCode::AUTH_EXPIRED
40
+ puts "Authorization issue. perhaps your password is incorrect or expired."
41
+ false
42
+ else
43
+ puts exception.error_message
44
+ true
45
+ end
46
+ elsif exception.instance_of?(Evernote::EDAM::Error::EDAMSystemException)
47
+ puts exception.error_message
48
+ true
49
+ elsif exception.class == Evernote::EDAM::Error::InvalidXmlError
50
+ puts exception.xml
51
+ true
52
+ else
53
+ true
54
+ end
55
+
56
+ end
57
+
58
+ $app = Rnote::App.new
59
+
60
+ exit run(ARGV)
data/lib/rnote.rb ADDED
@@ -0,0 +1,75 @@
1
+
2
+
3
+ # environment detection
4
+ begin
5
+ # this file only exists in development
6
+ # its not included in the gem,
7
+ # and thus not found in production (the installed gem)
8
+ require_relative 'rnote/environment'
9
+ rescue LoadError
10
+ # production environment
11
+ # should only happen in the installed gem.
12
+ RNOTE_HOME ||= ENV['HOME'] + '/.rnote'
13
+ RNOTE_TESTING_OK = false
14
+ RNOTE_SANDBOX_ONLY = false
15
+ end
16
+
17
+ require 'rnote/version'
18
+ require 'rnote/converter'
19
+ require 'rnote/persister'
20
+ require 'rnote/auth'
21
+
22
+ # verbs
23
+ Dir[File.absolute_path(File.dirname(__FILE__)) + '/rnote/cmd/*.rb'].each do |file|
24
+ require file
25
+ end
26
+
27
+
28
+ module Rnote
29
+
30
+ class App
31
+
32
+ attr_reader :persister,:auth
33
+
34
+ def initialize
35
+ @persister = Persister.new
36
+ @auth = Auth.new(@persister)
37
+ end
38
+
39
+ def client
40
+ auth.client
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ module EDAMErrors
48
+
49
+ def error_code_string
50
+ Evernote::EDAM::Error::EDAMErrorCode.constants.select { |constant_sym|
51
+ Evernote::EDAM::Error::EDAMErrorCode.const_get(constant_sym) == self.errorCode
52
+ }.first.to_s
53
+ end
54
+
55
+ end
56
+
57
+ class Evernote::EDAM::Error::EDAMSystemException
58
+
59
+ include EDAMErrors
60
+
61
+ def error_message
62
+ "#{self.error_code_string}(#{self.errorCode}: #{self.message})"
63
+ end
64
+
65
+ end
66
+
67
+ class Evernote::EDAM::Error::EDAMUserException
68
+
69
+ include EDAMErrors
70
+
71
+ def error_message
72
+ "#{self.error_code_string}(#{self.errorCode}): #{self.parameter}"
73
+ end
74
+
75
+ end
data/lib/rnote/auth.rb ADDED
@@ -0,0 +1,130 @@
1
+
2
+ require 'mechanize'
3
+ require 'evernote_oauth'
4
+ require 'rnote/persister'
5
+
6
+ DUMMY_CALLBACK_URL = 'http://www.evernote.com'
7
+
8
+ module Rnote
9
+
10
+ class Auth
11
+
12
+ def initialize(persister=Persister.new)
13
+ @persister = persister
14
+ end
15
+
16
+ def login_with_developer_token(developer_token,sandbox)
17
+ if is_logged_in
18
+ if @persister.get_developer_token == developer_token
19
+ return
20
+ else
21
+ logout
22
+ end
23
+ end
24
+ @persister.persist_developer_token(developer_token)
25
+ @persister.persist_sandbox(sandbox)
26
+ end
27
+
28
+ def login_with_password(username,password,sandbox)
29
+
30
+ if is_logged_in
31
+ if who == username
32
+ # already logged in (we don't check against service though)
33
+ # if a re-login is truely required, the user can just logout first.
34
+ return
35
+ else
36
+ logout
37
+ end
38
+ end
39
+
40
+ ## Get a user key using these crednetials
41
+
42
+ # this client isn't authorized, and can only request authorization. no api calls.
43
+ auth_client = EvernoteOAuth::Client.new(
44
+ consumer_key: @persister.get_consumer_key,
45
+ consumer_secret: @persister.get_consumer_secret,
46
+ sandbox: sandbox
47
+ )
48
+
49
+ request_token = auth_client.request_token(:oauth_callback => DUMMY_CALLBACK_URL)
50
+ oauth_verifier = mechanize_login(request_token.authorize_url, username, password)
51
+ access_token = request_token.get_access_token(oauth_verifier: oauth_verifier)
52
+ user_token = access_token.token
53
+
54
+ @persister.persist_username(username)
55
+ @persister.persist_user_token(user_token)
56
+ @persister.persist_sandbox(sandbox)
57
+
58
+ end
59
+
60
+ def client
61
+ # not the same as the client used to get the token.
62
+ # this one is fully authorized and can make actual api calls.
63
+
64
+ if not is_logged_in
65
+ raise "not logged in"
66
+ end
67
+
68
+ token = @persister.get_user_token || @persister.get_developer_token
69
+
70
+ @client ||= EvernoteOAuth::Client.new(token: token, sandbox: @persister.get_sandbox)
71
+
72
+ @client
73
+ end
74
+
75
+ def note_store
76
+ client.note_store
77
+ end
78
+
79
+ def mechanize_login(url, username, password)
80
+
81
+ agent = Mechanize.new
82
+ login_page = agent.get(url)
83
+ login_form = login_page.form('login_form')
84
+ raise unless login_form
85
+ login_form.username = username
86
+ login_form.password = password
87
+ accept_page = agent.submit(login_form,login_form.buttons.first)
88
+
89
+ if accept_page.form('login_form')
90
+ # sent us back to the login page
91
+ raise "bad username/password"
92
+ elsif not accept_page.form('oauth_authorize_form')
93
+ raise "failed to login"
94
+ end
95
+
96
+ accept_form = accept_page.form('oauth_authorize_form')
97
+ # we don't need to go so far as to retrieve the callback url.
98
+ agent.redirect_ok = false
99
+ callback_redirect = agent.submit(accept_form, accept_form.buttons.first)
100
+ response_url = callback_redirect.response['location']
101
+ oauth_verifier = CGI.parse(URI.parse(response_url).query)['oauth_verifier'][0]
102
+
103
+ oauth_verifier
104
+ end
105
+
106
+ def is_logged_in
107
+ @persister.get_user_token or @persister.get_developer_token
108
+ end
109
+
110
+ def who
111
+ if is_logged_in
112
+ @persister.get_username or @persister.get_developer_token
113
+ else
114
+ nil
115
+ end
116
+ end
117
+
118
+
119
+ def logout
120
+ # unfortunately, no way to revoke a token via API
121
+ # TODO perhaps I can redo the oauth, and choose revoke instead of re-accept
122
+ @persister.forget_user_token
123
+ @persister.forget_username
124
+ @persister.forget_developer_token
125
+ @persister.forget_sandbox
126
+ end
127
+
128
+ end
129
+
130
+ end
@@ -0,0 +1,32 @@
1
+
2
+ require 'gli'
3
+
4
+ require 'rnote/edit'
5
+
6
+ include GLI::App
7
+
8
+
9
+ desc 'create a note and launch the editor for it'
10
+ command :create do |verb|
11
+ verb.command :note do |noun|
12
+
13
+
14
+ Rnote::Edit.include_set_options(noun)
15
+ Rnote::Edit.include_editor_options(noun)
16
+
17
+ noun.action do |global_options,options,args|
18
+
19
+ if args.length > 0
20
+ raise "create doesn't take a search query"
21
+ end
22
+
23
+ edit = Rnote::Edit.new($app.auth)
24
+ edit.options(options.merge(global_options))
25
+ edit.edit_action
26
+
27
+
28
+ end
29
+ end
30
+
31
+ verb.default_command :note
32
+ end
@@ -0,0 +1,36 @@
1
+
2
+ require 'rnote/edit'
3
+ require 'rnote/find'
4
+
5
+ include GLI::App
6
+
7
+
8
+ desc 'Describe edit here'
9
+ arg_name 'Describe arguments to edit here'
10
+ command :edit do |verb|
11
+
12
+ verb.command :note do |noun|
13
+
14
+ Rnote::Edit.include_set_options(noun)
15
+ Rnote::Edit.include_editor_options(noun)
16
+ Rnote::Find.include_search_options(noun)
17
+
18
+ noun.action do |global_options,options,args|
19
+
20
+ find = Rnote::Find.new($app.auth,$app.persister)
21
+ note = find.find_note(options.merge(global_options),args)
22
+
23
+ edit = Rnote::Edit.new($app.auth)
24
+ edit.options(options.merge(global_options))
25
+ edit.note(note)
26
+ edit.edit_action
27
+
28
+ end
29
+
30
+ end
31
+
32
+ verb.default_command :note
33
+
34
+ end
35
+
36
+
@@ -0,0 +1,24 @@
1
+
2
+ require 'rnote/find'
3
+
4
+ include GLI::App
5
+
6
+
7
+ desc 'search for notes/tags/notebooks'
8
+ command :find do |verb|
9
+
10
+ verb.command :note do |noun|
11
+
12
+ Rnote::Find.include_search_options(noun)
13
+
14
+ noun.action do |global_options,options,args|
15
+
16
+ find = Rnote::Find.new($app.auth,$app.persister)
17
+ find.find_cmd(options.merge(global_options),args)
18
+
19
+ end
20
+ end
21
+
22
+ verb.default_command :note
23
+ end
24
+
@@ -0,0 +1,68 @@
1
+
2
+ require 'highline/import'
3
+
4
+ include GLI::App
5
+
6
+ desc 'provide rnote credentials'
7
+ command :login do |c|
8
+
9
+ c.desc "username"
10
+ c.flag [:u,:user,:username]
11
+
12
+ c.desc "password (if not provided, will ask)"
13
+ c.flag [:p,:pass,:password]
14
+
15
+ c.desc "developer token, if you wish to forgoe a password."
16
+ c.flag [:d,:'dev-token',:'developer-token']
17
+
18
+ c.desc "use the sandbox environment in lue of the production evernote system."
19
+ c.default_value false
20
+ c.switch [:s,:sandbox]
21
+
22
+ c.desc "provide a consumer key, instead of the included one."
23
+ c.flag [:k,:key,:'consumer-key']
24
+
25
+ c.desc "provide a consumer secret to go along with the consumer key."
26
+ c.flag [:c,:secret,:'consumer-secret']
27
+
28
+ c.action do |global_options,options,args|
29
+ raise "This command takes no arguments, only options (i.e. --username" unless args.length == 0
30
+
31
+ if options[:key]
32
+ $app.persister.persist_consumer_key(options[:key])
33
+ end
34
+
35
+ if options[:secret]
36
+ $app.persister.persist_consumer_secret(options[:secret])
37
+ end
38
+
39
+ if options[:d]
40
+ # first check for a dev token
41
+ $app.auth.login_with_developer_token(options[:d],options[:sandbox])
42
+ puts "login successful using developer key"
43
+ else
44
+ # then fall back to using a username and password
45
+
46
+ if not options[:u]
47
+ answer = ask("Enter your username: ")
48
+ options[:u] = answer
49
+ end
50
+
51
+ if not options[:p]
52
+ answer = ask("Enter your password: ") { |q| q.echo = 'x' }
53
+ options[:p] = answer
54
+ end
55
+
56
+ $app.auth.login_with_password(options[:user],options[:password], options[:sandbox])
57
+
58
+ # test the login with a harmless api call.
59
+ $app.auth.client.user_store.getUser
60
+
61
+ puts "you are now logged in as '#{$app.auth.who}'"
62
+
63
+ end
64
+
65
+ end
66
+ end
67
+
68
+
@@ -0,0 +1,24 @@
1
+
2
+ include GLI::App
3
+
4
+ =begin
5
+
6
+ only drops the token.
7
+ there is no way to ask evernote to revoke the token, from an api.
8
+
9
+ will forget about the user,password or developer key.
10
+ but won't forget about a consumer key, as that by itself is not considered a login.
11
+
12
+ =end
13
+
14
+ desc 'log user out of rnote'
15
+ command :logout do |c|
16
+ c.action do |global_options,options,args|
17
+ raise unless args.length == 0
18
+
19
+ $app.auth.logout
20
+
21
+ end
22
+ end
23
+
24
+