gmail 0.5.0 → 0.6.0
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.
- checksums.yaml +4 -4
- data/.gitignore +27 -27
- data/.rspec +1 -1
- data/.rubocop.yml +13 -13
- data/.rubocop_todo.yml +239 -239
- data/.travis.yml +19 -19
- data/CHANGELOG.md +145 -139
- data/Gemfile +3 -3
- data/LICENSE +21 -21
- data/README.md +355 -354
- data/Rakefile +46 -46
- data/gmail.gemspec +34 -34
- data/lib/gmail.rb +78 -78
- data/lib/gmail/client.rb +34 -34
- data/lib/gmail/client/base.rb +229 -229
- data/lib/gmail/client/plain.rb +24 -24
- data/lib/gmail/client/xoauth.rb +68 -68
- data/lib/gmail/client/xoauth2.rb +39 -39
- data/lib/gmail/imap_extensions.rb +159 -150
- data/lib/gmail/labels.rb +79 -79
- data/lib/gmail/mailbox.rb +175 -135
- data/lib/gmail/message.rb +207 -207
- data/lib/gmail/version.rb +3 -3
- data/spec/account.yml.example +1 -1
- data/spec/account.yml.obfus +2 -2
- data/spec/gmail/client/base_spec.rb +5 -5
- data/spec/gmail/client/plain_spec.rb +169 -168
- data/spec/gmail/client/xoauth2_spec.rb +186 -186
- data/spec/gmail/client/xoauth_spec.rb +5 -5
- data/spec/gmail/client_spec.rb +5 -5
- data/spec/gmail/imap_extensions_spec.rb +47 -12
- data/spec/gmail/labels_spec.rb +27 -18
- data/spec/gmail/mailbox_spec.rb +84 -47
- data/spec/gmail/message_spec.rb +181 -181
- data/spec/gmail_spec.rb +39 -39
- data/spec/recordings/gmail/_new_connects_with_client_and_give_it_context_when_block_given.yml +28 -28
- data/spec/recordings/gmail/_new_connects_with_gmail_service_and_return_valid_connection_object.yml +28 -28
- data/spec/recordings/gmail/_new_does_not_raise_error_when_couldn_t_connect_with_given_account.yml +13 -13
- data/spec/recordings/gmail/_new_raises_error_when_couldn_t_connect_with_given_account.yml +13 -13
- data/spec/recordings/gmail_client_plain/instance/_connection_automatically_logs_in_to_gmail_account_when_it_s_called.yml +42 -42
- data/spec/recordings/gmail_client_plain/instance/delivers_inline_composed_email.yml +42 -42
- data/spec/recordings/gmail_client_plain/instance/does_not_log_in_when_given_gmail_account_is_invalid.yml +13 -13
- data/spec/recordings/gmail_client_plain/instance/does_not_raise_error_even_though_gmail_account_is_invalid.yml +13 -13
- data/spec/recordings/gmail_client_plain/instance/labels/checks_if_there_is_given_label_defined.yml +196 -196
- data/spec/recordings/gmail_client_plain/instance/labels/creates_given_label.yml +151 -151
- data/spec/recordings/gmail_client_plain/instance/labels/removes_existing_label.yml +146 -146
- data/spec/recordings/gmail_client_plain/instance/labels/returns_list_of_all_available_labels.yml +113 -113
- data/spec/recordings/gmail_client_plain/instance/properly_logs_in_to_valid_gmail_account.yml +42 -42
- data/spec/recordings/gmail_client_plain/instance/properly_logs_out_from_gmail.yml +42 -42
- data/spec/recordings/gmail_client_plain/instance/properly_switches_to_given_mailbox.yml +109 -109
- data/spec/recordings/gmail_client_plain/instance/properly_switches_to_given_mailbox_using_block_style.yml +109 -109
- data/spec/recordings/gmail_client_plain/instance/raises_error_when_given_gmail_account_is_invalid_and_errors_enabled.yml +13 -13
- data/spec/recordings/gmail_client_xo_auth2/instance/does_not_log_in_when_given_gmail_account_is_invalid.yml +13 -13
- data/spec/recordings/gmail_client_xo_auth2/instance/labels/checks_if_there_is_given_label_defined.yml +27 -27
- data/spec/recordings/gmail_client_xo_auth2/instance/labels/creates_given_label.yml +39 -39
- data/spec/recordings/gmail_client_xo_auth2/instance/labels/removes_existing_label.yml +39 -39
- data/spec/recordings/gmail_client_xo_auth2/instance/labels/returns_list_of_all_available_labels.yml +27 -27
- data/spec/recordings/gmail_client_xo_auth2/instance/properly_logs_in_to_valid_gmail_account.yml +15 -15
- data/spec/recordings/gmail_client_xo_auth2/instance/properly_logs_out_from_gmail.yml +15 -15
- data/spec/recordings/gmail_client_xo_auth2/instance/properly_switches_to_given_mailbox.yml +40 -40
- data/spec/recordings/gmail_client_xo_auth2/instance/properly_switches_to_given_mailbox_using_block_style.yml +40 -40
- data/spec/recordings/gmail_client_xo_auth2/instance/raises_error_when_given_gmail_account_is_invalid_and_errors_enabled.yml +13 -13
- data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/all/localizes_into_the_appropriate_label.yml +116 -116
- data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/and_the_mailbox_does_not_exist/returns_the_mailbox_name_as_a_string.yml +110 -0
- data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/drafts/localizes_into_the_appropriate_label.yml +116 -116
- data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/flagged/localizes_into_the_appropriate_label.yml +116 -116
- data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/important/localizes_into_the_appropriate_label.yml +116 -116
- data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/inbox/localizes_into_the_appropriate_label.yml +42 -42
- data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/junk/localizes_into_the_appropriate_label.yml +116 -116
- data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/sent/localizes_into_the_appropriate_label.yml +116 -116
- data/spec/recordings/gmail_labels/localize/when_given_the_xl_is_tflag/trash/localizes_into_the_appropriate_label.yml +116 -116
- data/spec/recordings/gmail_mailbox/instance/counts_all_emails.yml +277 -277
- data/spec/recordings/gmail_mailbox/instance/finds_messages.yml +586 -586
- data/spec/recordings/gmail_mailbox/instance/waits_once.yml +136 -0
- data/spec/recordings/gmail_mailbox/instance/waits_repeatedly.yml +141 -0
- data/spec/recordings/gmail_mailbox/instance/waits_with_29-minute_re-issue.yml +136 -0
- data/spec/recordings/gmail_mailbox/instance/waits_with_an_unblocked_connection.yml +207 -0
- data/spec/recordings/gmail_mailbox/on_initialize/sets_client_and_name.yml +42 -42
- data/spec/recordings/gmail_mailbox/on_initialize/works_in_inbox_by_default.yml +42 -42
- data/spec/recordings/gmail_message/initialize/sets_prefetch_attrs.yml +578 -578
- data/spec/recordings/gmail_message/initialize/sets_uid_and_mailbox.yml +580 -580
- data/spec/recordings/gmail_message/instance_methods/deletes_itself.yml +637 -637
- data/spec/recordings/gmail_message/instance_methods/marks_itself_read.yml +682 -682
- data/spec/recordings/gmail_message/instance_methods/marks_itself_unread.yml +686 -686
- data/spec/recordings/gmail_message/instance_methods/moves_from_one_tag_to_other.yml +862 -862
- data/spec/recordings/gmail_message/instance_methods/removes_a_given_label.yml +776 -776
- data/spec/recordings/gmail_message/instance_methods/removes_a_given_label_with_old_method.yml +776 -776
- data/spec/recordings/gmail_message/instance_methods/sets_given_label.yml +690 -690
- data/spec/recordings/gmail_message/instance_methods/sets_given_label_with_old_method.yml +691 -691
- data/spec/spec_helper.rb +53 -53
- data/spec/support/imap_mock.rb +181 -129
- data/spec/support/obfuscation.rb +52 -52
- metadata +78 -5
data/Rakefile
CHANGED
@@ -1,46 +1,46 @@
|
|
1
|
-
#!/usr/bin/env rake
|
2
|
-
|
3
|
-
begin
|
4
|
-
require 'bundler/setup'
|
5
|
-
rescue LoadError
|
6
|
-
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
-
end
|
8
|
-
|
9
|
-
begin
|
10
|
-
require 'rdoc/task'
|
11
|
-
rescue LoadError
|
12
|
-
require 'rdoc/rdoc'
|
13
|
-
require 'rake/rdoctask'
|
14
|
-
RDoc::Task = Rake::RDocTask
|
15
|
-
end
|
16
|
-
|
17
|
-
RDoc::Task.new(:rdoc) do |rdoc|
|
18
|
-
rdoc.rdoc_dir = 'rdoc'
|
19
|
-
rdoc.title = "Gmail for Ruby #{Gmail::VERSION}"
|
20
|
-
rdoc.rdoc_files.include('README*')
|
21
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
-
end
|
23
|
-
|
24
|
-
Bundler::GemHelper.install_tasks
|
25
|
-
|
26
|
-
begin
|
27
|
-
require 'rspec/core/rake_task'
|
28
|
-
RSpec::Core::RakeTask.new(:spec)
|
29
|
-
rescue LoadError
|
30
|
-
task :spec do
|
31
|
-
abort 'Run `gem install rspec` to install RSpec'
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
task :default => :spec
|
36
|
-
|
37
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
38
|
-
require 'spec/support/obfuscation'
|
39
|
-
desc 'Obfuscates account.yml file.'
|
40
|
-
task :obfuscate do
|
41
|
-
Spec::Obfuscation.encrypt_file(File.join(File.dirname(__FILE__), 'spec', 'account.yml'))
|
42
|
-
end
|
43
|
-
|
44
|
-
task :deobfuscate do
|
45
|
-
puts Spec::Obfuscation.decrypt_file(File.join(File.dirname(__FILE__), 'spec', 'account.yml.obfus'))
|
46
|
-
end
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
begin
|
10
|
+
require 'rdoc/task'
|
11
|
+
rescue LoadError
|
12
|
+
require 'rdoc/rdoc'
|
13
|
+
require 'rake/rdoctask'
|
14
|
+
RDoc::Task = Rake::RDocTask
|
15
|
+
end
|
16
|
+
|
17
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
18
|
+
rdoc.rdoc_dir = 'rdoc'
|
19
|
+
rdoc.title = "Gmail for Ruby #{Gmail::VERSION}"
|
20
|
+
rdoc.rdoc_files.include('README*')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
23
|
+
|
24
|
+
Bundler::GemHelper.install_tasks
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rspec/core/rake_task'
|
28
|
+
RSpec::Core::RakeTask.new(:spec)
|
29
|
+
rescue LoadError
|
30
|
+
task :spec do
|
31
|
+
abort 'Run `gem install rspec` to install RSpec'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => :spec
|
36
|
+
|
37
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
38
|
+
require 'spec/support/obfuscation'
|
39
|
+
desc 'Obfuscates account.yml file.'
|
40
|
+
task :obfuscate do
|
41
|
+
Spec::Obfuscation.encrypt_file(File.join(File.dirname(__FILE__), 'spec', 'account.yml'))
|
42
|
+
end
|
43
|
+
|
44
|
+
task :deobfuscate do
|
45
|
+
puts Spec::Obfuscation.decrypt_file(File.join(File.dirname(__FILE__), 'spec', 'account.yml.obfus'))
|
46
|
+
end
|
data/gmail.gemspec
CHANGED
@@ -1,34 +1,34 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
$:.push File.expand_path('../lib', __FILE__)
|
4
|
-
require 'gmail/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = "gmail"
|
8
|
-
s.summary = "A Rubyesque interface to Gmail, with all the tools you will need."
|
9
|
-
s.description = "A Rubyesque interface to Gmail, with all the tools you will need.
|
10
|
-
Search, read and send multipart emails; archive, mark as read/unread,
|
11
|
-
delete emails; and manage labels.
|
12
|
-
"
|
13
|
-
s.version = Gmail::VERSION
|
14
|
-
s.platform = Gem::Platform::RUBY
|
15
|
-
s.authors = ["Chris Kowalik"]
|
16
|
-
s.email = ["chris@nu7hat.ch"]
|
17
|
-
s.homepage = "http://github.com/gmailgem/gmail"
|
18
|
-
s.licenses = ["MIT"]
|
19
|
-
|
20
|
-
# runtime dependencies
|
21
|
-
s.add_dependency "mail", ">= 2.2.1"
|
22
|
-
s.add_dependency "gmail_xoauth", ">= 0.3.0"
|
23
|
-
|
24
|
-
# development dependencies
|
25
|
-
s.add_development_dependency "rake"
|
26
|
-
s.add_development_dependency "rspec", ">= 3.1"
|
27
|
-
s.add_development_dependency "rubocop"
|
28
|
-
s.add_development_dependency "gem-release"
|
29
|
-
|
30
|
-
s.files = `git ls-files`.split("\n")
|
31
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
32
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
33
|
-
s.require_paths = ["lib"]
|
34
|
-
end
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
$:.push File.expand_path('../lib', __FILE__)
|
4
|
+
require 'gmail/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "gmail"
|
8
|
+
s.summary = "A Rubyesque interface to Gmail, with all the tools you will need."
|
9
|
+
s.description = "A Rubyesque interface to Gmail, with all the tools you will need.
|
10
|
+
Search, read and send multipart emails; archive, mark as read/unread,
|
11
|
+
delete emails; and manage labels.
|
12
|
+
"
|
13
|
+
s.version = Gmail::VERSION
|
14
|
+
s.platform = Gem::Platform::RUBY
|
15
|
+
s.authors = ["Chris Kowalik"]
|
16
|
+
s.email = ["chris@nu7hat.ch"]
|
17
|
+
s.homepage = "http://github.com/gmailgem/gmail"
|
18
|
+
s.licenses = ["MIT"]
|
19
|
+
|
20
|
+
# runtime dependencies
|
21
|
+
s.add_dependency "mail", ">= 2.2.1"
|
22
|
+
s.add_dependency "gmail_xoauth", ">= 0.3.0"
|
23
|
+
|
24
|
+
# development dependencies
|
25
|
+
s.add_development_dependency "rake"
|
26
|
+
s.add_development_dependency "rspec", ">= 3.1"
|
27
|
+
s.add_development_dependency "rubocop"
|
28
|
+
s.add_development_dependency "gem-release"
|
29
|
+
|
30
|
+
s.files = `git ls-files`.split("\n")
|
31
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
32
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
end
|
data/lib/gmail.rb
CHANGED
@@ -1,78 +1,78 @@
|
|
1
|
-
require 'net/imap'
|
2
|
-
require 'net/smtp'
|
3
|
-
require 'mail'
|
4
|
-
require 'date'
|
5
|
-
require 'time'
|
6
|
-
|
7
|
-
if RUBY_VERSION < "1.8.7"
|
8
|
-
require "smtp_tls"
|
9
|
-
end
|
10
|
-
|
11
|
-
class Object
|
12
|
-
def to_imap_date
|
13
|
-
date = respond_to?(:utc) ? utc.to_s : to_s
|
14
|
-
Date.parse(date).strftime("%d-%B-%Y")
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
module Gmail
|
19
|
-
autoload :Version, "gmail/version"
|
20
|
-
autoload :Client, "gmail/client"
|
21
|
-
autoload :Labels, "gmail/labels"
|
22
|
-
autoload :Mailbox, "gmail/mailbox"
|
23
|
-
autoload :Message, "gmail/message"
|
24
|
-
|
25
|
-
class << self
|
26
|
-
# Creates new Gmail connection using given authorization options.
|
27
|
-
#
|
28
|
-
# ==== Examples
|
29
|
-
#
|
30
|
-
# Gmail.new(:plain, "foo@gmail.com", "password")
|
31
|
-
# Gmail.new(:xoauth, "foo@gmail.com",
|
32
|
-
# :consumer_key => "",
|
33
|
-
# :consumer_secret => "",
|
34
|
-
# :token => "",
|
35
|
-
# :secret => "")
|
36
|
-
#
|
37
|
-
# To use plain authentication mehod you can also call:
|
38
|
-
#
|
39
|
-
# Gmail.new("foo@gmail.com", "password")
|
40
|
-
#
|
41
|
-
# You can also use block-style call:
|
42
|
-
#
|
43
|
-
# Gmail.new("foo@gmail.com", "password") do |client|
|
44
|
-
# # ...
|
45
|
-
# end
|
46
|
-
#
|
47
|
-
|
48
|
-
def new(*args, &block)
|
49
|
-
args.unshift(:plain) unless args.first.is_a?(Symbol)
|
50
|
-
client = Gmail::Client.new(*args)
|
51
|
-
client.connect
|
52
|
-
client.login
|
53
|
-
|
54
|
-
if block_given?
|
55
|
-
yield client
|
56
|
-
client.logout
|
57
|
-
end
|
58
|
-
|
59
|
-
client
|
60
|
-
end
|
61
|
-
alias_method :connect, :new
|
62
|
-
|
63
|
-
def new!(*args, &block)
|
64
|
-
args.unshift(:plain) unless args.first.is_a?(Symbol)
|
65
|
-
client = Gmail::Client.new(*args)
|
66
|
-
client.connect!
|
67
|
-
client.login!
|
68
|
-
|
69
|
-
if block_given?
|
70
|
-
yield client
|
71
|
-
client.logout
|
72
|
-
end
|
73
|
-
|
74
|
-
client
|
75
|
-
end
|
76
|
-
alias_method :connect!, :new!
|
77
|
-
end # << self
|
78
|
-
end # Gmail
|
1
|
+
require 'net/imap'
|
2
|
+
require 'net/smtp'
|
3
|
+
require 'mail'
|
4
|
+
require 'date'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
if RUBY_VERSION < "1.8.7"
|
8
|
+
require "smtp_tls"
|
9
|
+
end
|
10
|
+
|
11
|
+
class Object
|
12
|
+
def to_imap_date
|
13
|
+
date = respond_to?(:utc) ? utc.to_s : to_s
|
14
|
+
Date.parse(date).strftime("%d-%B-%Y")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Gmail
|
19
|
+
autoload :Version, "gmail/version"
|
20
|
+
autoload :Client, "gmail/client"
|
21
|
+
autoload :Labels, "gmail/labels"
|
22
|
+
autoload :Mailbox, "gmail/mailbox"
|
23
|
+
autoload :Message, "gmail/message"
|
24
|
+
|
25
|
+
class << self
|
26
|
+
# Creates new Gmail connection using given authorization options.
|
27
|
+
#
|
28
|
+
# ==== Examples
|
29
|
+
#
|
30
|
+
# Gmail.new(:plain, "foo@gmail.com", "password")
|
31
|
+
# Gmail.new(:xoauth, "foo@gmail.com",
|
32
|
+
# :consumer_key => "",
|
33
|
+
# :consumer_secret => "",
|
34
|
+
# :token => "",
|
35
|
+
# :secret => "")
|
36
|
+
#
|
37
|
+
# To use plain authentication mehod you can also call:
|
38
|
+
#
|
39
|
+
# Gmail.new("foo@gmail.com", "password")
|
40
|
+
#
|
41
|
+
# You can also use block-style call:
|
42
|
+
#
|
43
|
+
# Gmail.new("foo@gmail.com", "password") do |client|
|
44
|
+
# # ...
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
|
48
|
+
def new(*args, &block)
|
49
|
+
args.unshift(:plain) unless args.first.is_a?(Symbol)
|
50
|
+
client = Gmail::Client.new(*args)
|
51
|
+
client.connect
|
52
|
+
client.login
|
53
|
+
|
54
|
+
if block_given?
|
55
|
+
yield client
|
56
|
+
client.logout
|
57
|
+
end
|
58
|
+
|
59
|
+
client
|
60
|
+
end
|
61
|
+
alias_method :connect, :new
|
62
|
+
|
63
|
+
def new!(*args, &block)
|
64
|
+
args.unshift(:plain) unless args.first.is_a?(Symbol)
|
65
|
+
client = Gmail::Client.new(*args)
|
66
|
+
client.connect!
|
67
|
+
client.login!
|
68
|
+
|
69
|
+
if block_given?
|
70
|
+
yield client
|
71
|
+
client.logout
|
72
|
+
end
|
73
|
+
|
74
|
+
client
|
75
|
+
end
|
76
|
+
alias_method :connect!, :new!
|
77
|
+
end # << self
|
78
|
+
end # Gmail
|
data/lib/gmail/client.rb
CHANGED
@@ -1,34 +1,34 @@
|
|
1
|
-
module Gmail
|
2
|
-
module Client
|
3
|
-
# Raised when connection with Gmail IMAP service couldn't be established.
|
4
|
-
class ConnectionError < SocketError; end
|
5
|
-
# Raised when given username or password are invalid.
|
6
|
-
class AuthorizationError < Net::IMAP::NoResponseError; end
|
7
|
-
# Raised when delivered email is invalid.
|
8
|
-
class DeliveryError < ArgumentError; end
|
9
|
-
# Raised when given client is not registered
|
10
|
-
class UnknownClient < ArgumentError; end
|
11
|
-
|
12
|
-
def self.clients
|
13
|
-
@clients ||= {}
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.register(name, klass)
|
17
|
-
clients[name] = klass
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.new(name, *args)
|
21
|
-
if client = clients[name]
|
22
|
-
client.new(*args)
|
23
|
-
else
|
24
|
-
raise UnknownClient, "No such client: #{name}"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
require 'gmail/imap_extensions'
|
29
|
-
require 'gmail/client/base'
|
30
|
-
require 'gmail/client/plain'
|
31
|
-
require 'gmail/client/xoauth'
|
32
|
-
require 'gmail/client/xoauth2'
|
33
|
-
end # Client
|
34
|
-
end # Gmail
|
1
|
+
module Gmail
|
2
|
+
module Client
|
3
|
+
# Raised when connection with Gmail IMAP service couldn't be established.
|
4
|
+
class ConnectionError < SocketError; end
|
5
|
+
# Raised when given username or password are invalid.
|
6
|
+
class AuthorizationError < Net::IMAP::NoResponseError; end
|
7
|
+
# Raised when delivered email is invalid.
|
8
|
+
class DeliveryError < ArgumentError; end
|
9
|
+
# Raised when given client is not registered
|
10
|
+
class UnknownClient < ArgumentError; end
|
11
|
+
|
12
|
+
def self.clients
|
13
|
+
@clients ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register(name, klass)
|
17
|
+
clients[name] = klass
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.new(name, *args)
|
21
|
+
if client = clients[name]
|
22
|
+
client.new(*args)
|
23
|
+
else
|
24
|
+
raise UnknownClient, "No such client: #{name}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'gmail/imap_extensions'
|
29
|
+
require 'gmail/client/base'
|
30
|
+
require 'gmail/client/plain'
|
31
|
+
require 'gmail/client/xoauth'
|
32
|
+
require 'gmail/client/xoauth2'
|
33
|
+
end # Client
|
34
|
+
end # Gmail
|
data/lib/gmail/client/base.rb
CHANGED
@@ -1,229 +1,229 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
module Gmail
|
4
|
-
module Client
|
5
|
-
class Base
|
6
|
-
# Gmail IMAP defaults
|
7
|
-
GMAIL_IMAP_HOST = 'imap.gmail.com'
|
8
|
-
GMAIL_IMAP_PORT = 993
|
9
|
-
|
10
|
-
# Gmail SMTP defaults
|
11
|
-
GMAIL_SMTP_HOST = "smtp.gmail.com"
|
12
|
-
GMAIL_SMTP_PORT = 587
|
13
|
-
|
14
|
-
attr_reader :username
|
15
|
-
attr_reader :options
|
16
|
-
|
17
|
-
def initialize(username, options = {})
|
18
|
-
defaults = {}
|
19
|
-
@username = fill_username(username)
|
20
|
-
@options = defaults.merge(options)
|
21
|
-
@mailbox_mutex = Mutex.new
|
22
|
-
end
|
23
|
-
|
24
|
-
# Connect to gmail service.
|
25
|
-
def connect(raise_errors = false)
|
26
|
-
@imap = Net::IMAP.new(GMAIL_IMAP_HOST, GMAIL_IMAP_PORT, true, nil, false)
|
27
|
-
Gmail::ImapExtensions.patch_net_imap_response_parser
|
28
|
-
@imap
|
29
|
-
rescue SocketError
|
30
|
-
raise_errors and raise ConnectionError, "Couldn't establish connection with Gmail IMAP service"
|
31
|
-
end
|
32
|
-
|
33
|
-
# This version of connect will raise error on failure...
|
34
|
-
def connect!
|
35
|
-
connect(true)
|
36
|
-
end
|
37
|
-
|
38
|
-
# Return current connection. Log in automaticaly to specified account if
|
39
|
-
# it is necessary.
|
40
|
-
def connection
|
41
|
-
login and at_exit { logout } unless logged_in?
|
42
|
-
@imap
|
43
|
-
end
|
44
|
-
alias :conn :connection
|
45
|
-
|
46
|
-
# Login to specified account.
|
47
|
-
def login(*args)
|
48
|
-
raise NotImplementedError, "The `#{self.class.name}#login` method is not implemented."
|
49
|
-
end
|
50
|
-
alias :sign_in :login
|
51
|
-
|
52
|
-
# This version of login will raise error on failure...
|
53
|
-
def login!
|
54
|
-
login(true)
|
55
|
-
end
|
56
|
-
alias :sign_in! :login!
|
57
|
-
|
58
|
-
# Returns +true+ when you are logged in to specified account.
|
59
|
-
def logged_in?
|
60
|
-
!!@logged_in
|
61
|
-
end
|
62
|
-
alias :signed_in? :logged_in?
|
63
|
-
|
64
|
-
# Logout from Gmail service.
|
65
|
-
def logout
|
66
|
-
@imap && logged_in? and @imap.logout
|
67
|
-
ensure
|
68
|
-
@logged_in = false
|
69
|
-
end
|
70
|
-
alias :sign_out :logout
|
71
|
-
|
72
|
-
# Disconnect from Gmail service.
|
73
|
-
def disconnect
|
74
|
-
@imap && @imap.disconnect
|
75
|
-
end
|
76
|
-
|
77
|
-
# Return labels object, which helps you with managing your Gmail labels.
|
78
|
-
# See <tt>Gmail::Labels</tt> for details.
|
79
|
-
def labels
|
80
|
-
@labels ||= Labels.new(conn)
|
81
|
-
end
|
82
|
-
|
83
|
-
# Compose new e-mail.
|
84
|
-
#
|
85
|
-
# ==== Examples
|
86
|
-
#
|
87
|
-
# mail = gmail.compose
|
88
|
-
# mail.from "test@gmail.org"
|
89
|
-
# mail.to "friend@gmail.com"
|
90
|
-
#
|
91
|
-
# ... or block style:
|
92
|
-
#
|
93
|
-
# mail = gmail.compose do
|
94
|
-
# from "test@gmail.org"
|
95
|
-
# to "friend@gmail.com"
|
96
|
-
# subject "Hello!"
|
97
|
-
# body "Hello my friend! long time..."
|
98
|
-
# end
|
99
|
-
#
|
100
|
-
# Now you can deliver your mail:
|
101
|
-
#
|
102
|
-
# gmail.deliver(mail)
|
103
|
-
def compose(mail = nil, &block)
|
104
|
-
if block_given?
|
105
|
-
mail = Mail.new(&block)
|
106
|
-
elsif !mail
|
107
|
-
mail = Mail.new
|
108
|
-
end
|
109
|
-
|
110
|
-
mail.delivery_method(*smtp_settings)
|
111
|
-
mail.from = username unless mail.from
|
112
|
-
mail
|
113
|
-
end
|
114
|
-
alias :message :compose
|
115
|
-
|
116
|
-
# Compose (optionaly) and send given email.
|
117
|
-
#
|
118
|
-
# ==== Examples
|
119
|
-
#
|
120
|
-
# gmail.deliver do
|
121
|
-
# to "friend@gmail.com"
|
122
|
-
# subject "Hello friend!"
|
123
|
-
# body "Hi! How are you?"
|
124
|
-
# end
|
125
|
-
#
|
126
|
-
# ... or with already created message:
|
127
|
-
#
|
128
|
-
# mail = Mail.new { ... }
|
129
|
-
# gmail.deliver(mail)
|
130
|
-
#
|
131
|
-
# mail = gmail.compose { ... }
|
132
|
-
# gmail.deliver(mail)
|
133
|
-
def deliver(mail = nil, raise_errors = false, &block)
|
134
|
-
mail = compose(mail, &block)
|
135
|
-
mail.deliver!
|
136
|
-
rescue Object => ex
|
137
|
-
raise_errors and raise DeliveryError, "Couldn't deliver email: #{ex.to_s}"
|
138
|
-
end
|
139
|
-
|
140
|
-
# This version of deliver will raise error on failure...
|
141
|
-
def deliver!(mail = nil, &block)
|
142
|
-
deliver(mail, true, &block)
|
143
|
-
end
|
144
|
-
|
145
|
-
# Do something with given mailbox or within it context.
|
146
|
-
#
|
147
|
-
# ==== Examples
|
148
|
-
#
|
149
|
-
# mailbox = gmail.mailbox("INBOX")
|
150
|
-
# mailbox.emails(:all)
|
151
|
-
# mailbox.count(:unread, :before => Time.now-(20*24*3600))
|
152
|
-
#
|
153
|
-
# ... or block style:
|
154
|
-
#
|
155
|
-
# gmail.label("Work") do |mailbox|
|
156
|
-
# mailbox.emails(:unread)
|
157
|
-
# mailbox.count(:all)
|
158
|
-
# ...
|
159
|
-
# end
|
160
|
-
def mailbox(name, &block)
|
161
|
-
@mailbox_mutex.synchronize do
|
162
|
-
name = labels.localize(name)
|
163
|
-
mailbox = (mailboxes[name] ||= Mailbox.new(self, name))
|
164
|
-
switch_to_mailbox(mailbox) if @current_mailbox != mailbox
|
165
|
-
|
166
|
-
if block_given?
|
167
|
-
mailbox_stack << @current_mailbox
|
168
|
-
result = block.arity == 1 ? block.call(mailbox) : block.call
|
169
|
-
mailbox_stack.pop
|
170
|
-
switch_to_mailbox(mailbox_stack.last)
|
171
|
-
return result
|
172
|
-
end
|
173
|
-
|
174
|
-
return mailbox
|
175
|
-
end
|
176
|
-
end
|
177
|
-
alias :in_mailbox :mailbox
|
178
|
-
alias :in_label :mailbox
|
179
|
-
alias :label :mailbox
|
180
|
-
|
181
|
-
# Alias for <tt>mailbox("INBOX")</tt>. See <tt>Gmail::Client#mailbox</tt>
|
182
|
-
# for details.
|
183
|
-
def inbox
|
184
|
-
mailbox("INBOX")
|
185
|
-
end
|
186
|
-
|
187
|
-
def mailboxes
|
188
|
-
@mailboxes ||= {}
|
189
|
-
end
|
190
|
-
|
191
|
-
def inspect
|
192
|
-
"#<Gmail::Client#{'0x%04x' % (object_id << 1)} (#{username}) #{'dis' if !logged_in?}connected>"
|
193
|
-
end
|
194
|
-
|
195
|
-
def fill_username(username)
|
196
|
-
username =~ /@/ ? username : "#{username}@gmail.com"
|
197
|
-
end
|
198
|
-
|
199
|
-
def mail_domain
|
200
|
-
username.split('@').last
|
201
|
-
end
|
202
|
-
|
203
|
-
private
|
204
|
-
|
205
|
-
def switch_to_mailbox(mailbox)
|
206
|
-
if mailbox
|
207
|
-
conn.select(mailbox.encoded_name)
|
208
|
-
end
|
209
|
-
@current_mailbox = mailbox
|
210
|
-
end
|
211
|
-
|
212
|
-
def mailbox_stack
|
213
|
-
@mailbox_stack ||= []
|
214
|
-
end
|
215
|
-
|
216
|
-
def smtp_settings
|
217
|
-
[:smtp, {
|
218
|
-
:address => GMAIL_SMTP_HOST,
|
219
|
-
:port => GMAIL_SMTP_PORT,
|
220
|
-
:domain => mail_domain,
|
221
|
-
:user_name => username,
|
222
|
-
:password => password,
|
223
|
-
:authentication => 'plain',
|
224
|
-
:enable_starttls_auto => true
|
225
|
-
}]
|
226
|
-
end
|
227
|
-
end # Base
|
228
|
-
end # Client
|
229
|
-
end # Gmail
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Gmail
|
4
|
+
module Client
|
5
|
+
class Base
|
6
|
+
# Gmail IMAP defaults
|
7
|
+
GMAIL_IMAP_HOST = 'imap.gmail.com'
|
8
|
+
GMAIL_IMAP_PORT = 993
|
9
|
+
|
10
|
+
# Gmail SMTP defaults
|
11
|
+
GMAIL_SMTP_HOST = "smtp.gmail.com"
|
12
|
+
GMAIL_SMTP_PORT = 587
|
13
|
+
|
14
|
+
attr_reader :username
|
15
|
+
attr_reader :options
|
16
|
+
|
17
|
+
def initialize(username, options = {})
|
18
|
+
defaults = {}
|
19
|
+
@username = fill_username(username)
|
20
|
+
@options = defaults.merge(options)
|
21
|
+
@mailbox_mutex = Mutex.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# Connect to gmail service.
|
25
|
+
def connect(raise_errors = false)
|
26
|
+
@imap = Net::IMAP.new(GMAIL_IMAP_HOST, GMAIL_IMAP_PORT, true, nil, false)
|
27
|
+
Gmail::ImapExtensions.patch_net_imap_response_parser
|
28
|
+
@imap
|
29
|
+
rescue SocketError
|
30
|
+
raise_errors and raise ConnectionError, "Couldn't establish connection with Gmail IMAP service"
|
31
|
+
end
|
32
|
+
|
33
|
+
# This version of connect will raise error on failure...
|
34
|
+
def connect!
|
35
|
+
connect(true)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return current connection. Log in automaticaly to specified account if
|
39
|
+
# it is necessary.
|
40
|
+
def connection
|
41
|
+
login and at_exit { logout } unless logged_in?
|
42
|
+
@imap
|
43
|
+
end
|
44
|
+
alias :conn :connection
|
45
|
+
|
46
|
+
# Login to specified account.
|
47
|
+
def login(*args)
|
48
|
+
raise NotImplementedError, "The `#{self.class.name}#login` method is not implemented."
|
49
|
+
end
|
50
|
+
alias :sign_in :login
|
51
|
+
|
52
|
+
# This version of login will raise error on failure...
|
53
|
+
def login!
|
54
|
+
login(true)
|
55
|
+
end
|
56
|
+
alias :sign_in! :login!
|
57
|
+
|
58
|
+
# Returns +true+ when you are logged in to specified account.
|
59
|
+
def logged_in?
|
60
|
+
!!@logged_in
|
61
|
+
end
|
62
|
+
alias :signed_in? :logged_in?
|
63
|
+
|
64
|
+
# Logout from Gmail service.
|
65
|
+
def logout
|
66
|
+
@imap && logged_in? and @imap.logout
|
67
|
+
ensure
|
68
|
+
@logged_in = false
|
69
|
+
end
|
70
|
+
alias :sign_out :logout
|
71
|
+
|
72
|
+
# Disconnect from Gmail service.
|
73
|
+
def disconnect
|
74
|
+
@imap && @imap.disconnect
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return labels object, which helps you with managing your Gmail labels.
|
78
|
+
# See <tt>Gmail::Labels</tt> for details.
|
79
|
+
def labels
|
80
|
+
@labels ||= Labels.new(conn)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Compose new e-mail.
|
84
|
+
#
|
85
|
+
# ==== Examples
|
86
|
+
#
|
87
|
+
# mail = gmail.compose
|
88
|
+
# mail.from "test@gmail.org"
|
89
|
+
# mail.to "friend@gmail.com"
|
90
|
+
#
|
91
|
+
# ... or block style:
|
92
|
+
#
|
93
|
+
# mail = gmail.compose do
|
94
|
+
# from "test@gmail.org"
|
95
|
+
# to "friend@gmail.com"
|
96
|
+
# subject "Hello!"
|
97
|
+
# body "Hello my friend! long time..."
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# Now you can deliver your mail:
|
101
|
+
#
|
102
|
+
# gmail.deliver(mail)
|
103
|
+
def compose(mail = nil, &block)
|
104
|
+
if block_given?
|
105
|
+
mail = Mail.new(&block)
|
106
|
+
elsif !mail
|
107
|
+
mail = Mail.new
|
108
|
+
end
|
109
|
+
|
110
|
+
mail.delivery_method(*smtp_settings)
|
111
|
+
mail.from = username unless mail.from
|
112
|
+
mail
|
113
|
+
end
|
114
|
+
alias :message :compose
|
115
|
+
|
116
|
+
# Compose (optionaly) and send given email.
|
117
|
+
#
|
118
|
+
# ==== Examples
|
119
|
+
#
|
120
|
+
# gmail.deliver do
|
121
|
+
# to "friend@gmail.com"
|
122
|
+
# subject "Hello friend!"
|
123
|
+
# body "Hi! How are you?"
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# ... or with already created message:
|
127
|
+
#
|
128
|
+
# mail = Mail.new { ... }
|
129
|
+
# gmail.deliver(mail)
|
130
|
+
#
|
131
|
+
# mail = gmail.compose { ... }
|
132
|
+
# gmail.deliver(mail)
|
133
|
+
def deliver(mail = nil, raise_errors = false, &block)
|
134
|
+
mail = compose(mail, &block)
|
135
|
+
mail.deliver!
|
136
|
+
rescue Object => ex
|
137
|
+
raise_errors and raise DeliveryError, "Couldn't deliver email: #{ex.to_s}"
|
138
|
+
end
|
139
|
+
|
140
|
+
# This version of deliver will raise error on failure...
|
141
|
+
def deliver!(mail = nil, &block)
|
142
|
+
deliver(mail, true, &block)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Do something with given mailbox or within it context.
|
146
|
+
#
|
147
|
+
# ==== Examples
|
148
|
+
#
|
149
|
+
# mailbox = gmail.mailbox("INBOX")
|
150
|
+
# mailbox.emails(:all)
|
151
|
+
# mailbox.count(:unread, :before => Time.now-(20*24*3600))
|
152
|
+
#
|
153
|
+
# ... or block style:
|
154
|
+
#
|
155
|
+
# gmail.label("Work") do |mailbox|
|
156
|
+
# mailbox.emails(:unread)
|
157
|
+
# mailbox.count(:all)
|
158
|
+
# ...
|
159
|
+
# end
|
160
|
+
def mailbox(name, &block)
|
161
|
+
@mailbox_mutex.synchronize do
|
162
|
+
name = labels.localize(name)
|
163
|
+
mailbox = (mailboxes[name] ||= Mailbox.new(self, name))
|
164
|
+
switch_to_mailbox(mailbox) if @current_mailbox != mailbox
|
165
|
+
|
166
|
+
if block_given?
|
167
|
+
mailbox_stack << @current_mailbox
|
168
|
+
result = block.arity == 1 ? block.call(mailbox) : block.call
|
169
|
+
mailbox_stack.pop
|
170
|
+
switch_to_mailbox(mailbox_stack.last)
|
171
|
+
return result
|
172
|
+
end
|
173
|
+
|
174
|
+
return mailbox
|
175
|
+
end
|
176
|
+
end
|
177
|
+
alias :in_mailbox :mailbox
|
178
|
+
alias :in_label :mailbox
|
179
|
+
alias :label :mailbox
|
180
|
+
|
181
|
+
# Alias for <tt>mailbox("INBOX")</tt>. See <tt>Gmail::Client#mailbox</tt>
|
182
|
+
# for details.
|
183
|
+
def inbox
|
184
|
+
mailbox("INBOX")
|
185
|
+
end
|
186
|
+
|
187
|
+
def mailboxes
|
188
|
+
@mailboxes ||= {}
|
189
|
+
end
|
190
|
+
|
191
|
+
def inspect
|
192
|
+
"#<Gmail::Client#{'0x%04x' % (object_id << 1)} (#{username}) #{'dis' if !logged_in?}connected>"
|
193
|
+
end
|
194
|
+
|
195
|
+
def fill_username(username)
|
196
|
+
username =~ /@/ ? username : "#{username}@gmail.com"
|
197
|
+
end
|
198
|
+
|
199
|
+
def mail_domain
|
200
|
+
username.split('@').last
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
def switch_to_mailbox(mailbox)
|
206
|
+
if mailbox
|
207
|
+
conn.select(mailbox.encoded_name)
|
208
|
+
end
|
209
|
+
@current_mailbox = mailbox
|
210
|
+
end
|
211
|
+
|
212
|
+
def mailbox_stack
|
213
|
+
@mailbox_stack ||= []
|
214
|
+
end
|
215
|
+
|
216
|
+
def smtp_settings
|
217
|
+
[:smtp, {
|
218
|
+
:address => GMAIL_SMTP_HOST,
|
219
|
+
:port => GMAIL_SMTP_PORT,
|
220
|
+
:domain => mail_domain,
|
221
|
+
:user_name => username,
|
222
|
+
:password => password,
|
223
|
+
:authentication => 'plain',
|
224
|
+
:enable_starttls_auto => true
|
225
|
+
}]
|
226
|
+
end
|
227
|
+
end # Base
|
228
|
+
end # Client
|
229
|
+
end # Gmail
|