gmail 0.3.4 → 0.4.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.
@@ -1,5 +1,11 @@
1
1
  # Gmail gem changelog
2
2
 
3
+ ## 0.4.0
4
+
5
+ * Added XOAUTH authentication method (Thanks Stefano Bernardi and Nicolas Fouché)
6
+ * Separated clients
7
+ * Fixed specs
8
+
3
9
  ## 0.3.4
4
10
 
5
11
  * Fixes in mailbox filters shortcuts (Thanks Benjamin Bock)
data/README.md CHANGED
@@ -14,6 +14,9 @@ API, is well tested, better documented and have many other improvements.
14
14
 
15
15
  Extra thanks for specific feature contributions from:
16
16
 
17
+ * [Nicolas Fouché](http://github.com/nfo)
18
+ * [Stefano Bernardi](http://github.com/stefanobernardi)
19
+ * [Benjamin Bock](http://github.com/bb)
17
20
  * [Arthur Chiu](http://github.com/achiu)
18
21
  * [Justin Perkins](http://github.com/justinperkins)
19
22
  * [Mikkel Malmberg](http://github.com/mikker)
data/TODO.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # TODO
2
2
 
3
- * Update docs and readme
3
+ * Info about xoauth and plain authentication in readme
4
+ * Specs for xoauth
4
5
  * Specs for message operations
5
6
  * Specs for filters in mailbox
6
7
  * Non-English accounts
@@ -15,8 +15,9 @@ Gem::Specification.new do |s|
15
15
  s.require_paths = %w[lib]
16
16
  s.extra_rdoc_files = %w[LICENSE README.md CHANGELOG.md TODO.md]
17
17
 
18
- s.add_runtime_dependency 'mime', ['>= 0.1']
19
- s.add_runtime_dependency 'mail', ['>= 2.2.1']
20
- s.add_development_dependency 'rspec', ['~> 2.0']
21
- s.add_development_dependency 'mocha', ['>= 0.9']
18
+ s.add_runtime_dependency 'mime', '>= 0.1'
19
+ s.add_runtime_dependency 'mail', '>= 2.2.1'
20
+ s.add_runtime_dependency 'gmail_xoauth', '>= 0.3.0'
21
+ s.add_development_dependency 'rspec', '~> 2.0'
22
+ s.add_development_dependency 'mocha', '>= 0.9'
22
23
  end
@@ -22,26 +22,61 @@ module Gmail
22
22
  autoload :Message, "gmail/message"
23
23
 
24
24
  class << self
25
- def new(username, password, options={}, &block)
26
- client = Client.new(username, password, options)
25
+
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
+ def new(*args, &block)
48
+ client = connect_with_proper_client(*args)
27
49
  client.connect and client.login
28
- if block_given?
29
- yield client
30
- client.logout
31
- end
32
- client
50
+ perform_block(client, &block)
33
51
  end
34
52
  alias :connect :new
35
-
36
- def new!(username, password, options={}, &block)
37
- client = Client.new(username, password, options)
53
+
54
+ def new!(*args, &block)
55
+ client = connect_with_proper_client(*args)
38
56
  client.connect! and client.login!
57
+ perform_block(client, &block)
58
+ end
59
+ alias :connect! :new!
60
+
61
+ protected
62
+
63
+ def connect_with_proper_client(*args)
64
+ if args.first.is_a?(Symbol)
65
+ login_method = args.shift
66
+ else
67
+ login_method ||= :plain
68
+ end
69
+
70
+ Client.send("new_#{login_method}", *args)
71
+ end
72
+
73
+ def perform_block(client, &block)
39
74
  if block_given?
40
75
  yield client
41
76
  client.logout
42
77
  end
43
78
  client
44
79
  end
45
- alias :connect! :new!
80
+
46
81
  end # << self
47
82
  end # Gmail
@@ -1,221 +1,22 @@
1
1
  module Gmail
2
- class Client
2
+ module Client
3
3
  # Raised when connection with GMail IMAP service couldn't be established.
4
4
  class ConnectionError < SocketError; end
5
5
  # Raised when given username or password are invalid.
6
6
  class AuthorizationError < Net::IMAP::NoResponseError; end
7
7
  # Raised when delivered email is invalid.
8
8
  class DeliveryError < ArgumentError; end
9
-
10
- # GMail IMAP defaults
11
- GMAIL_IMAP_HOST = 'imap.gmail.com'
12
- GMAIL_IMAP_PORT = 993
13
9
 
14
- # GMail SMTP defaults
15
- GMAIL_SMTP_HOST = "smtp.gmail.com"
16
- GMAIL_SMTP_PORT = 587
17
-
18
- attr_reader :username
19
- attr_reader :password
20
- attr_reader :options
21
-
22
- def initialize(username, password, options={})
23
- defaults = {}
24
- @username = fill_username(username)
25
- @password = password
26
- @options = defaults.merge(options)
27
- end
28
-
29
- # Connect to gmail service.
30
- def connect(raise_errors=false)
31
- @imap = Net::IMAP.new(GMAIL_IMAP_HOST, GMAIL_IMAP_PORT, true, nil, false)
32
- rescue SocketError
33
- raise_errors and raise ConnectionError, "Couldn't establish connection with GMail IMAP service"
34
- end
35
-
36
- # This version of connect will raise error on failure...
37
- def connect!
38
- connect(true)
39
- end
40
-
41
- # Return current connection. Log in automaticaly to specified account if
42
- # it is necessary.
43
- def connection
44
- login and at_exit { logout } unless logged_in?
45
- @imap
46
- end
47
- alias :conn :connection
48
-
49
- # Login to specified account.
50
- def login(raise_errors=false)
51
- @imap and @logged_in = (login = @imap.login(username, password)) && login.name == 'OK'
52
- rescue Net::IMAP::NoResponseError
53
- raise_errors and raise AuthorizationError, "Couldn't login to given GMail account: #{username}"
54
- end
55
- alias :sign_in :login
56
-
57
- # This version of login will raise error on failure...
58
- def login!
59
- login(true)
60
- end
61
- alias :sign_in! :login!
62
-
63
- # Returns +true+ when you are logged in to specified account.
64
- def logged_in?
65
- !!@logged_in
66
- end
67
- alias :signed_in? :logged_in?
68
-
69
- # Logout from GMail service.
70
- def logout
71
- @imap && logged_in? and @imap.logout
72
- ensure
73
- @logged_in = false
74
- end
75
- alias :sign_out :logout
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
- mail.delivery_method(*smtp_settings)
110
- mail.from = username unless mail.from
111
- mail
112
- end
113
- alias :message :compose
114
-
115
- # Compose (optionaly) and send given email.
116
- #
117
- # ==== Examples
118
- #
119
- # gmail.deliver do
120
- # to "friend@gmail.com"
121
- # subject "Hello friend!"
122
- # body "Hi! How are you?"
123
- # end
124
- #
125
- # ... or with already created message:
126
- #
127
- # mail = Mail.new { ... }
128
- # gmail.deliver(mail)
129
- #
130
- # mail = gmail.compose { ... }
131
- # gmail.deliver(mail)
132
- def deliver(mail=nil, raise_errors=false, &block)
133
- mail = compose(mail, &block) if block_given?
134
- mail.deliver!
135
- rescue Object => ex
136
- raise_errors and raise DeliveryError, "Couldn't deliver email: #{ex.to_s}"
137
- end
138
-
139
- # This version of deliver will raise error on failure...
140
- def deliver!(mail=nil, &block)
141
- deliver(mail, true, &block)
142
- end
143
-
144
- # Do something with given mailbox or within it context.
145
- #
146
- # ==== Examples
147
- #
148
- # mailbox = gmail.mailbox("INBOX")
149
- # mailbox.emails(:all)
150
- # mailbox.count(:unread, :before => Time.now-(20*24*3600))
151
- #
152
- # ... or block style:
153
- #
154
- # gmail.label("Work") do |mailbox|
155
- # mailbox.emails(:unread)
156
- # mailbox.count(:all)
157
- # ...
158
- # end
159
- def mailbox(name, &block)
160
- name = name.to_s
161
- mailbox = (mailboxes[name] ||= Mailbox.new(self, name))
162
- switch_to_mailbox(name) if @current_mailbox != name
163
- if block_given?
164
- mailbox_stack << @current_mailbox
165
- result = block.arity == 1 ? block.call(mailbox) : block.call
166
- mailbox_stack.pop
167
- switch_to_mailbox(mailbox_stack.last)
168
- return result
169
- end
170
- mailbox
171
- end
172
- alias :in_mailbox :mailbox
173
- alias :in_label :mailbox
174
- alias :label :mailbox
175
-
176
- # Alias for <tt>mailbox("INBOX")</tt>. See <tt>Gmail::Client#mailbox</tt>
177
- # for details.
178
- def inbox
179
- mailbox("INBOX")
180
- end
181
-
182
- def mailboxes
183
- @mailboxes ||= {}
184
- end
185
-
186
- def inspect
187
- "#<Gmail::Client#{'0x%04x' % (object_id << 1)} (#{username}) #{'dis' if !logged_in?}connected>"
188
- end
189
-
190
- def fill_username(username)
191
- username =~ /@/ ? username : "#{username}@gmail.com"
192
- end
10
+ autoload :Base, 'gmail/client/base'
11
+ autoload :Plain, 'gmail/client/plain'
12
+ autoload :XOAuth, 'gmail/client/xoauth'
193
13
 
194
- def mail_domain
195
- username.split('@')[0]
196
- end
197
-
198
- private
199
-
200
- def switch_to_mailbox(mailbox)
201
- conn.select(mailbox) if mailbox
202
- @current_mailbox = mailbox
203
- end
204
-
205
- def mailbox_stack
206
- @mailbox_stack ||= []
14
+ def self.new_plain(*args)
15
+ Gmail::Client::Plain.new(*args)
207
16
  end
208
-
209
- def smtp_settings
210
- [:smtp, {
211
- :address => GMAIL_SMTP_HOST,
212
- :port => GMAIL_SMTP_PORT,
213
- :domain => mail_domain,
214
- :user_name => username,
215
- :password => password,
216
- :authentication => 'plain',
217
- :enable_starttls_auto => true
218
- }]
17
+
18
+ def self.new_xoauth(*args)
19
+ Gmail::Client::XOAuth.new(*args)
219
20
  end
220
21
  end # Client
221
22
  end # Gmail
@@ -0,0 +1,220 @@
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
+ rescue SocketError
28
+ raise_errors and raise ConnectionError, "Couldn't establish connection with GMail IMAP service"
29
+ end
30
+
31
+ # This version of connect will raise error on failure...
32
+ def connect!
33
+ connect(true)
34
+ end
35
+
36
+ # Return current connection. Log in automaticaly to specified account if
37
+ # it is necessary.
38
+ def connection
39
+ login and at_exit { logout } unless logged_in?
40
+ @imap
41
+ end
42
+ alias :conn :connection
43
+
44
+ # Login to specified account.
45
+ def login(*args)
46
+ raise NotImplementedError, "The `#{self.class.name}#login` method is not implemented."
47
+ end
48
+ alias :sign_in :login
49
+
50
+ # This version of login will raise error on failure...
51
+ def login!
52
+ login(true)
53
+ end
54
+ alias :sign_in! :login!
55
+
56
+ # Returns +true+ when you are logged in to specified account.
57
+ def logged_in?
58
+ !!@logged_in
59
+ end
60
+ alias :signed_in? :logged_in?
61
+
62
+ # Logout from GMail service.
63
+ def logout
64
+ @imap && logged_in? and @imap.logout
65
+ ensure
66
+ @logged_in = false
67
+ end
68
+ alias :sign_out :logout
69
+
70
+ # Return labels object, which helps you with managing your GMail labels.
71
+ # See <tt>Gmail::Labels</tt> for details.
72
+ def labels
73
+ @labels ||= Labels.new(conn)
74
+ end
75
+
76
+ # Compose new e-mail.
77
+ #
78
+ # ==== Examples
79
+ #
80
+ # mail = gmail.compose
81
+ # mail.from "test@gmail.org"
82
+ # mail.to "friend@gmail.com"
83
+ #
84
+ # ... or block style:
85
+ #
86
+ # mail = gmail.compose do
87
+ # from "test@gmail.org"
88
+ # to "friend@gmail.com"
89
+ # subject "Hello!"
90
+ # body "Hello my friend! long time..."
91
+ # end
92
+ #
93
+ # Now you can deliver your mail:
94
+ #
95
+ # gmail.deliver(mail)
96
+ def compose(mail=nil, &block)
97
+ if block_given?
98
+ mail = Mail.new(&block)
99
+ elsif !mail
100
+ mail = Mail.new
101
+ end
102
+
103
+ mail.delivery_method(*smtp_settings)
104
+ mail.from = username unless mail.from
105
+ mail
106
+ end
107
+ alias :message :compose
108
+
109
+ # Compose (optionaly) and send given email.
110
+ #
111
+ # ==== Examples
112
+ #
113
+ # gmail.deliver do
114
+ # to "friend@gmail.com"
115
+ # subject "Hello friend!"
116
+ # body "Hi! How are you?"
117
+ # end
118
+ #
119
+ # ... or with already created message:
120
+ #
121
+ # mail = Mail.new { ... }
122
+ # gmail.deliver(mail)
123
+ #
124
+ # mail = gmail.compose { ... }
125
+ # gmail.deliver(mail)
126
+ def deliver(mail=nil, raise_errors=false, &block)
127
+ mail = compose(mail, &block) if block_given?
128
+ mail.deliver!
129
+ rescue Object => ex
130
+ raise_errors and raise DeliveryError, "Couldn't deliver email: #{ex.to_s}"
131
+ end
132
+
133
+ # This version of deliver will raise error on failure...
134
+ def deliver!(mail=nil, &block)
135
+ deliver(mail, true, &block)
136
+ end
137
+
138
+ # Do something with given mailbox or within it context.
139
+ #
140
+ # ==== Examples
141
+ #
142
+ # mailbox = gmail.mailbox("INBOX")
143
+ # mailbox.emails(:all)
144
+ # mailbox.count(:unread, :before => Time.now-(20*24*3600))
145
+ #
146
+ # ... or block style:
147
+ #
148
+ # gmail.label("Work") do |mailbox|
149
+ # mailbox.emails(:unread)
150
+ # mailbox.count(:all)
151
+ # ...
152
+ # end
153
+ def mailbox(name, &block)
154
+ @mailbox_mutex.synchronize do
155
+ name = Net::IMAP.encode_utf7(name.to_s)
156
+ mailbox = (mailboxes[name] ||= Mailbox.new(self, name))
157
+ switch_to_mailbox(name) if @current_mailbox != name
158
+
159
+ if block_given?
160
+ mailbox_stack << @current_mailbox
161
+ result = block.arity == 1 ? block.call(mailbox) : block.call
162
+ mailbox_stack.pop
163
+ switch_to_mailbox(mailbox_stack.last)
164
+ return result
165
+ end
166
+
167
+ return mailbox
168
+ end
169
+ end
170
+ alias :in_mailbox :mailbox
171
+ alias :in_label :mailbox
172
+ alias :label :mailbox
173
+
174
+ # Alias for <tt>mailbox("INBOX")</tt>. See <tt>Gmail::Client#mailbox</tt>
175
+ # for details.
176
+ def inbox
177
+ mailbox("INBOX")
178
+ end
179
+
180
+ def mailboxes
181
+ @mailboxes ||= {}
182
+ end
183
+
184
+ def inspect
185
+ "#<Gmail::Client#{'0x%04x' % (object_id << 1)} (#{username}) #{'dis' if !logged_in?}connected>"
186
+ end
187
+
188
+ def fill_username(username)
189
+ username =~ /@/ ? username : "#{username}@gmail.com"
190
+ end
191
+
192
+ def mail_domain
193
+ username.split('@')[0]
194
+ end
195
+
196
+ private
197
+
198
+ def switch_to_mailbox(mailbox)
199
+ conn.select(mailbox) if mailbox
200
+ @current_mailbox = mailbox
201
+ end
202
+
203
+ def mailbox_stack
204
+ @mailbox_stack ||= []
205
+ end
206
+
207
+ def smtp_settings
208
+ [:smtp, {
209
+ :address => GMAIL_SMTP_HOST,
210
+ :port => GMAIL_SMTP_PORT,
211
+ :domain => mail_domain,
212
+ :user_name => username,
213
+ :password => password,
214
+ :authentication => 'plain',
215
+ :enable_starttls_auto => true
216
+ }]
217
+ end
218
+ end # Base
219
+ end # Client
220
+ end # Gmail
@@ -0,0 +1,18 @@
1
+ module Gmail
2
+ module Client
3
+ class Plain < Base
4
+ attr_reader :password
5
+
6
+ def initialize(username, password, options={})
7
+ @password = password
8
+ super(username, options)
9
+ end
10
+
11
+ def login(raise_errors=false)
12
+ @imap and @logged_in = (login = @imap.login(username, password)) && login.name == 'OK'
13
+ rescue Net::IMAP::NoResponseError
14
+ raise_errors and raise AuthorizationError, "Couldn't login to given GMail account: #{username}"
15
+ end
16
+ end # Plain
17
+ end # Client
18
+ end # Gmail
@@ -0,0 +1,49 @@
1
+ require 'gmail_xoauth'
2
+
3
+ module Gmail
4
+ module Client
5
+ class XOAuth < Base
6
+ attr_reader :token
7
+ attr_reader :secret
8
+ attr_reader :consumer_key
9
+ attr_reader :consumer_secret
10
+
11
+ def initialize(username, options={})
12
+ @token = options.delete(:token)
13
+ @secret = options.delete(:secret)
14
+ @consumer_key = options.delete(:consumer_key)
15
+ @consumer_secret = options.delete(:consumer_secret)
16
+
17
+ super(username, options)
18
+ end
19
+
20
+ def login(raise_errors=false)
21
+ @imap and @logged_in = (login = @imap.authenticate('XOAUTH', username,
22
+ :consumer_key => consumer_key,
23
+ :consumer_secret => consumer_secret,
24
+ :token => token,
25
+ :token_secret => secret
26
+ )) && login.name == 'OK'
27
+ rescue
28
+ raise_errors and raise AuthorizationError, "Couldn't login to given GMail account: #{username}"
29
+ end
30
+
31
+ def smtp_settings
32
+ [:smtp, {
33
+ :address => GMAIL_SMTP_HOST,
34
+ :port => GMAIL_SMTP_PORT,
35
+ :domain => mail_domain,
36
+ :user_name => username,
37
+ :password => secret = {
38
+ :consumer_key => consumer_key,
39
+ :consumer_secret => consumer_secret,
40
+ :token => token,
41
+ :token_secret => token_secret
42
+ },
43
+ :authentication => :xoauth,
44
+ :enable_starttls_auto => true
45
+ }]
46
+ end
47
+ end # XOAuth
48
+ end # Client
49
+ end # Gmail
@@ -11,7 +11,7 @@ module Gmail
11
11
  # Get list of all defined labels.
12
12
  def all
13
13
  (conn.list("", "%")+conn.list("[Gmail]/", "%")).inject([]) do |labels,label|
14
- label[:name].each_line {|l| labels << l }
14
+ label[:name].each_line {|l| labels << Net::IMAP.decode_utf7(l) }
15
15
  labels
16
16
  end
17
17
  end
@@ -24,20 +24,20 @@ module Gmail
24
24
 
25
25
  # Returns +true+ when given label defined.
26
26
  def exists?(label)
27
- all.include?(label)
27
+ all.include?(Net::IMAP.encode_utf7(label))
28
28
  end
29
29
  alias :exist? :exists?
30
30
 
31
31
  # Creates given label in your account.
32
32
  def create(label)
33
- !!conn.create(label) rescue false
33
+ !!conn.create(Net::IMAP.encode_utf7(label)) rescue false
34
34
  end
35
35
  alias :new :create
36
36
  alias :add :create
37
37
 
38
38
  # Deletes given label from your account.
39
39
  def delete(label)
40
- !!conn.delete(label) rescue false
40
+ !!conn.delete(Net::IMAP.encode_utf7(label)) rescue false
41
41
  end
42
42
  alias :remove :delete
43
43
 
@@ -17,9 +17,11 @@ module Gmail
17
17
  }
18
18
 
19
19
  attr_reader :name
20
+ attr_reader :external_name
20
21
 
21
22
  def initialize(gmail, name="INBOX")
22
23
  @name = name
24
+ @external_name = Net::IMAP.decode_utf7(name)
23
25
  @gmail = gmail
24
26
  end
25
27
 
@@ -53,8 +55,10 @@ module Gmail
53
55
  opts[:subject] and search.concat ['SUBJECT', opts[:subject]]
54
56
  opts[:label] and search.concat ['LABEL', opts[:label]]
55
57
  opts[:attachment] and search.concat ['HAS', 'attachment']
56
- opts[:search] and search.concat [opts[:search]]
57
-
58
+ opts[:search] and search.concat ['BODY', opts[:search]]
59
+ opts[:body] and search.concat ['BODY', opts[:body]]
60
+ opts[:query] and search.concat opts[:query]
61
+
58
62
  @gmail.mailbox(name) do
59
63
  @gmail.conn.uid_search(search).collect do |uid|
60
64
  message = (messages[uid] ||= Message.new(self, uid))
@@ -97,7 +101,7 @@ module Gmail
97
101
  end
98
102
 
99
103
  def inspect
100
- "#<Gmail::Mailbox#{'0x%04x' % (object_id << 1)} name=#{@name}>"
104
+ "#<Gmail::Mailbox#{'0x%04x' % (object_id << 1)} name=#{external_name}>"
101
105
  end
102
106
 
103
107
  def to_s
@@ -64,11 +64,14 @@ module Gmail
64
64
  unflag('[Gmail]/Starred')
65
65
  end
66
66
 
67
- # Move to trash.
67
+ # Move to trash / bin.
68
68
  def delete!
69
69
  @mailbox.messages.delete(uid)
70
- flag(:Deleted)
71
- move_to('[Gmail]/Trash') unless %w[[Gmail]/Spam [Gmail]/Trash].include?(@mailbox.name)
70
+ flag(:deleted)
71
+
72
+ # For some, it's called "Trash", for others, it's called "Bin". Support both.
73
+ trash = @gmail.labels.exist?('[Gmail]/Bin') ? '[Gmail]/Bin' : '[Gmail]/Trash'
74
+ move_to(trash) unless %w[[Gmail]/Spam [Gmail]/Bin [Gmail]/Trash].include?(@mailbox.name)
72
75
  end
73
76
 
74
77
  # Archive this message.
@@ -78,7 +81,8 @@ module Gmail
78
81
 
79
82
  # Move to given box and delete from others.
80
83
  def move_to(name, from=nil)
81
- label(name, from) && delete!
84
+ label(name, from)
85
+ delete! if !%w[[Gmail]/Bin [Gmail]/Trash].include?(name)
82
86
  end
83
87
  alias :move :move_to
84
88
 
@@ -94,11 +98,10 @@ module Gmail
94
98
  #
95
99
  # See also <tt>Gmail::Message#label!</tt>.
96
100
  def label(name, from=nil)
97
- @gmail.mailbox(from || @mailbox.name) { @gmail.conn.uid_copy(uid, name) }
101
+ @gmail.mailbox(Net::IMAP.encode_utf7(from || @mailbox.external_name)) { @gmail.conn.uid_copy(uid, Net::IMAP.encode_utf7(name)) }
98
102
  rescue Net::IMAP::NoResponseError
99
103
  raise NoLabelError, "Label '#{name}' doesn't exist!"
100
104
  end
101
- alias :add_label :label
102
105
 
103
106
  # Mark this message with given label. When given label doesn't exist then
104
107
  # it will be automaticaly created.
@@ -107,10 +110,11 @@ module Gmail
107
110
  def label!(name, from=nil)
108
111
  label(name, from)
109
112
  rescue NoLabelError
110
- @gmail.labels.add(name)
111
- label!(name, from)
113
+ @gmail.labels.add(Net::IMAP.encode_utf7(name))
114
+ label(name, from)
112
115
  end
113
- alias :add_label! :add_label
116
+ alias :add_label :label!
117
+ alias :add_label! :label!
114
118
 
115
119
  # Remove given label from this message.
116
120
  def remove_label!(name)
@@ -119,7 +123,7 @@ module Gmail
119
123
  alias :delete_label! :remove_label!
120
124
 
121
125
  def inspect
122
- "#<Gmail::Message#{'0x%04x' % (object_id << 1)} mailbox=#{@mailbox.name}#{' uid='+@uid.to_s if @uid}#{' message_id='+@message_id.to_s if @message_id}>"
126
+ "#<Gmail::Message#{'0x%04x' % (object_id << 1)} mailbox=#{@mailbox.external_name}#{' uid='+@uid.to_s if @uid}#{' message_id='+@message_id.to_s if @message_id}>"
123
127
  end
124
128
 
125
129
  def method_missing(meth, *args, &block)
@@ -132,19 +136,29 @@ module Gmail
132
136
  super(meth, *args, &block)
133
137
  end
134
138
  end
135
-
139
+
140
+ def respond_to?(meth, *args, &block)
141
+ if envelope.respond_to?(meth)
142
+ return true
143
+ elsif message.respond_to?(meth)
144
+ return true
145
+ else
146
+ super(meth, *args, &block)
147
+ end
148
+ end
149
+
136
150
  def envelope
137
151
  @envelope ||= @gmail.mailbox(@mailbox.name) {
138
152
  @gmail.conn.uid_fetch(uid, "ENVELOPE")[0].attr["ENVELOPE"]
139
153
  }
140
154
  end
141
-
142
- private
143
155
 
144
156
  def message
145
157
  @message ||= Mail.new(@gmail.mailbox(@mailbox.name) {
146
158
  @gmail.conn.uid_fetch(uid, "RFC822")[0].attr["RFC822"] # RFC822
147
159
  })
148
160
  end
161
+ alias_method :raw_message, :message
162
+
149
163
  end # Message
150
164
  end # Gmail
@@ -1,8 +1,8 @@
1
1
  module Gmail
2
2
  class Version #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 3
5
- PATCH = 4
4
+ MINOR = 4
5
+ PATCH = 0
6
6
  STRING = [MAJOR, MINOR, PATCH].join('.')
7
7
  end # Version
8
8
 
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Gmail client" do
4
- subject { Gmail::Client }
3
+ describe "Gmail client (Plain)" do
4
+ subject { Gmail::Client::Plain }
5
5
 
6
6
  context "on initialize" do
7
7
  it "should set username, password and options" do
@@ -19,7 +19,7 @@ describe "Gmail client" do
19
19
 
20
20
  context "instance" do
21
21
  def mock_client(&block)
22
- client = Gmail::Client.new(*TEST_ACCOUNT)
22
+ client = Gmail::Client::Plain.new(*TEST_ACCOUNT)
23
23
  if block_given?
24
24
  client.connect
25
25
  yield client
@@ -45,7 +45,7 @@ describe "Gmail client" do
45
45
 
46
46
  it "should raise error when given GMail account is invalid and errors enabled" do
47
47
  lambda {
48
- client = Gmail::Client.new("foo", "bar")
48
+ client = Gmail::Client::Plain.new("foo", "bar")
49
49
  client.connect.should be_true
50
50
  client.login!.should_not be_true
51
51
  }.should raise_error(Gmail::Client::AuthorizationError)
@@ -53,7 +53,7 @@ describe "Gmail client" do
53
53
 
54
54
  it "shouldn't login when given GMail account is invalid" do
55
55
  lambda {
56
- client = Gmail::Client.new("foo", "bar")
56
+ client = Gmail::Client::Plain.new("foo", "bar")
57
57
  client.connect.should be_true
58
58
  client.login.should_not be_true
59
59
  }.should_not raise_error(Gmail::Client::AuthorizationError)
@@ -137,7 +137,7 @@ describe "Gmail client" do
137
137
 
138
138
  context "labels" do
139
139
  subject {
140
- client = Gmail::Client.new(*TEST_ACCOUNT)
140
+ client = Gmail::Client::Plain.new(*TEST_ACCOUNT)
141
141
  client.connect
142
142
  client.labels
143
143
  }
@@ -8,14 +8,14 @@ describe "Any object" do
8
8
  %w[new new!].each do |method|
9
9
  it "##{method} should properly connect with GMail service and return valid connection object" do
10
10
  gmail = Gmail.send(method, *TEST_ACCOUNT)
11
- gmail.should be_kind_of(Gmail::Client)
11
+ gmail.should be_kind_of(Gmail::Client::Plain)
12
12
  gmail.connection.should_not be_nil
13
13
  gmail.should be_logged_in
14
14
  end
15
15
 
16
16
  it "##{method} should connect with client and give it context when block given" do
17
17
  Gmail.send(method, *TEST_ACCOUNT) do |gmail|
18
- gmail.should be_kind_of(Gmail::Client)
18
+ gmail.should be_kind_of(Gmail::Client::Plain)
19
19
  gmail.connection.should_not be_nil
20
20
  gmail.should be_logged_in
21
21
  end
@@ -34,5 +34,16 @@ describe "A Gmail mailbox" do
34
34
  mailbox.emails(:all, :from => message.from.first.name) == message.from.first.name
35
35
  end
36
36
  end
37
+
38
+ it "should be able to do a full text search of message bodies" do
39
+ pending "This can wait..."
40
+ #mock_mailbox do |mailbox|
41
+ # message = mailbox.emails.first
42
+ # body = message.parts.blank? ? message.body.decoded : message.parts[0].body.decoded
43
+ # emails = mailbox.emails(:search => body.split(' ').first)
44
+ # emails.size.should > 0
45
+ #end
46
+ end
47
+
37
48
  end
38
49
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gmail
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 15
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
- - 3
8
8
  - 4
9
- version: 0.3.4
9
+ - 0
10
+ version: 0.4.0
10
11
  platform: ruby
11
12
  authors:
12
13
  - BehindLogic
@@ -15,7 +16,7 @@ autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2010-11-21 00:00:00 +01:00
19
+ date: 2011-01-13 00:00:00 +01:00
19
20
  default_executable:
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency
@@ -26,6 +27,7 @@ dependencies:
26
27
  requirements:
27
28
  - - ">="
28
29
  - !ruby/object:Gem::Version
30
+ hash: 9
29
31
  segments:
30
32
  - 0
31
33
  - 1
@@ -40,6 +42,7 @@ dependencies:
40
42
  requirements:
41
43
  - - ">="
42
44
  - !ruby/object:Gem::Version
45
+ hash: 5
43
46
  segments:
44
47
  - 2
45
48
  - 2
@@ -48,33 +51,51 @@ dependencies:
48
51
  type: :runtime
49
52
  version_requirements: *id002
50
53
  - !ruby/object:Gem::Dependency
51
- name: rspec
54
+ name: gmail_xoauth
52
55
  prerelease: false
53
56
  requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 19
62
+ segments:
63
+ - 0
64
+ - 3
65
+ - 0
66
+ version: 0.3.0
67
+ type: :runtime
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
54
73
  none: false
55
74
  requirements:
56
75
  - - ~>
57
76
  - !ruby/object:Gem::Version
77
+ hash: 3
58
78
  segments:
59
79
  - 2
60
80
  - 0
61
81
  version: "2.0"
62
82
  type: :development
63
- version_requirements: *id003
83
+ version_requirements: *id004
64
84
  - !ruby/object:Gem::Dependency
65
85
  name: mocha
66
86
  prerelease: false
67
- requirement: &id004 !ruby/object:Gem::Requirement
87
+ requirement: &id005 !ruby/object:Gem::Requirement
68
88
  none: false
69
89
  requirements:
70
90
  - - ">="
71
91
  - !ruby/object:Gem::Version
92
+ hash: 25
72
93
  segments:
73
94
  - 0
74
95
  - 9
75
96
  version: "0.9"
76
97
  type: :development
77
- version_requirements: *id004
98
+ version_requirements: *id005
78
99
  description: A Rubyesque interface to Gmail, with all the tools you will need. Search, read and send multipart emails; archive, mark as read/unread, delete emails; and manage labels.
79
100
  email:
80
101
  - chris@nu7hat.ch
@@ -97,6 +118,9 @@ files:
97
118
  - gmail.gemspec
98
119
  - lib/gmail.rb
99
120
  - lib/gmail/client.rb
121
+ - lib/gmail/client/base.rb
122
+ - lib/gmail/client/plain.rb
123
+ - lib/gmail/client/xoauth.rb
100
124
  - lib/gmail/labels.rb
101
125
  - lib/gmail/mailbox.rb
102
126
  - lib/gmail/message.rb
@@ -121,6 +145,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
121
145
  requirements:
122
146
  - - ">="
123
147
  - !ruby/object:Gem::Version
148
+ hash: 3
124
149
  segments:
125
150
  - 0
126
151
  version: "0"
@@ -129,6 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
154
  requirements:
130
155
  - - ">="
131
156
  - !ruby/object:Gem::Version
157
+ hash: 3
132
158
  segments:
133
159
  - 0
134
160
  version: "0"