karthik-ruby-gmail 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.autotest +23 -0
- data/.gitignore +3 -0
- data/History.txt +75 -0
- data/Manifest.txt +14 -0
- data/README.markdown +173 -0
- data/Rakefile +28 -0
- data/VERSION +1 -0
- data/lib/gmail.rb +196 -0
- data/lib/gmail/mailbox.rb +83 -0
- data/lib/gmail/message.rb +110 -0
- data/lib/smtp_tls.rb +94 -0
- data/ruby-gmail.gemspec +64 -0
- data/test/test_gmail.rb +87 -0
- data/test/test_helper.rb +4 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NDIzM2RjNjAxMDM1MjVlODlkY2VmMWY5NjQ3MDVjZWMyOTM5MGMzOA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MzM0OTg1YzdkOGFiYzhjMDU2YTQ2MjU5YTM4MDk4ZDQ1YjkyNzBiOA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YmUxMWNiMGY4NjM0ZTc0YmNlZTcwM2FhNmJhNzg0MjE0MDliNWE5MDEwMmY0
|
10
|
+
NTg3ZjBhOTM0NzUxZjUwOThkNWQ3N2YzZDdjMTBkZmVhYjJiYjRhOTc2ODk1
|
11
|
+
YzZlZTc0M2ZmNDRjNjJjNmEyMGE5ZTk3N2IyYTI5ODMzMTE3Njk=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MTM1YzE5NTI4NzIwNzBkZTA0N2Q5MGVhOWFiYWI0Y2JjYzE1YzVhYjYwMWFk
|
14
|
+
MDJlYjRmMGY2NGU1MGY0Y2E5YjQ4NzEwYjZiNjMyYmQ5MzZlNGU2OGE1YjVm
|
15
|
+
ZTk1ZmIxNGJjODNkODdhNTcyMzNiNjgzNDYwMjJlZGIzMjJlN2I=
|
data/.autotest
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'autotest/restart'
|
4
|
+
|
5
|
+
# Autotest.add_hook :initialize do |at|
|
6
|
+
# at.extra_files << "../some/external/dependency.rb"
|
7
|
+
#
|
8
|
+
# at.libs << ":../some/external"
|
9
|
+
#
|
10
|
+
# at.add_exception 'vendor'
|
11
|
+
#
|
12
|
+
# at.add_mapping(/dependency.rb/) do |f, _|
|
13
|
+
# at.files_matching(/test_.*rb$/)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# %w(TestA TestB).each do |klass|
|
17
|
+
# at.extra_class_map[klass] = "test/test_misc.rb"
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
|
21
|
+
# Autotest.add_hook :run_command do |at|
|
22
|
+
# system "rake build"
|
23
|
+
# end
|
data/.gitignore
ADDED
data/History.txt
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
=== 0.1.1 / 2010-05-11
|
2
|
+
|
3
|
+
* 1 minor fix
|
4
|
+
|
5
|
+
* Added explicit tmail dependency in gemspec
|
6
|
+
* Added better README tutorial content
|
7
|
+
|
8
|
+
=== 0.0.9 / 2010-04-17
|
9
|
+
|
10
|
+
* 1 bugfix
|
11
|
+
|
12
|
+
* Fixed content-transfer-encoding when sending email
|
13
|
+
|
14
|
+
=== 0.0.8 / 2009-12-23
|
15
|
+
|
16
|
+
* 1 bugfix
|
17
|
+
|
18
|
+
* Fixed attaching a file to an empty message
|
19
|
+
|
20
|
+
=== 0.0.7 / 2009-12-23
|
21
|
+
|
22
|
+
* 1 bugfix
|
23
|
+
|
24
|
+
* Improved multipart message parsing reliability
|
25
|
+
|
26
|
+
=== 0.0.6 / 2009-12-21
|
27
|
+
|
28
|
+
* 1 bugfix
|
29
|
+
|
30
|
+
* Fixed multipart parsing for when the boundary is marked in quotes.
|
31
|
+
|
32
|
+
=== 0.0.5 / 2009-12-16
|
33
|
+
|
34
|
+
* 1 bugfix
|
35
|
+
|
36
|
+
* Fixed IMAP initializer to work with Ruby 1.9's net/imap
|
37
|
+
|
38
|
+
* 4 minor enhancements
|
39
|
+
|
40
|
+
* Better logout depending on the IMAP connection itself
|
41
|
+
* Added MIME::Message#text and MIME::Message#html for easier access to an email body
|
42
|
+
* Improved the MIME-parsing API slightly
|
43
|
+
* Added some tests
|
44
|
+
|
45
|
+
=== 0.0.4 / 2009-11-30
|
46
|
+
|
47
|
+
* 4 minor enhancement
|
48
|
+
|
49
|
+
* Added label creation (thanks to Justin Perkins / http://github.com/justinperkins)
|
50
|
+
* Made the gem login automatically when first needed
|
51
|
+
* Added an optional block on the Gmail.new object that will login and logout for you
|
52
|
+
* Added several search options (thanks to Mikkel Malmberg / http://github.com/mikker)
|
53
|
+
|
54
|
+
=== 0.0.3 / 2009-11-19
|
55
|
+
|
56
|
+
* 1 bugfix
|
57
|
+
|
58
|
+
* Fixed MIME::Message#content= for messages without an encoding
|
59
|
+
|
60
|
+
* 1 minor enhancement
|
61
|
+
|
62
|
+
* Added Gmail#new_message
|
63
|
+
|
64
|
+
=== 0.0.2 / 2009-11-18
|
65
|
+
|
66
|
+
* 1 minor enhancement
|
67
|
+
|
68
|
+
* Made all of the examples in the README possible
|
69
|
+
|
70
|
+
=== 0.0.1 / 2009-11-18
|
71
|
+
|
72
|
+
* 1 major enhancement
|
73
|
+
|
74
|
+
* Birthday!
|
75
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
.autotest
|
2
|
+
History.txt
|
3
|
+
lib/gmail/mailbox.rb
|
4
|
+
lib/gmail/message.rb
|
5
|
+
lib/gmail.rb
|
6
|
+
lib/ietf/rfc2045.rb
|
7
|
+
lib/ietf/rfc822.rb
|
8
|
+
lib/mime/entity.rb
|
9
|
+
lib/mime/entity_tmail.rb
|
10
|
+
lib/mime/message.rb
|
11
|
+
lib/smtp_tls.rb
|
12
|
+
Manifest.txt
|
13
|
+
Rakefile
|
14
|
+
README.markdown
|
data/README.markdown
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
|
2
|
+
# Original ruby-gmail documentation
|
3
|
+
|
4
|
+
* Code: [http://github.com/dcparker/ruby-gmail](http://github.com/dcparker/ruby-gmail)
|
5
|
+
* Gem: [http://gemcutter.org/gems/ruby-gmail](http://rubygems.org/gems/ruby-gmail)
|
6
|
+
|
7
|
+
## Author(s)
|
8
|
+
|
9
|
+
* Daniel Parker of BehindLogic.com
|
10
|
+
* Nathan Herald
|
11
|
+
|
12
|
+
Extra thanks for specific feature contributions from:
|
13
|
+
|
14
|
+
* [Justin Perkins](http://github.com/justinperkins)
|
15
|
+
* [Mikkel Malmberg](http://github.com/mikker)
|
16
|
+
* [Julien Blanchard](http://github.com/julienXX)
|
17
|
+
* [Federico Galassi](http://github.com/fgalassi)
|
18
|
+
|
19
|
+
## Description
|
20
|
+
|
21
|
+
This is a fork of the original ruby-gmail gem created by Daniel Parker (http://github.com/dcparker/). Minor improvements have been made in email search functionality.
|
22
|
+
Search functionality now supports search by email subject and a limit on the number of search results. A Rubyesque interface to Gmail, with all the tools you'll need. Search, read and send multipart emails; archive, mark as read/unread, delete emails; and manage labels.
|
23
|
+
|
24
|
+
## Features
|
25
|
+
|
26
|
+
* Search emails (including search by subject)
|
27
|
+
* Read emails (handles attachments)
|
28
|
+
* Emails: Label, archive, delete, mark as read/unread/spam
|
29
|
+
* Create and delete labels
|
30
|
+
* Create and send multipart email messages in plaintext and/or html, with inline images and attachments
|
31
|
+
* Utilizes Gmail's IMAP & SMTP, MIME-type detection and parses and generates MIME properly.
|
32
|
+
|
33
|
+
## Problems:
|
34
|
+
|
35
|
+
* May not correctly read malformed MIME messages. This could possibly be corrected by having IMAP parse the MIME structure.
|
36
|
+
* Cannot grab the plain or html message without also grabbing attachments. It might be nice to lazy-[down]load attachments.
|
37
|
+
|
38
|
+
## Example Code:
|
39
|
+
|
40
|
+
### 1) Require gmail
|
41
|
+
|
42
|
+
require 'gmail'
|
43
|
+
|
44
|
+
### 2) Start an authenticated gmail session
|
45
|
+
|
46
|
+
# If you pass a block, the session will be passed into the block,
|
47
|
+
# and the session will be logged out after the block is executed.
|
48
|
+
gmail = Gmail.new(username, password)
|
49
|
+
# ...do things...
|
50
|
+
gmail.logout
|
51
|
+
|
52
|
+
Gmail.new(username, password) do |gmail|
|
53
|
+
# ...do things...
|
54
|
+
end
|
55
|
+
|
56
|
+
### 3) Count and gather emails!
|
57
|
+
|
58
|
+
# Get counts for messages in the inbox
|
59
|
+
gmail.inbox.count
|
60
|
+
gmail.inbox.count(:unread)
|
61
|
+
gmail.inbox.count(:read)
|
62
|
+
|
63
|
+
# Count with some criteria
|
64
|
+
gmail.inbox.count(:after => Date.parse("2010-02-20"), :before => Date.parse("2010-03-20"))
|
65
|
+
gmail.inbox.count(:on => Date.parse("2010-04-15"))
|
66
|
+
gmail.inbox.count(:from => "myfriend@gmail.com")
|
67
|
+
gmail.inbox.count(:to => "directlytome@gmail.com")
|
68
|
+
|
69
|
+
# Combine flags and options
|
70
|
+
gmail.inbox.count(:unread, :from => "myboss@gmail.com")
|
71
|
+
|
72
|
+
# Labels work the same way as inbox
|
73
|
+
gmail.mailbox('Urgent').count
|
74
|
+
|
75
|
+
# Getting messages works the same way as counting: optional flag, and optional arguments
|
76
|
+
# Remember that every message in a conversation/thread will come as a separate message.
|
77
|
+
gmail.inbox.emails(:unread, :before => Date.parse("2010-04-20"), :from => "myboss@gmail.com")
|
78
|
+
|
79
|
+
# Get messages without marking them as read on the server.
|
80
|
+
gmail.peek = true
|
81
|
+
gmail.inbox.emails(:unread, :before => Date.parse("2010-04-20"), :from => "myboss@gmail.com", :subject => "Appointment at 10 PM")
|
82
|
+
|
83
|
+
### 4) Work with emails!
|
84
|
+
|
85
|
+
# any news older than 4-20, mark as read and archive it...
|
86
|
+
gmail.inbox.emails(:before => Date.parse("2010-04-20"), :from => "news@nbcnews.com").each do |email|
|
87
|
+
email.mark(:read) # can also mark :unread or :spam
|
88
|
+
email.archive!
|
89
|
+
end
|
90
|
+
|
91
|
+
# delete emails from X...
|
92
|
+
gmail.inbox.emails(:from => "x-fiancé@gmail.com").each do |email|
|
93
|
+
email.delete!
|
94
|
+
end
|
95
|
+
|
96
|
+
# Save all attachments in the "Faxes" label to a folder
|
97
|
+
folder = "/where/ever"
|
98
|
+
gmail.mailbox("Faxes").emails.each do |email|
|
99
|
+
email.attachments.each do |attachment|
|
100
|
+
file = File.new(folder + attachment.filename, "w+")
|
101
|
+
file << attachment.decoded
|
102
|
+
file.close
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Add a label to a message
|
107
|
+
email.label("Faxes")
|
108
|
+
|
109
|
+
# Or "move" the message to a label
|
110
|
+
email.move_to("Faxes")
|
111
|
+
|
112
|
+
### 5) Create new emails!
|
113
|
+
|
114
|
+
Creating emails now uses the amazing [Mail](http://rubygems.org/gems/mail) rubygem. See its [documentation here](http://github.com/mikel/mail). Ruby-gmail will automatically configure your Mail emails to be sent via your Gmail account's SMTP, so they will be in your Gmail's "Sent" folder. Also, no need to specify the "From" email either, because ruby-gmail will set it for you.
|
115
|
+
|
116
|
+
gmail.deliver do
|
117
|
+
to "email@example.com"
|
118
|
+
subject "Having fun in Puerto Rico!"
|
119
|
+
text_part do
|
120
|
+
body "Text of plaintext message."
|
121
|
+
end
|
122
|
+
html_part do
|
123
|
+
content_type 'text/html; charset=UTF-8'
|
124
|
+
body "<p>Text of <em>html</em> message.</p>"
|
125
|
+
end
|
126
|
+
add_file "/path/to/some_image.jpg"
|
127
|
+
end
|
128
|
+
# Or, generate the message first and send it later
|
129
|
+
email = gmail.generate_message do
|
130
|
+
to "email@example.com"
|
131
|
+
subject "Having fun in Puerto Rico!"
|
132
|
+
body "Spent the day on the road..."
|
133
|
+
end
|
134
|
+
email.deliver!
|
135
|
+
# Or...
|
136
|
+
gmail.deliver(email)
|
137
|
+
|
138
|
+
## Requirements
|
139
|
+
|
140
|
+
* ruby
|
141
|
+
* net/smtp
|
142
|
+
* net/imap
|
143
|
+
* tmail
|
144
|
+
* shared-mime-info rubygem (for MIME-detection when attaching files)
|
145
|
+
|
146
|
+
## Install
|
147
|
+
|
148
|
+
gem install ruby-gmail
|
149
|
+
|
150
|
+
## License
|
151
|
+
|
152
|
+
(The MIT License)
|
153
|
+
|
154
|
+
Copyright (c) 2009 BehindLogic
|
155
|
+
|
156
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
157
|
+
a copy of this software and associated documentation files (the
|
158
|
+
'Software'), to deal in the Software without restriction, including
|
159
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
160
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
161
|
+
permit persons to whom the Software is furnished to do so, subject to
|
162
|
+
the following conditions:
|
163
|
+
|
164
|
+
The above copyright notice and this permission notice shall be
|
165
|
+
included in all copies or substantial portions of the Software.
|
166
|
+
|
167
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
168
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
169
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
170
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
171
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
172
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
173
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
Jeweler::Tasks.new do |gem|
|
8
|
+
gem.name = "karthik-ruby-gmail"
|
9
|
+
gem.summary = %Q{A Rubyesque interface to Gmail, with all the tools you'll need.}
|
10
|
+
gem.description = %Q{A Rubyesque interface to Gmail, with all the tools you'll need. Search, read and send multipart emails; archive, mark as read/unread, delete emails; and manage labels. Fork of
|
11
|
+
the original ruby-gmail gem (https://github.com/dcparker/ruby-gmail). Added search by subject, limit number of email search results.}
|
12
|
+
gem.email = "karthik.mallavarapu@gmail.com"
|
13
|
+
gem.homepage = "https://github.com/karthik-mallavarapu/ruby-gmail"
|
14
|
+
gem.authors = ["Karthik Mallavarapu"]
|
15
|
+
gem.authors = ["Karthik Mallavarapu"]
|
16
|
+
gem.add_dependency('shared-mime-info', '>= 0')
|
17
|
+
gem.add_dependency('mail', '>= 2.2.1')
|
18
|
+
gem.add_dependency('mime', '>= 0.1')
|
19
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
20
|
+
end
|
21
|
+
Jeweler::GemcutterTasks.new
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
24
|
+
end
|
25
|
+
|
26
|
+
task :default do
|
27
|
+
sh "find test -type f -name '*rb' -exec testrb -I lib:test {} +"
|
28
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/lib/gmail.rb
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
require 'net/imap'
|
2
|
+
|
3
|
+
class Gmail
|
4
|
+
VERSION = '0.0.9'
|
5
|
+
|
6
|
+
class NoLabel < RuntimeError; end
|
7
|
+
|
8
|
+
##################################
|
9
|
+
# Gmail.new(username, password)
|
10
|
+
##################################
|
11
|
+
def initialize(username, password)
|
12
|
+
# This is to hide the username and password, not like it REALLY needs hiding, but ... you know.
|
13
|
+
# Could be helpful when demoing the gem in irb, these bits won't show up that way.
|
14
|
+
class << self
|
15
|
+
class << self
|
16
|
+
attr_accessor :username, :password
|
17
|
+
end
|
18
|
+
end
|
19
|
+
meta.username = username =~ /@/ ? username : username + '@gmail.com'
|
20
|
+
meta.password = password
|
21
|
+
@imap = Net::IMAP.new('imap.gmail.com',993,true,nil,false)
|
22
|
+
if block_given?
|
23
|
+
login # This is here intentionally. Normally, we get auto logged-in when first needed.
|
24
|
+
yield self
|
25
|
+
logout
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
###########################
|
30
|
+
# READING EMAILS
|
31
|
+
#
|
32
|
+
# gmail.inbox
|
33
|
+
# gmail.label('News')
|
34
|
+
#
|
35
|
+
###########################
|
36
|
+
|
37
|
+
def inbox
|
38
|
+
in_label('inbox')
|
39
|
+
end
|
40
|
+
|
41
|
+
def destroy_label(name)
|
42
|
+
imap.delete(name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def rename_label(oldname, newname)
|
46
|
+
imap.rename(oldname, newname)
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_label(name)
|
50
|
+
imap.create(name)
|
51
|
+
end
|
52
|
+
|
53
|
+
def mailbox_list
|
54
|
+
imap.list("","*")
|
55
|
+
end
|
56
|
+
|
57
|
+
# List the available labels
|
58
|
+
def labels
|
59
|
+
mailbox_list.inject([]) do |labels,label|
|
60
|
+
labels << label[:name] unless label.attr.include?(:Noselect)
|
61
|
+
|
62
|
+
labels
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# gmail.label(name)
|
67
|
+
def label(name)
|
68
|
+
mailboxes[name] ||= Mailbox.new(self, name)
|
69
|
+
end
|
70
|
+
alias :mailbox :label
|
71
|
+
|
72
|
+
def find_label_by_attribute(attribute)
|
73
|
+
mailbox_list.find{ |label| label.attr.include?(attribute.to_sym.capitalize) }
|
74
|
+
end
|
75
|
+
|
76
|
+
# don't mark emails as read on the server when downloading them
|
77
|
+
attr_accessor :peek
|
78
|
+
|
79
|
+
###########################
|
80
|
+
# MAKING EMAILS
|
81
|
+
#
|
82
|
+
# gmail.generate_message do
|
83
|
+
# ...inside Mail context...
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# gmail.deliver do ... end
|
87
|
+
#
|
88
|
+
# mail = Mail.new...
|
89
|
+
# gmail.deliver!(mail)
|
90
|
+
###########################
|
91
|
+
def generate_message(&block)
|
92
|
+
require 'net/smtp'
|
93
|
+
require 'smtp_tls'
|
94
|
+
require 'mail'
|
95
|
+
mail = Mail.new(&block)
|
96
|
+
mail.delivery_method(*smtp_settings)
|
97
|
+
mail
|
98
|
+
end
|
99
|
+
|
100
|
+
def deliver(mail=nil, &block)
|
101
|
+
require 'net/smtp'
|
102
|
+
require 'smtp_tls'
|
103
|
+
require 'mail'
|
104
|
+
mail = Mail.new(&block) if block_given?
|
105
|
+
mail.delivery_method(*smtp_settings)
|
106
|
+
mail.from = meta.username unless mail.from
|
107
|
+
mail.deliver!
|
108
|
+
end
|
109
|
+
|
110
|
+
###########################
|
111
|
+
# LOGIN
|
112
|
+
###########################
|
113
|
+
def login
|
114
|
+
res = @imap.login(meta.username, meta.password)
|
115
|
+
@logged_in = true if res && res.name == 'OK'
|
116
|
+
end
|
117
|
+
def logged_in?
|
118
|
+
!!@logged_in
|
119
|
+
end
|
120
|
+
# Log out of gmail
|
121
|
+
def logout
|
122
|
+
if logged_in?
|
123
|
+
res = @imap.logout
|
124
|
+
@logged_in = false if res && res.name == 'OK'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Shutdown socket and disconnect
|
129
|
+
def disconnect
|
130
|
+
logout if logged_in?
|
131
|
+
@imap.disconnect unless @imap.disconnected?
|
132
|
+
end
|
133
|
+
|
134
|
+
def in_mailbox(mailbox, &block)
|
135
|
+
if block_given?
|
136
|
+
mailbox_stack << mailbox
|
137
|
+
unless @selected == mailbox.name
|
138
|
+
imap.select(mailbox.name)
|
139
|
+
@selected = mailbox.name
|
140
|
+
end
|
141
|
+
value = block.arity == 1 ? block.call(mailbox) : block.call
|
142
|
+
mailbox_stack.pop
|
143
|
+
# Select previously selected mailbox if there is one
|
144
|
+
if mailbox_stack.last
|
145
|
+
imap.select(mailbox_stack.last.name)
|
146
|
+
@selected = mailbox.name
|
147
|
+
end
|
148
|
+
return value
|
149
|
+
else
|
150
|
+
mailboxes[mailbox] ||= Mailbox.new(self, mailbox)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
alias :in_label :in_mailbox
|
154
|
+
|
155
|
+
###########################
|
156
|
+
# Other...
|
157
|
+
###########################
|
158
|
+
def inspect
|
159
|
+
"#<Gmail:#{'0x%x' % (object_id << 1)} (#{meta.username}) #{'dis' if !logged_in?}connected>"
|
160
|
+
end
|
161
|
+
|
162
|
+
# Accessor for @imap, but ensures that it's logged in first.
|
163
|
+
def imap
|
164
|
+
unless logged_in?
|
165
|
+
login
|
166
|
+
at_exit { logout } # Set up auto-logout for later.
|
167
|
+
end
|
168
|
+
@imap
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
def mailboxes
|
173
|
+
@mailboxes ||= {}
|
174
|
+
end
|
175
|
+
def mailbox_stack
|
176
|
+
@mailbox_stack ||= []
|
177
|
+
end
|
178
|
+
def meta
|
179
|
+
class << self; self end
|
180
|
+
end
|
181
|
+
def domain
|
182
|
+
meta.username.split('@')[0]
|
183
|
+
end
|
184
|
+
def smtp_settings
|
185
|
+
[:smtp, {:address => "smtp.gmail.com",
|
186
|
+
:port => 587,
|
187
|
+
:domain => domain,
|
188
|
+
:user_name => meta.username,
|
189
|
+
:password => meta.password,
|
190
|
+
:authentication => 'plain',
|
191
|
+
:enable_starttls_auto => true}]
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
require 'gmail/mailbox'
|
196
|
+
require 'gmail/message'
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'time'
|
3
|
+
class Object
|
4
|
+
def to_imap_date
|
5
|
+
Date.parse(to_s).strftime("%d-%B-%Y")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Gmail
|
10
|
+
class Mailbox
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
def initialize(gmail, name)
|
14
|
+
@gmail = gmail
|
15
|
+
@name = name
|
16
|
+
end
|
17
|
+
|
18
|
+
def inspect
|
19
|
+
"<#Mailbox name=#{@name}>"
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
name
|
24
|
+
end
|
25
|
+
|
26
|
+
# Method: emails
|
27
|
+
# Args: [ :all | :unread | :read ]
|
28
|
+
# Opts: {:since => Date.new}
|
29
|
+
# Adding subject as a search parameter option.
|
30
|
+
# Also adding a param called limit, for limiting the number of results.
|
31
|
+
def emails(key_or_opts = :all, opts={})
|
32
|
+
if key_or_opts.is_a?(Hash) && opts.empty?
|
33
|
+
search = ['ALL']
|
34
|
+
opts = key_or_opts
|
35
|
+
elsif key_or_opts.is_a?(Symbol) && opts.is_a?(Hash)
|
36
|
+
aliases = {
|
37
|
+
:all => ['ALL'],
|
38
|
+
:unread => ['UNSEEN'],
|
39
|
+
:read => ['SEEN']
|
40
|
+
}
|
41
|
+
search = aliases[key_or_opts]
|
42
|
+
elsif key_or_opts.is_a?(Array) && opts.empty?
|
43
|
+
search = key_or_opts
|
44
|
+
else
|
45
|
+
raise ArgumentError, "Couldn't make sense of arguments to #emails - should be an optional hash of options preceded by an optional read-status bit; OR simply an array of parameters to pass directly to the IMAP uid_search call."
|
46
|
+
end
|
47
|
+
if !opts.empty?
|
48
|
+
# Support for several search macros
|
49
|
+
# :before => Date, :on => Date, :since => Date, :from => String, :to => String
|
50
|
+
search.concat ['SINCE', opts[:after].to_imap_date] if opts[:after]
|
51
|
+
search.concat ['BEFORE', opts[:before].to_imap_date] if opts[:before]
|
52
|
+
search.concat ['ON', opts[:on].to_imap_date] if opts[:on]
|
53
|
+
search.concat ['FROM', opts[:from]] if opts[:from]
|
54
|
+
search.concat ['TO', opts[:to]] if opts[:to]
|
55
|
+
# Adding search support for subject field
|
56
|
+
search.concat ['SUBJECT', opts[:subject]] if opts[:subject]
|
57
|
+
if opts[:limit]
|
58
|
+
raise ArgumentError "limit parameter must be an integer to limit the email search results" unless opts[:limit].is_a? Integer
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# puts "Gathering #{(aliases[key] || key).inspect} messages for mailbox '#{name}'..."
|
63
|
+
@gmail.in_mailbox(self) do
|
64
|
+
email_results = @gmail.imap.uid_search(search).collect { |uid| messages[uid] ||= Message.new(@gmail, self, uid) }
|
65
|
+
limit = email_results.count
|
66
|
+
if opts[:limit]
|
67
|
+
limit = opts[:limit]
|
68
|
+
end
|
69
|
+
email_results.first(limit)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# This is a convenience method that really probably shouldn't need to exist, but it does make code more readable
|
74
|
+
# if seriously all you want is the count of messages.
|
75
|
+
def count(*args)
|
76
|
+
emails(*args).length
|
77
|
+
end
|
78
|
+
|
79
|
+
def messages
|
80
|
+
@messages ||= {}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
class Gmail
|
2
|
+
class Message
|
3
|
+
def initialize(gmail, mailbox, uid)
|
4
|
+
@gmail = gmail
|
5
|
+
@mailbox = mailbox
|
6
|
+
@uid = uid
|
7
|
+
end
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
"<#Message:#{object_id} mailbox=#{@mailbox.name}#{' uid='+@uid.to_s if @uid}#{' message_id='+@message_id.to_s if @message_id}>"
|
11
|
+
end
|
12
|
+
|
13
|
+
# Auto IMAP info
|
14
|
+
def uid
|
15
|
+
@uid ||= @gmail.imap.uid_search(['HEADER', 'Message-ID', message_id])[0]
|
16
|
+
end
|
17
|
+
|
18
|
+
# IMAP Operations
|
19
|
+
def flag(flg)
|
20
|
+
@gmail.in_mailbox(@mailbox) do
|
21
|
+
@gmail.imap.uid_store(uid, "+FLAGS", [flg])
|
22
|
+
end ? true : false
|
23
|
+
end
|
24
|
+
|
25
|
+
def unflag(flg)
|
26
|
+
@gmail.in_mailbox(@mailbox) do
|
27
|
+
@gmail.imap.uid_store(uid, "-FLAGS", [flg])
|
28
|
+
end ? true : false
|
29
|
+
end
|
30
|
+
|
31
|
+
# Gmail Operations
|
32
|
+
def mark(flag)
|
33
|
+
case flag
|
34
|
+
when :read
|
35
|
+
flag(:Seen)
|
36
|
+
when :unread
|
37
|
+
unflag(:Seen)
|
38
|
+
when :deleted
|
39
|
+
flag(:Deleted)
|
40
|
+
when :spam
|
41
|
+
move_to_special_folder(:Junk)
|
42
|
+
end ? true : false
|
43
|
+
end
|
44
|
+
|
45
|
+
def delete!
|
46
|
+
@mailbox.messages.delete(uid)
|
47
|
+
flag(:Deleted)
|
48
|
+
end
|
49
|
+
|
50
|
+
def label(name)
|
51
|
+
@gmail.in_mailbox(@mailbox) do
|
52
|
+
begin
|
53
|
+
@gmail.imap.uid_copy(uid, name)
|
54
|
+
rescue Net::IMAP::NoResponseError
|
55
|
+
raise Gmail::NoLabel, "No label `#{name}' exists!"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def label!(name)
|
61
|
+
@gmail.in_mailbox(@mailbox) do
|
62
|
+
begin
|
63
|
+
@gmail.imap.uid_copy(uid, name)
|
64
|
+
rescue Net::IMAP::NoResponseError
|
65
|
+
# need to create the label first
|
66
|
+
@gmail.create_label(name)
|
67
|
+
retry
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# We're not sure of any 'labels' except the 'mailbox' we're in at the moment.
|
73
|
+
# Research whether we can find flags that tell which other labels this email is a part of.
|
74
|
+
# def remove_label(name)
|
75
|
+
# end
|
76
|
+
|
77
|
+
def move_to(name)
|
78
|
+
label(name) && delete!
|
79
|
+
end
|
80
|
+
|
81
|
+
def move_to_special_folder(attribute)
|
82
|
+
name = @gmail.find_label_by_attribute(attribute).name
|
83
|
+
move_to(name)
|
84
|
+
end
|
85
|
+
|
86
|
+
def archive!
|
87
|
+
move_to_special_folder(:All)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Parsed MIME message object
|
91
|
+
def message
|
92
|
+
require 'mail'
|
93
|
+
request,part = 'RFC822','RFC822'
|
94
|
+
request,part = 'BODY.PEEK[]','BODY[]' if @gmail.peek
|
95
|
+
_body = @gmail.in_mailbox(@mailbox) { @gmail.imap.uid_fetch(uid, request)[0].attr[part] }
|
96
|
+
@message ||= Mail.new(_body)
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
# Delegate all other methods to the Mail message
|
102
|
+
def method_missing(*args, &block)
|
103
|
+
if block_given?
|
104
|
+
message.send(*args, &block)
|
105
|
+
else
|
106
|
+
message.send(*args)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/smtp_tls.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require "openssl"
|
2
|
+
require "net/smtp"
|
3
|
+
|
4
|
+
Net::SMTP.class_eval do
|
5
|
+
|
6
|
+
def self.start( address, port = nil,
|
7
|
+
helo = 'localhost.localdomain',
|
8
|
+
user = nil, secret = nil, authtype = nil, use_tls = false,
|
9
|
+
&block) # :yield: smtp
|
10
|
+
new(address, port).start(helo, user, secret, authtype, use_tls, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def start( helo = 'localhost.localdomain',
|
14
|
+
user = nil, secret = nil, authtype = nil ) # :yield: smtp
|
15
|
+
start_method = starttls_auto? ? :do_tls_start : :do_start
|
16
|
+
if block_given?
|
17
|
+
begin
|
18
|
+
send(start_method, helo, user, secret, authtype)
|
19
|
+
return yield(self)
|
20
|
+
ensure
|
21
|
+
do_finish
|
22
|
+
end
|
23
|
+
else
|
24
|
+
send(start_method, helo, user, secret, authtype)
|
25
|
+
return self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def do_tls_start(helodomain, user, secret, authtype)
|
32
|
+
raise IOError, 'SMTP session already started' if @started
|
33
|
+
if RUBY_VERSION == '1.8.6'
|
34
|
+
check_auth_args(user, secret, authtype) if user or secret
|
35
|
+
else
|
36
|
+
check_auth_args(user, secret) if user or secret
|
37
|
+
end
|
38
|
+
|
39
|
+
sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
|
40
|
+
@socket = Net::InternetMessageIO.new(sock)
|
41
|
+
@socket.read_timeout = 60 #@read_timeout
|
42
|
+
@socket.debug_output = STDERR #@debug_output
|
43
|
+
|
44
|
+
check_response(critical { recv_response() })
|
45
|
+
do_helo(helodomain)
|
46
|
+
|
47
|
+
raise 'openssl library not installed' unless defined?(OpenSSL)
|
48
|
+
starttls
|
49
|
+
ssl = OpenSSL::SSL::SSLSocket.new(sock)
|
50
|
+
ssl.sync_close = true
|
51
|
+
ssl.connect
|
52
|
+
@socket = Net::InternetMessageIO.new(ssl)
|
53
|
+
@socket.read_timeout = 60 #@read_timeout
|
54
|
+
@socket.debug_output = STDERR #@debug_output
|
55
|
+
do_helo(helodomain)
|
56
|
+
|
57
|
+
authenticate user, secret, authtype if user
|
58
|
+
@started = true
|
59
|
+
ensure
|
60
|
+
unless @started
|
61
|
+
# authentication failed, cancel connection.
|
62
|
+
@socket.close if not @started and @socket and not @socket.closed?
|
63
|
+
@socket = nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def do_helo(helodomain)
|
68
|
+
begin
|
69
|
+
if @esmtp
|
70
|
+
ehlo helodomain
|
71
|
+
else
|
72
|
+
helo helodomain
|
73
|
+
end
|
74
|
+
rescue Net::ProtocolError
|
75
|
+
if @esmtp
|
76
|
+
@esmtp = false
|
77
|
+
@error_occured = false
|
78
|
+
retry
|
79
|
+
end
|
80
|
+
raise
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def starttls
|
85
|
+
getok('STARTTLS')
|
86
|
+
end
|
87
|
+
|
88
|
+
def quit
|
89
|
+
begin
|
90
|
+
getok('QUIT')
|
91
|
+
rescue EOFError, OpenSSL::SSL::SSLError
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/ruby-gmail.gemspec
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{karthik-ruby-gmail}
|
8
|
+
s.version = "1.0.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Karthik Mallavarapu"]
|
12
|
+
s.date = %q{2014-04-03}
|
13
|
+
s.description = %q{A Rubyesque interface to Gmail, with all the tools you'll need. Search, read and send multipart emails; archive, mark as read/unread, delete emails; and manage labels. Fork of
|
14
|
+
the original ruby-gmail gem (https://github.com/dcparker/ruby-gmail). Added search by subject, limit number of email search results.}
|
15
|
+
s.email = %q{karthik.mallavarapu@gmail.com}
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"README.markdown"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".autotest",
|
21
|
+
".gitignore",
|
22
|
+
"History.txt",
|
23
|
+
"Manifest.txt",
|
24
|
+
"README.markdown",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"lib/gmail.rb",
|
28
|
+
"lib/gmail/mailbox.rb",
|
29
|
+
"lib/gmail/message.rb",
|
30
|
+
"lib/smtp_tls.rb",
|
31
|
+
"ruby-gmail.gemspec",
|
32
|
+
"test/test_gmail.rb",
|
33
|
+
"test/test_helper.rb"
|
34
|
+
]
|
35
|
+
s.homepage = %q{https://github.com/karthik-mallavarapu/ruby-gmail}
|
36
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
37
|
+
s.require_paths = ["lib"]
|
38
|
+
s.rubygems_version = %q{1.3.7}
|
39
|
+
s.summary = %q{A Rubyesque interface to Gmail, with all the tools you'll need.}
|
40
|
+
s.test_files = [
|
41
|
+
"test/test_gmail.rb",
|
42
|
+
"test/test_helper.rb"
|
43
|
+
]
|
44
|
+
|
45
|
+
if s.respond_to? :specification_version then
|
46
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
47
|
+
s.specification_version = 3
|
48
|
+
|
49
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
50
|
+
s.add_runtime_dependency(%q<shared-mime-info>, [">= 0"])
|
51
|
+
s.add_runtime_dependency(%q<mail>, [">= 2.2.1"])
|
52
|
+
s.add_runtime_dependency(%q<mime>, [">= 0.1"])
|
53
|
+
else
|
54
|
+
s.add_dependency(%q<shared-mime-info>, [">= 0"])
|
55
|
+
s.add_dependency(%q<mail>, [">= 2.2.1"])
|
56
|
+
s.add_dependency(%q<mime>, [">= 0.1"])
|
57
|
+
end
|
58
|
+
else
|
59
|
+
s.add_dependency(%q<shared-mime-info>, [">= 0"])
|
60
|
+
s.add_dependency(%q<mail>, [">= 2.2.1"])
|
61
|
+
s.add_dependency(%q<mime>, [">= 0.1"])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
data/test/test_gmail.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class GmailTest < Test::Unit::TestCase
|
4
|
+
def test_initialize
|
5
|
+
imap = mock('imap')
|
6
|
+
Net::IMAP.expects(:new).with('imap.gmail.com', 993, true, nil, false).returns(imap)
|
7
|
+
gmail = Gmail.new('test', 'password')
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_imap_does_login
|
11
|
+
setup_mocks(:at_exit => true)
|
12
|
+
res = mock('res')
|
13
|
+
res.expects(:name).at_least(1).returns('OK')
|
14
|
+
|
15
|
+
# @imap.expects(:disconnected?).at_least_once.returns(true).then.returns(false)
|
16
|
+
# TODO: figure why this was here in the first place
|
17
|
+
@imap.expects(:login).with('test@gmail.com', 'password').returns(res)
|
18
|
+
@gmail.imap
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_imap_does_login_only_once
|
22
|
+
setup_mocks(:at_exit => true)
|
23
|
+
res = mock('res')
|
24
|
+
res.expects(:name).at_least(1).returns('OK')
|
25
|
+
|
26
|
+
# @imap.expects(:disconnected?).at_least_once.returns(true).then.returns(false)
|
27
|
+
# TODO: figure why this was here in the first place
|
28
|
+
@imap.expects(:login).with('test@gmail.com', 'password').returns(res)
|
29
|
+
@gmail.imap
|
30
|
+
@gmail.imap
|
31
|
+
@gmail.imap
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_imap_does_login_without_appending_gmail_domain
|
35
|
+
setup_mocks(:at_exit => true)
|
36
|
+
res = mock('res')
|
37
|
+
res.expects(:name).at_least(1).returns('OK')
|
38
|
+
|
39
|
+
# @imap.expects(:disconnected?).at_least_once.returns(true).then.returns(false)
|
40
|
+
# TODO: figure why this was here in the first place
|
41
|
+
@imap.expects(:login).with('test@gmail.com', 'password').returns(res)
|
42
|
+
@gmail.imap
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_imap_logs_out
|
46
|
+
setup_mocks(:at_exit => true)
|
47
|
+
res = mock('res')
|
48
|
+
res.expects(:name).at_least(1).returns('OK')
|
49
|
+
|
50
|
+
# @imap.expects(:disconnected?).at_least_once.returns(true).then.returns(false)
|
51
|
+
# TODO: figure why this was here in the first place
|
52
|
+
@imap.expects(:login).with('test@gmail.com', 'password').returns(res)
|
53
|
+
@gmail.imap
|
54
|
+
@imap.expects(:logout).returns(res)
|
55
|
+
@gmail.logout
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_imap_logout_does_nothing_if_not_logged_in
|
59
|
+
setup_mocks
|
60
|
+
|
61
|
+
@gmail.expects(:logged_in?).returns(false)
|
62
|
+
@imap.expects(:logout).never
|
63
|
+
@gmail.logout
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_imap_calls_create_label
|
67
|
+
setup_mocks(:at_exit => true)
|
68
|
+
res = mock('res')
|
69
|
+
res.expects(:name).at_least(1).returns('OK')
|
70
|
+
# @imap.expects(:disconnected?).at_least_once.returns(true).then.returns(false)
|
71
|
+
# TODO: figure out why this was here in the first place
|
72
|
+
@imap.expects(:login).with('test@gmail.com', 'password').returns(res)
|
73
|
+
@imap.expects(:create).with('foo')
|
74
|
+
@gmail.create_label('foo')
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def setup_mocks(options = {})
|
79
|
+
options = {:at_exit => false}.merge(options)
|
80
|
+
@imap = mock('imap')
|
81
|
+
Net::IMAP.expects(:new).with('imap.gmail.com', 993, true, nil, false).returns(@imap)
|
82
|
+
@gmail = Gmail.new('test@gmail.com', 'password')
|
83
|
+
|
84
|
+
# need this for the at_exit block that auto-exits after this test method completes
|
85
|
+
@imap.expects(:logout).at_least(0) if options[:at_exit]
|
86
|
+
end
|
87
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: karthik-ruby-gmail
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Karthik Mallavarapu
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: shared-mime-info
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mail
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.2.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.2.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: mime
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.1'
|
55
|
+
description: ! "A Rubyesque interface to Gmail, with all the tools you'll need. Search,
|
56
|
+
read and send multipart emails; archive, mark as read/unread, delete emails; and
|
57
|
+
manage labels. Fork of \n the original ruby-gmail gem (https://github.com/dcparker/ruby-gmail).
|
58
|
+
Added search by subject, limit number of email search results."
|
59
|
+
email: karthik.mallavarapu@gmail.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files:
|
63
|
+
- README.markdown
|
64
|
+
files:
|
65
|
+
- .autotest
|
66
|
+
- .gitignore
|
67
|
+
- History.txt
|
68
|
+
- Manifest.txt
|
69
|
+
- README.markdown
|
70
|
+
- Rakefile
|
71
|
+
- VERSION
|
72
|
+
- lib/gmail.rb
|
73
|
+
- lib/gmail/mailbox.rb
|
74
|
+
- lib/gmail/message.rb
|
75
|
+
- lib/smtp_tls.rb
|
76
|
+
- ruby-gmail.gemspec
|
77
|
+
- test/test_gmail.rb
|
78
|
+
- test/test_helper.rb
|
79
|
+
homepage: https://github.com/karthik-mallavarapu/ruby-gmail
|
80
|
+
licenses: []
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options:
|
84
|
+
- --charset=UTF-8
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ! '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 2.1.11
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: A Rubyesque interface to Gmail, with all the tools you'll need.
|
103
|
+
test_files:
|
104
|
+
- test/test_gmail.rb
|
105
|
+
- test/test_helper.rb
|