gmail 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"