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 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
@@ -0,0 +1,3 @@
1
+ pkg
2
+ .rvmrc
3
+ .ruby-version
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
@@ -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
+
@@ -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
@@ -0,0 +1,4 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'mocha/setup'
4
+ require 'gmail'
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