gmail_oauth 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ *.rdb
23
+
24
+ ## Test Account details
25
+ spec/account.yml
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # gmail_oauth gem changelog
2
+
3
+ ## 0.1 / 2010-12-13
4
+
5
+ * Birthday of OAuth version from https://github.com/nu7hatch/gmail
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyrignt (c) 2010 Kriss 'nu7hatch' Kowalik
2
+ Copyright (c) 2009-2010 BehindLogic
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,257 @@
1
+ # GMail for Ruby
2
+
3
+ I changed the auth method to Oauth, using Nicolas Fouché's [gmail_xoauth](https://github.com/nfo/gmail_xoauth).
4
+
5
+ To use it you must produce your own token and secret. You can follow an example of how to do it by [Nicolas Fouché](https://github.com/nfo/gmail-oauth-sinatra).
6
+
7
+ THIS IS NOT TESTED.
8
+
9
+ ###To Do
10
+ * Write specs (I know, I know)
11
+
12
+ A Rubyesque interface to Google's GMail with Oauth, with all the tools you'll need. Search,
13
+ read and send multipart emails, archive, mark as read/unread, delete emails,
14
+ and manage labels.
15
+
16
+ It's based on Kriss 'nu7hatch' Kowalik gmail gem. This version has oauth login and does not require username and password from users.
17
+
18
+ ## Author(s)
19
+
20
+ * [Kriss 'nu7hatch' Kowalik](http://github.com/nu7hatch)
21
+ * [Daniel Parker of BehindLogic.com](http://github.com/dcparker)
22
+
23
+ Extra thanks for specific feature contributions from:
24
+
25
+ * [Arthur Chiu](http://github.com/achiu)
26
+ * [Justin Perkins](http://github.com/justinperkins)
27
+ * [Mikkel Malmberg](http://github.com/mikker)
28
+ * [Julien Blanchard](http://github.com/julienXX)
29
+ * [Federico Galassi](http://github.com/fgalassi)
30
+
31
+ ## Installation
32
+
33
+ You can install it easy using rubygems:
34
+
35
+ sudo gem install gmail_oauth
36
+
37
+ Or install it manualy:
38
+
39
+ git clone git://github.com/stefanobernardi/gmail_oauth.git
40
+ cd gmail
41
+ rake install
42
+
43
+ To install gmail gem you have to met following requirements (with rubygems all
44
+ will be installed automatically):
45
+
46
+ * mail
47
+ * mime
48
+ * gmail_xoauth
49
+ * smpt_tls (Ruby < 1.8.7)
50
+
51
+ ## Features
52
+
53
+ * Search emails
54
+ * Read emails (handles attachments)
55
+ * Emails: label, archive, delete, mark as read/unread/spam, star
56
+ * Manage labels
57
+ * Create and send multipart email messages in plaintext and/or html, with inline
58
+ images and attachments
59
+ * Utilizes Gmail's IMAP & SMTP, MIME-type detection and parses and generates
60
+ MIME properly.
61
+
62
+ ## Basic usage
63
+
64
+ First of all require the `gmail-oauth` library.
65
+
66
+ require 'gmail_oauth'
67
+
68
+ ### Authenticating gmail sessions
69
+
70
+ This will you automatically log in to your account.
71
+
72
+ gmail = Gmail.connect(email, token, secret, consumer_key, consumer_secret)
73
+ # play with your gmail...
74
+ gmail.logout
75
+
76
+ If you pass a block, the session will be passed into the block, and the session
77
+ will be logged out after the block is executed.
78
+
79
+ Gmail.connect(email, token, secret, consumer_key, consumer_secret) do |gmail|
80
+ # play with your gmail...
81
+ end
82
+
83
+ Examples above are "quiet", it means that it will not raise any errors when
84
+ session couldn't be started (eg. because of connection error or invalid
85
+ authorization data). You can use connection which handles errors raising:
86
+
87
+ Gmail.connect!(email, token, secret, consumer_key, consumer_secret)
88
+ Gmail.connect!(email, token, secret, consumer_key, consumer_secret) {|gmail| ... play with gmail ... }
89
+
90
+ You can also check if you are logged in at any time:
91
+
92
+ Gmail.connect(email, token, secret, consumer_key, consumer_secret) do |gmail|
93
+ gmail.logged_in?
94
+ end
95
+
96
+ ### Counting and gathering emails
97
+
98
+ Get counts for messages in the inbox:
99
+
100
+ gmail.inbox.count
101
+ gmail.inbox.count(:unread)
102
+ gmail.inbox.count(:read)
103
+
104
+ Count with some criteria:
105
+
106
+ gmail.inbox.count(:after => Date.parse("2010-02-20"), :before => Date.parse("2010-03-20"))
107
+ gmail.inbox.count(:on => Date.parse("2010-04-15"))
108
+ gmail.inbox.count(:from => "myfriend@gmail.com")
109
+ gmail.inbox.count(:to => "directlytome@gmail.com")
110
+
111
+ Combine flags and options:
112
+
113
+ gmail.inbox.count(:unread, :from => "myboss@gmail.com")
114
+
115
+ Browsing labeled emails is similar to work with inbox.
116
+
117
+ gmail.mailbox('Urgent').count
118
+
119
+ Getting messages works the same way as counting: Remember that every message in a
120
+ conversation/thread will come as a separate message.
121
+
122
+ gmail.inbox.emails(:unread, :before => Date.parse("2010-04-20"), :from => "myboss@gmail.com")
123
+
124
+ You can use also one of aliases:
125
+
126
+ gmail.inbox.find(...)
127
+ gmail.inbox.search(...)
128
+ gmail.inbox.mails(...)
129
+
130
+ Also you can manipulate each message using block style:
131
+
132
+ gmail.inbox.find(:unread) do |email|
133
+ email.read!
134
+ end
135
+
136
+ ### Working with emails!
137
+
138
+ Any news older than 4-20, mark as read and archive it:
139
+
140
+ gmail.inbox.find(:before => Date.parse("2010-04-20"), :from => "news@nbcnews.com") do |email|
141
+ email.read! # can also unread!, spam! or star!
142
+ email.archive!
143
+ end
144
+
145
+ Delete emails from X:
146
+
147
+ gmail.inbox.find(:from => "x-fiance@gmail.com").each do |email|
148
+ email.delete!
149
+ end
150
+
151
+ Save all attachments in the "Faxes" label to a local folder:
152
+
153
+ folder = "/where/ever"
154
+ gmail.mailbox("Faxes").emails do |email|
155
+ if !email.message.attachments.empty?
156
+ email.message.save_attachments_to(folder)
157
+ end
158
+ end
159
+
160
+ You can use also `#label` method instead of `#mailbox`:
161
+
162
+ gmail.label("Faxes").emails {|email| ... }
163
+
164
+ Save just the first attachment from the newest unread email (assuming pdf):
165
+
166
+ email = gmail.inbox.find(:unread).first
167
+ email.attachments[0].save_to_file("/path/to/location")
168
+
169
+ Add a label to a message:
170
+
171
+ email.label("Faxes")
172
+
173
+ Example above will raise error when you don't have the `Faxes` label. You can
174
+ avoid this using:
175
+
176
+ email.label!("Faxes") # The `Faxes` label will be automatically created now
177
+
178
+ You can also move message to a label/mailbox:
179
+
180
+ email.move_to("Faxes")
181
+ email.move_to!("NewLabel")
182
+
183
+ There is also few shortcuts to mark messages quickly:
184
+
185
+ email.read!
186
+ email.unread!
187
+ email.spam!
188
+ email.star!
189
+ email.unstar!
190
+
191
+ ### Managing labels
192
+
193
+ With Gmail gem you can also manage your labels. You can get list of defined
194
+ labels:
195
+
196
+ gmail.labels.all
197
+
198
+ Create new label:
199
+
200
+ gmail.labels.new("Uregent")
201
+ gmail.labels.add("AnotherOne")
202
+
203
+ Remove labels:
204
+
205
+ gmail.labels.delete("Uregent")
206
+
207
+ Or check if given label exists:
208
+
209
+ gmail.labels.exists?("Uregent") # => false
210
+ gmail.labels.exists?("AnotherOne") # => true
211
+
212
+ ### Composing and sending emails
213
+
214
+ Creating emails now uses the amazing [Mail](http://rubygems.org/gems/mail) rubygem.
215
+ See its [documentation here](http://github.com/mikel/mail). The Ruby Gmail will
216
+ automatically configure your Mail emails to be sent via your Gmail account's SMTP,
217
+ so they will be in your Gmail's "Sent" folder. Also, no need to specify the "From"
218
+ email either, because ruby-gmail will set it for you.
219
+
220
+ gmail.deliver do
221
+ to "email@example.com"
222
+ subject "Having fun in Puerto Rico!"
223
+ text_part do
224
+ body "Text of plaintext message."
225
+ end
226
+ html_part do
227
+ body "<p>Text of <em>html</em> message.</p>"
228
+ end
229
+ add_file "/path/to/some_image.jpg"
230
+ end
231
+
232
+ Or, generate the message first and send it later
233
+
234
+ email = gmail.generate_message do
235
+ to "email@example.com"
236
+ subject "Having fun in Puerto Rico!"
237
+ body "Spent the day on the road..."
238
+ end
239
+ email.deliver! # or: gmail.deliver(email)
240
+
241
+ ## Note on Patches/Pull Requests
242
+
243
+ * Fork the project.
244
+ * Make your feature addition or bug fix.
245
+ * Add tests for it. This is important so I don't break it in a
246
+ future version unintentionally.
247
+ * Commit, do not mess with rakefile, version, or history.
248
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
249
+ * Send me a pull request. Bonus points for topic branches.
250
+
251
+ ## Copyright
252
+
253
+ * Copyrignt (c) 2010 Kriss 'nu7hatch' Kowalik
254
+ * Copyright (c) 2009-2010 BehindLogic
255
+
256
+ See LICENSE for details.
257
+
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ # -*- ruby -*-
2
+ $:.unshift(File.expand_path('../lib', __FILE__))
3
+ require 'gmail/version'
4
+ require 'rspec/core/rake_task'
5
+ require 'rake/rdoctask'
6
+
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.pattern = 'spec/**/*_spec.rb'
9
+ t.rspec_opts = %q[-c -b]
10
+ end
11
+
12
+ RSpec::Core::RakeTask.new(:rcov) do |t|
13
+ t.rcov = true
14
+ t.rspec_opts = %q[-c -b]
15
+ t.rcov_opts = %q[-T -x "spec"]
16
+ end
17
+
18
+ Rake::RDocTask.new do |rdoc|
19
+ rdoc.rdoc_dir = 'rdoc'
20
+ rdoc.title = "Gmail #{Gmail.version}"
21
+ rdoc.rdoc_files.include('README*')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+ task :default => :spec
26
+
27
+ desc "Build current version as a rubygem"
28
+ task :build do
29
+ `gem build gmail-oauth.gemspec`
30
+ `mkdir -p pkg`
31
+ `mv gmail_oauth-*.gem pkg/`
32
+ end
33
+
34
+ desc "Relase current version to rubygems.org"
35
+ task :release => :build do
36
+ `git tag -am "Version bump to #{Gmail.version}" v#{Gmail.version}`
37
+ `git push origin master`
38
+ `git push origin master --tags`
39
+ `gem push pkg/gmail_oauth-#{Gmail.version}.gem`
40
+ end
41
+
42
+ desc "Perform installation via rubygems"
43
+ task :install => :build do
44
+ `gem install pkg/gmail_oauth-#{Gmail.version}.gem`
45
+ end
data/TODO.md ADDED
@@ -0,0 +1,3 @@
1
+ # TODO
2
+
3
+ * Specs
@@ -0,0 +1,236 @@
1
+ module Gmail
2
+ class 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
+
10
+ # GMail IMAP defaults
11
+ GMAIL_IMAP_HOST = 'imap.gmail.com'
12
+ GMAIL_IMAP_PORT = 993
13
+
14
+ # GMail SMTP defaults
15
+ GMAIL_SMTP_HOST = "smtp.gmail.com"
16
+ GMAIL_SMTP_PORT = 587
17
+
18
+ attr_reader :email
19
+ attr_reader :token
20
+ attr_reader :secret
21
+ attr_reader :options
22
+
23
+ def initialize(email, token, secret, consumer_key, consumer_secret, options={})
24
+ defaults = {}
25
+ @email = email
26
+ @username = email
27
+ @token = token
28
+ @secret = secret
29
+ @consumer_key = consumer_key
30
+ @consumer_secret = consumer_secret
31
+ @options = defaults.merge(options)
32
+ end
33
+
34
+ # Connect to gmail service.
35
+ def connect(raise_errors=false)
36
+ @imap = Net::IMAP.new(GMAIL_IMAP_HOST, GMAIL_IMAP_PORT, true, nil, false)
37
+ rescue SocketError
38
+ raise_errors and raise ConnectionError, "Couldn't establish connection with GMail IMAP service"
39
+ end
40
+
41
+ # This version of connect will raise error on failure...
42
+ def connect!
43
+ connect(true)
44
+ end
45
+
46
+ # Return current connection. Log in automaticaly to specified account if
47
+ # it is necessary.
48
+ def connection
49
+ login and at_exit { logout } unless logged_in?
50
+ @imap
51
+ end
52
+ alias :conn :connection
53
+
54
+ # Login to specified account.
55
+ def login(raise_errors=false)
56
+ @imap and @logged_in = (login = @imap.authenticate('XOAUTH', email,
57
+ :consumer_key => consumer_key,
58
+ :consumer_secret => consumer_secret,
59
+ :token => token,
60
+ :token_secret => secret
61
+ )) && login.name == 'OK'
62
+ rescue Net::IMAP::NoResponseError
63
+ raise_errors and raise AuthorizationError, "Couldn't login to given GMail account"
64
+ end
65
+ alias :sign_in :login
66
+
67
+ # This version of login will raise error on failure...
68
+ def login!
69
+ login(true)
70
+ end
71
+ alias :sign_in! :login!
72
+
73
+ # Returns +true+ when you are logged in to specified account.
74
+ def logged_in?
75
+ !!@logged_in
76
+ end
77
+ alias :signed_in? :logged_in?
78
+
79
+ # Logout from GMail service.
80
+ def logout
81
+ @imap && logged_in? and @imap.logout
82
+ ensure
83
+ @logged_in = false
84
+ end
85
+ alias :sign_out :logout
86
+
87
+ # Return labels object, which helps you with managing your GMail labels.
88
+ # See <tt>Gmail::Labels</tt> for details.
89
+ def labels
90
+ @labels ||= Labels.new(conn)
91
+ end
92
+
93
+ # Compose new e-mail.
94
+ #
95
+ # ==== Examples
96
+ #
97
+ # mail = gmail.compose
98
+ # mail.from "test@gmail.org"
99
+ # mail.to "friend@gmail.com"
100
+ #
101
+ # ... or block style:
102
+ #
103
+ # mail = gmail.compose do
104
+ # from "test@gmail.org"
105
+ # to "friend@gmail.com"
106
+ # subject "Hello!"
107
+ # body "Hello my friend! long time..."
108
+ # end
109
+ #
110
+ # Now you can deliver your mail:
111
+ #
112
+ # gmail.deliver(mail)
113
+ def compose(mail=nil, &block)
114
+ if block_given?
115
+ mail = Mail.new(&block)
116
+ elsif !mail
117
+ mail = Mail.new
118
+ end
119
+ mail.delivery_method(*smtp_settings)
120
+ mail.from = @email unless mail.from
121
+ mail
122
+ end
123
+ alias :message :compose
124
+
125
+ # Compose (optionaly) and send given email.
126
+ #
127
+ # ==== Examples
128
+ #
129
+ # gmail.deliver do
130
+ # to "friend@gmail.com"
131
+ # subject "Hello friend!"
132
+ # body "Hi! How are you?"
133
+ # end
134
+ #
135
+ # ... or with already created message:
136
+ #
137
+ # mail = Mail.new { ... }
138
+ # gmail.deliver(mail)
139
+ #
140
+ # mail = gmail.compose { ... }
141
+ # gmail.deliver(mail)
142
+ def deliver(mail=nil, raise_errors=false, &block)
143
+ mail = compose(mail, &block) if block_given?
144
+ mail.deliver!
145
+ rescue Object => ex
146
+ raise_errors and raise DeliveryError, "Couldn't deliver email: #{ex.to_s}"
147
+ end
148
+
149
+ # This version of deliver will raise error on failure...
150
+ def deliver!(mail=nil, &block)
151
+ deliver(mail, true, &block)
152
+ end
153
+
154
+ # Do something with given mailbox or within it context.
155
+ #
156
+ # ==== Examples
157
+ #
158
+ # mailbox = gmail.mailbox("INBOX")
159
+ # mailbox.emails(:all)
160
+ # mailbox.count(:unread, :before => Time.now-(20*24*3600))
161
+ #
162
+ # ... or block style:
163
+ #
164
+ # gmail.label("Work") do |mailbox|
165
+ # mailbox.emails(:unread)
166
+ # mailbox.count(:all)
167
+ # ...
168
+ # end
169
+ def mailbox(name, &block)
170
+ name = Net::IMAP.encode_utf7(name.to_s)
171
+ mailbox = (mailboxes[name] ||= Mailbox.new(self, name))
172
+ switch_to_mailbox(name) if @current_mailbox != name
173
+ if block_given?
174
+ mailbox_stack << @current_mailbox
175
+ result = block.arity == 1 ? block.call(mailbox) : block.call
176
+ mailbox_stack.pop
177
+ switch_to_mailbox(mailbox_stack.last)
178
+ return result
179
+ end
180
+ mailbox
181
+ end
182
+ alias :in_mailbox :mailbox
183
+ alias :in_label :mailbox
184
+ alias :label :mailbox
185
+
186
+ # Alias for <tt>mailbox("INBOX")</tt>. See <tt>Gmail::Client#mailbox</tt>
187
+ # for details.
188
+ def inbox
189
+ mailbox("INBOX")
190
+ end
191
+
192
+ def mailboxes
193
+ @mailboxes ||= {}
194
+ end
195
+
196
+ def inspect
197
+ "#<Gmail::Client#{'0x%04x' % (object_id << 1)} (#{@email}) #{'dis' if !logged_in?}connected>"
198
+ end
199
+
200
+ #def fill_username(username)
201
+ # username =~ /@/ ? username : "#{username}@gmail.com"
202
+ #end
203
+
204
+ def mail_domain
205
+ @email.split('@')[0]
206
+ end
207
+
208
+ private
209
+
210
+ def switch_to_mailbox(mailbox)
211
+ conn.select(mailbox) if mailbox
212
+ @current_mailbox = mailbox
213
+ end
214
+
215
+ def mailbox_stack
216
+ @mailbox_stack ||= []
217
+ end
218
+
219
+ def smtp_settings
220
+ [:smtp, {
221
+ :address => GMAIL_SMTP_HOST,
222
+ :port => GMAIL_SMTP_PORT,
223
+ :domain => mail_domain,
224
+ :user_name => username,
225
+ :password => {
226
+ :consumer_key => 'anonymous',
227
+ :consumer_secret => 'anonymous',
228
+ :token => '4/nM2QAaunKUINb4RrXPC55F-mix_k',
229
+ :token_secret => '41r18IyXjIvuyabS/NDyW6+m'
230
+ }
231
+ :authentication => 'xoauth',
232
+ :enable_starttls_auto => true
233
+ }]
234
+ end
235
+ end # Client
236
+ end # Gmail
@@ -0,0 +1,48 @@
1
+ module Gmail
2
+ class Labels
3
+ include Enumerable
4
+ attr_reader :connection
5
+ alias :conn :connection
6
+
7
+ def initialize(connection)
8
+ @connection = connection
9
+ end
10
+
11
+ # Get list of all defined labels.
12
+ def all
13
+ (conn.list("", "%")+conn.list("[Gmail]/", "%")).inject([]) do |labels,label|
14
+ label[:name].each_line {|l| labels << Net::IMAP.decode_utf7(l) }
15
+ labels
16
+ end
17
+ end
18
+ alias :list :all
19
+ alias :to_a :all
20
+
21
+ def each(*args, &block)
22
+ all.each(*args, &block)
23
+ end
24
+
25
+ # Returns +true+ when given label defined.
26
+ def exists?(label)
27
+ all.include?(Net::IMAP.encode_utf7(label))
28
+ end
29
+ alias :exist? :exists?
30
+
31
+ # Creates given label in your account.
32
+ def create(label)
33
+ !!conn.create(Net::IMAP.encode_utf7(label)) rescue false
34
+ end
35
+ alias :new :create
36
+ alias :add :create
37
+
38
+ # Deletes given label from your account.
39
+ def delete(label)
40
+ !!conn.delete(Net::IMAP.encode_utf7(label)) rescue false
41
+ end
42
+ alias :remove :delete
43
+
44
+ def inspect
45
+ "#<Gmail::Labels#{'0x%04x' % (object_id << 1)}>"
46
+ end
47
+ end # Labels
48
+ end # Gmail
@@ -0,0 +1,115 @@
1
+ module Gmail
2
+ class Mailbox
3
+ MAILBOX_ALIASES = {
4
+ :all => ['ALL'],
5
+ :seen => ['SEEN'],
6
+ :unseen => ['UNSEEN'],
7
+ :read => ['SEEN'],
8
+ :unread => ['UNSEEN'],
9
+ :flagged => ['FLAGGED'],
10
+ :unflagged => ['UNFLAGGED'],
11
+ :starred => ['FLAGGED'],
12
+ :unstarred => ['UNFLAGGED'],
13
+ :deleted => ['DELETED'],
14
+ :undeleted => ['UNDELETED'],
15
+ :draft => ['DRAFT'],
16
+ :undrafted => ['UNDRAFT']
17
+ }
18
+
19
+ attr_reader :name
20
+ attr_reader :external_name
21
+
22
+ def initialize(gmail, name="INBOX")
23
+ @name = name
24
+ @external_name = Net::IMAP.decode_utf7(name)
25
+ @gmail = gmail
26
+ end
27
+
28
+ # Returns list of emails which meets given criteria.
29
+ #
30
+ # ==== Examples
31
+ #
32
+ # gmail.inbox.emails(:all)
33
+ # gmail.inbox.emails(:unread, :from => "friend@gmail.com")
34
+ # gmail.inbox.emails(:all, :after => Time.now-(20*24*3600))
35
+ # gmail.mailbox("Test").emails(:read)
36
+ #
37
+ # gmail.mailbox("Test") do |box|
38
+ # box.emails(:read)
39
+ # box.emails(:unread) do |email|
40
+ # ... do something with each email...
41
+ # end
42
+ # end
43
+ def emails(*args, &block)
44
+ args << :all if args.size == 0
45
+
46
+ if args.first.is_a?(Symbol)
47
+ search = MAILBOX_ALIASES[args.shift].dup
48
+ opts = args.first.is_a?(Hash) ? args.first : {}
49
+
50
+ opts[:after] and search.concat ['SINCE', opts[:after].to_imap_date]
51
+ opts[:before] and search.concat ['BEFORE', opts[:before].to_imap_date]
52
+ opts[:on] and search.concat ['ON', opts[:on].to_imap_date]
53
+ opts[:from] and search.concat ['FROM', opts[:from]]
54
+ opts[:to] and search.concat ['TO', opts[:to]]
55
+ opts[:subject] and search.concat ['SUBJECT', opts[:subject]]
56
+ opts[:label] and search.concat ['LABEL', opts[:label]]
57
+ opts[:attachment] and search.concat ['HAS', 'attachment']
58
+ opts[:search] and search.concat [opts[:search]]
59
+
60
+ @gmail.mailbox(name) do
61
+ @gmail.conn.uid_search(search).collect do |uid|
62
+ message = (messages[uid] ||= Message.new(self, uid))
63
+ block.call(message) if block_given?
64
+ message
65
+ end
66
+ end
67
+ elsif args.first.is_a?(Hash)
68
+ emails(:all, args.first)
69
+ else
70
+ raise ArgumentError, "Invalid search criteria"
71
+ end
72
+ end
73
+ alias :mails :emails
74
+ alias :search :emails
75
+ alias :find :emails
76
+ alias :filter :emails
77
+
78
+ # This is a convenience method that really probably shouldn't need to exist,
79
+ # but it does make code more readable, if seriously all you want is the count
80
+ # of messages.
81
+ #
82
+ # ==== Examples
83
+ #
84
+ # gmail.inbox.count(:all)
85
+ # gmail.inbox.count(:unread, :from => "friend@gmail.com")
86
+ # gmail.mailbox("Test").count(:all, :after => Time.now-(20*24*3600))
87
+ def count(*args)
88
+ emails(*args).size
89
+ end
90
+
91
+ # This permanently removes messages which are marked as deleted
92
+ def expunge
93
+ @gmail.mailbox(name) { @gmail.conn.expunge }
94
+ end
95
+
96
+ # Cached messages.
97
+ def messages
98
+ @messages ||= {}
99
+ end
100
+
101
+ def inspect
102
+ "#<Gmail::Mailbox#{'0x%04x' % (object_id << 1)} name=#{external_name}>"
103
+ end
104
+
105
+ def to_s
106
+ name
107
+ end
108
+
109
+ MAILBOX_ALIASES.each_key { |mailbox|
110
+ define_method(mailbox) do |*args, &block|
111
+ emails(mailbox, *args, &block)
112
+ end
113
+ }
114
+ end # Message
115
+ end # Gmail
@@ -0,0 +1,164 @@
1
+ require 'mime/message'
2
+
3
+ module Gmail
4
+ class Message
5
+ # Raised when given label doesn't exists.
6
+ class NoLabelError < Exception; end
7
+
8
+ attr_reader :uid
9
+
10
+ def initialize(mailbox, uid)
11
+ @uid = uid
12
+ @mailbox = mailbox
13
+ @gmail = mailbox.instance_variable_get("@gmail") if mailbox
14
+ end
15
+
16
+ def uid
17
+ @uid ||= @gmail.conn.uid_search(['HEADER', 'Message-ID', message_id])[0]
18
+ end
19
+
20
+ # Mark message with given flag.
21
+ def flag(name)
22
+ !!@gmail.mailbox(@mailbox.name) { @gmail.conn.uid_store(uid, "+FLAGS", [name]) }
23
+ end
24
+
25
+ # Unmark message.
26
+ def unflag(name)
27
+ !!@gmail.mailbox(@mailbox.name) { @gmail.conn.uid_store(uid, "-FLAGS", [name]) }
28
+ end
29
+
30
+ # Do commonly used operations on message.
31
+ def mark(flag)
32
+ case flag
33
+ when :read then read!
34
+ when :unread then unread!
35
+ when :deleted then delete!
36
+ when :spam then spam!
37
+ else
38
+ flag(flag)
39
+ end
40
+ end
41
+
42
+ # Mark this message as a spam.
43
+ def spam!
44
+ move_to('[Gmail]/Spam')
45
+ end
46
+
47
+ # Mark as read.
48
+ def read!
49
+ flag(:Seen)
50
+ end
51
+
52
+ # Mark as unread.
53
+ def unread!
54
+ unflag(:Seen)
55
+ end
56
+
57
+ # Mark message with star.
58
+ def star!
59
+ flag('[Gmail]/Starred')
60
+ end
61
+
62
+ # Remove message from list of starred.
63
+ def unstar!
64
+ unflag('[Gmail]/Starred')
65
+ end
66
+
67
+ # Move to trash / bin.
68
+ def delete!
69
+ @mailbox.messages.delete(uid)
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)
75
+ end
76
+
77
+ # Archive this message.
78
+ def archive!
79
+ move_to('[Gmail]/All Mail')
80
+ end
81
+
82
+ # Move to given box and delete from others.
83
+ def move_to(name, from=nil)
84
+ label(name, from)
85
+ delete! if !%w[[Gmail]/Bin [Gmail]/Trash].include?(name)
86
+ end
87
+ alias :move :move_to
88
+
89
+ # Move message to given and delete from others. When given mailbox doesn't
90
+ # exist then it will be automaticaly created.
91
+ def move_to!(name, from=nil)
92
+ label!(name, from) && delete!
93
+ end
94
+ alias :move! :move_to!
95
+
96
+ # Mark this message with given label. When given label doesn't exist then
97
+ # it will raise <tt>NoLabelError</tt>.
98
+ #
99
+ # See also <tt>Gmail::Message#label!</tt>.
100
+ def label(name, from=nil)
101
+ @gmail.mailbox(Net::IMAP.encode_utf7(from || @mailbox.external_name)) { @gmail.conn.uid_copy(uid, Net::IMAP.encode_utf7(name)) }
102
+ rescue Net::IMAP::NoResponseError
103
+ raise NoLabelError, "Label '#{name}' doesn't exist!"
104
+ end
105
+
106
+ # Mark this message with given label. When given label doesn't exist then
107
+ # it will be automaticaly created.
108
+ #
109
+ # See also <tt>Gmail::Message#label</tt>.
110
+ def label!(name, from=nil)
111
+ label(name, from)
112
+ rescue NoLabelError
113
+ @gmail.labels.add(Net::IMAP.encode_utf7(name))
114
+ label(name, from)
115
+ end
116
+ alias :add_label :label!
117
+ alias :add_label! :label!
118
+
119
+ # Remove given label from this message.
120
+ def remove_label!(name)
121
+ move_to('[Gmail]/All Mail', name)
122
+ end
123
+ alias :delete_label! :remove_label!
124
+
125
+ def inspect
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}>"
127
+ end
128
+
129
+ def method_missing(meth, *args, &block)
130
+ # Delegate rest directly to the message.
131
+ if envelope.respond_to?(meth)
132
+ envelope.send(meth, *args, &block)
133
+ elsif message.respond_to?(meth)
134
+ message.send(meth, *args, &block)
135
+ else
136
+ super(meth, *args, &block)
137
+ end
138
+ end
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
+
150
+ def envelope
151
+ @envelope ||= @gmail.mailbox(@mailbox.name) {
152
+ @gmail.conn.uid_fetch(uid, "ENVELOPE")[0].attr["ENVELOPE"]
153
+ }
154
+ end
155
+
156
+ private
157
+
158
+ def message
159
+ @message ||= Mail.new(@gmail.mailbox(@mailbox.name) {
160
+ @gmail.conn.uid_fetch(uid, "RFC822")[0].attr["RFC822"] # RFC822
161
+ })
162
+ end
163
+ end # Message
164
+ end # Gmail
@@ -0,0 +1,12 @@
1
+ module Gmail
2
+ class Version #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ PATCH = 1
6
+ STRING = [MAJOR, MINOR, PATCH].join('.')
7
+ end # Version
8
+
9
+ def self.version # :nodoc:
10
+ Version::STRING
11
+ end
12
+ end # Gmail
data/lib/gmail.rb ADDED
@@ -0,0 +1,48 @@
1
+ require 'net/imap'
2
+ require 'net/smtp'
3
+ require 'mail'
4
+ require 'date'
5
+ require 'time'
6
+ require 'gmail_xoauth'
7
+
8
+ if RUBY_VERSION < "1.8.7"
9
+ require "smtp_tls"
10
+ end
11
+
12
+ class Object
13
+ def to_imap_date
14
+ Date.parse(to_s).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
+ def new(email, token, secret, consumer_key, consumer_secret, options={}, &block)
27
+ client = Client.new(email, token, secret, consumer_key, consumer_secret, options)
28
+ client.connect and client.login
29
+ if block_given?
30
+ yield client
31
+ client.logout
32
+ end
33
+ client
34
+ end
35
+ alias :connect :new
36
+
37
+ def new!(email, token, secret, consumer_key, consumer_secret, options={}, &block)
38
+ client = Client.new(email, token, secret, consumer_key, consumer_secret, options)
39
+ client.connect! and client.login!
40
+ if block_given?
41
+ yield client
42
+ client.logout
43
+ end
44
+ client
45
+ end
46
+ alias :connect! :new!
47
+ end # << self
48
+ end # Gmail
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gmail_oauth
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - BehindLogic
13
+ - Chris Kowalik
14
+ - Stefano Bernardi
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-12-14 00:00:00 +01:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: mime
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ segments:
31
+ - 0
32
+ - 1
33
+ version: "0.1"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: mail
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ segments:
45
+ - 2
46
+ - 2
47
+ - 1
48
+ version: 2.2.1
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: gmail_xoauth
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ segments:
60
+ - 0
61
+ - 3
62
+ version: "0.3"
63
+ type: :runtime
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: rspec
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 2
75
+ - 0
76
+ version: "2.0"
77
+ type: :development
78
+ version_requirements: *id004
79
+ - !ruby/object:Gem::Dependency
80
+ name: mocha
81
+ prerelease: false
82
+ requirement: &id005 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ segments:
88
+ - 0
89
+ - 9
90
+ version: "0.9"
91
+ type: :development
92
+ version_requirements: *id005
93
+ description: A Rubyesque interface to Gmail with Oauth, with all the tools you will need. Search, read and send multipart emails; archive, mark as read/unread, delete emails; and manage labels.
94
+ email:
95
+ - hello@stefanobernardi.com
96
+ executables: []
97
+
98
+ extensions: []
99
+
100
+ extra_rdoc_files:
101
+ - LICENSE
102
+ - README.md
103
+ - CHANGELOG.md
104
+ - TODO.md
105
+ files:
106
+ - .gitignore
107
+ - CHANGELOG.md
108
+ - LICENSE
109
+ - README.md
110
+ - Rakefile
111
+ - TODO.md
112
+ - lib/gmail.rb
113
+ - lib/gmail/client.rb
114
+ - lib/gmail/labels.rb
115
+ - lib/gmail/mailbox.rb
116
+ - lib/gmail/message.rb
117
+ - lib/gmail/version.rb
118
+ has_rdoc: true
119
+ homepage: http://github.com/stefanobernardi/gmail_oauth
120
+ licenses: []
121
+
122
+ post_install_message:
123
+ rdoc_options: []
124
+
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ segments:
133
+ - 0
134
+ version: "0"
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ segments:
141
+ - 0
142
+ version: "0"
143
+ requirements: []
144
+
145
+ rubyforge_project:
146
+ rubygems_version: 1.3.7
147
+ signing_key:
148
+ specification_version: 3
149
+ summary: A Rubyesque interface to Gmail with Oauth, with all the tools you will need.
150
+ test_files: []
151
+