gmail 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,221 @@
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 :username
19
+ attr_reader :password
20
+ attr_reader :options
21
+
22
+ def initialize(username, password, options={})
23
+ defaults = {}
24
+ @username = fill_username(username)
25
+ @password = password
26
+ @options = defaults.merge(options)
27
+ end
28
+
29
+ # Connect to gmail service.
30
+ def connect(raise_errors=false)
31
+ @imap = Net::IMAP.new(GMAIL_IMAP_HOST, GMAIL_IMAP_PORT, true, nil, false)
32
+ rescue SocketError
33
+ raise_errors and raise ConnectionError, "Couldn't establish connection with GMail IMAP service"
34
+ end
35
+
36
+ # This version of connect will raise error on failure...
37
+ def connect!
38
+ connect(true)
39
+ end
40
+
41
+ # Return current connection. Log in automaticaly to specified account if
42
+ # it is necessary.
43
+ def connection
44
+ login and at_exit { logout } unless logged_in?
45
+ @imap
46
+ end
47
+ alias :conn :connection
48
+
49
+ # Login to specified account.
50
+ def login(raise_errors=false)
51
+ @imap and @logged_in = (login = @imap.login(username, password)) && login.name == 'OK'
52
+ rescue Net::IMAP::NoResponseError
53
+ raise_errors and raise AuthorizationError, "Couldn't login to given GMail account: #{username}"
54
+ end
55
+ alias :sign_in :login
56
+
57
+ # This version of login will raise error on failure...
58
+ def login!
59
+ login(true)
60
+ end
61
+ alias :sign_in! :login!
62
+
63
+ # Returns +true+ when you are logged in to specified account.
64
+ def logged_in?
65
+ !!@logged_in
66
+ end
67
+ alias :signed_in? :logged_in?
68
+
69
+ # Logout from GMail service.
70
+ def logout
71
+ @imap && logged_in? and @imap.logout
72
+ ensure
73
+ @logged_in = false
74
+ end
75
+ alias :sign_out :logout
76
+
77
+ # Return labels object, which helps you with managing your GMail labels.
78
+ # See <tt>Gmail::Labels</tt> for details.
79
+ def labels
80
+ @labels ||= Labels.new(conn)
81
+ end
82
+
83
+ # Compose new e-mail.
84
+ #
85
+ # ==== Examples
86
+ #
87
+ # mail = gmail.compose
88
+ # mail.from "test@gmail.org"
89
+ # mail.to "friend@gmail.com"
90
+ #
91
+ # ... or block style:
92
+ #
93
+ # mail = gmail.compose do
94
+ # from "test@gmail.org"
95
+ # to "friend@gmail.com"
96
+ # subject "Hello!"
97
+ # body "Hello my friend! long time..."
98
+ # end
99
+ #
100
+ # Now you can deliver your mail:
101
+ #
102
+ # gmail.deliver(mail)
103
+ def compose(mail=nil, &block)
104
+ if block_given?
105
+ mail = Mail.new(&block)
106
+ elsif !mail
107
+ mail = Mail.new
108
+ end
109
+ mail.delivery_method(*smtp_settings)
110
+ mail.from = username unless mail.from
111
+ mail
112
+ end
113
+ alias :message :compose
114
+
115
+ # Compose (optionaly) and send given email.
116
+ #
117
+ # ==== Examples
118
+ #
119
+ # gmail.deliver do
120
+ # to "friend@gmail.com"
121
+ # subject "Hello friend!"
122
+ # body "Hi! How are you?"
123
+ # end
124
+ #
125
+ # ... or with already created message:
126
+ #
127
+ # mail = Mail.new { ... }
128
+ # gmail.deliver(mail)
129
+ #
130
+ # mail = gmail.compose { ... }
131
+ # gmail.deliver(mail)
132
+ def deliver(mail=nil, raise_errors=false, &block)
133
+ mail = compose(mail, &block) if block_given?
134
+ mail.deliver!
135
+ rescue Object => ex
136
+ raise_errors and raise DeliveryError, "Couldn't deliver email: #{ex.to_s}"
137
+ end
138
+
139
+ # This version of deliver will raise error on failure...
140
+ def deliver!(mail=nil, &block)
141
+ deliver(mail, true, &block)
142
+ end
143
+
144
+ # Do something with given mailbox or within it context.
145
+ #
146
+ # ==== Examples
147
+ #
148
+ # mailbox = gmail.mailbox("INBOX")
149
+ # mailbox.emails(:all)
150
+ # mailbox.count(:unread, :before => Time.now-(20*24*3600))
151
+ #
152
+ # ... or block style:
153
+ #
154
+ # gmail.label("Work") do |mailbox|
155
+ # mailbox.emails(:unread)
156
+ # mailbox.count(:all)
157
+ # ...
158
+ # end
159
+ def mailbox(name, &block)
160
+ name = name.to_s
161
+ mailbox = (mailboxes[name] ||= Mailbox.new(self, name))
162
+ switch_to_mailbox(name) if @current_mailbox != name
163
+ if block_given?
164
+ mailbox_stack << @current_mailbox
165
+ result = block.arity == 1 ? block.call(mailbox) : block.call
166
+ mailbox_stack.pop
167
+ switch_to_mailbox(mailbox_stack.last)
168
+ return result
169
+ end
170
+ mailbox
171
+ end
172
+ alias :in_mailbox :mailbox
173
+ alias :in_label :mailbox
174
+ alias :label :mailbox
175
+
176
+ # Alias for <tt>mailbox("INBOX")</tt>. See <tt>Gmail::Client#mailbox</tt>
177
+ # for details.
178
+ def inbox
179
+ mailbox("INBOX")
180
+ end
181
+
182
+ def mailboxes
183
+ @mailboxes ||= {}
184
+ end
185
+
186
+ def inspect
187
+ "#<Gmail::Client#{'0x%04x' % (object_id << 1)} (#{username}) #{'dis' if !logged_in?}connected>"
188
+ end
189
+
190
+ def fill_username(username)
191
+ username =~ /@/ ? username : "#{username}@gmail.com"
192
+ end
193
+
194
+ def mail_domain
195
+ username.split('@')[0]
196
+ end
197
+
198
+ private
199
+
200
+ def switch_to_mailbox(mailbox)
201
+ conn.select(mailbox) if mailbox
202
+ @current_mailbox = mailbox
203
+ end
204
+
205
+ def mailbox_stack
206
+ @mailbox_stack ||= []
207
+ end
208
+
209
+ def smtp_settings
210
+ [:smtp, {
211
+ :address => GMAIL_SMTP_HOST,
212
+ :port => GMAIL_SMTP_PORT,
213
+ :domain => mail_domain,
214
+ :user_name => username,
215
+ :password => password,
216
+ :authentication => 'plain',
217
+ :enable_starttls_auto => true
218
+ }]
219
+ end
220
+ end # Client
221
+ end # Gmail
@@ -0,0 +1,42 @@
1
+ module Gmail
2
+ class Labels
3
+ attr_reader :connection
4
+ alias :conn :connection
5
+
6
+ def initialize(connection)
7
+ @connection = connection
8
+ end
9
+
10
+ # Get list of all defined labels.
11
+ def all
12
+ (conn.list("", "%")+conn.list("[Gmail]/", "%")).inject([]) do |labels,label|
13
+ label[:name].each_line {|l| labels << l }
14
+ labels
15
+ end
16
+ end
17
+ alias :list :all
18
+
19
+ # Returns +true+ when given label defined.
20
+ def exists?(label)
21
+ all.include?(label)
22
+ end
23
+ alias :exist? :exists?
24
+
25
+ # Creates given label in your account.
26
+ def create(label)
27
+ !!conn.create(label) rescue false
28
+ end
29
+ alias :new :create
30
+ alias :add :create
31
+
32
+ # Deletes given label from your account.
33
+ def delete(label)
34
+ !!conn.delete(label) rescue false
35
+ end
36
+ alias :remove :delete
37
+
38
+ def inspect
39
+ "#<Gmail::Labels#{'0x%04x' % (object_id << 1)}>"
40
+ end
41
+ end # Labels
42
+ end # Gmail
@@ -0,0 +1,92 @@
1
+ module Gmail
2
+ class Mailbox
3
+ MAILBOX_ALIASES = {
4
+ :all => ['ALL'],
5
+ :unread => ['UNSEEN'],
6
+ :read => ['SEEN']
7
+ }
8
+
9
+ attr_reader :name
10
+
11
+ def initialize(gmail, name="INBOX")
12
+ @name = name
13
+ @gmail = gmail
14
+ end
15
+
16
+ # Returns list of emails which meets given criteria.
17
+ #
18
+ # ==== Examples
19
+ #
20
+ # gmail.inbox.emails(:all)
21
+ # gmail.inbox.emails(:unread, :from => "friend@gmail.com")
22
+ # gmail.inbox.emails(:all, :after => Time.now-(20*24*3600))
23
+ # gmail.mailbox("Test").emails(:read)
24
+ #
25
+ # gmail.mailbox("Test") do |box|
26
+ # box.emails(:read)
27
+ # box.emails(:unread) do |email|
28
+ # ... do something with each email...
29
+ # end
30
+ # end
31
+ def emails(*args, &block)
32
+ args << :all if args.size == 0
33
+
34
+ if args.first.is_a?(Symbol)
35
+ search = MAILBOX_ALIASES[args.shift]
36
+ opts = args.first.is_a?(Hash) ? args.first : {}
37
+
38
+ opts[:after] and search.concat ['SINCE', opts[:after].to_imap_date]
39
+ opts[:before] and search.concat ['BEFORE', opts[:before].to_imap_date]
40
+ opts[:on] and search.concat ['ON', opts[:on].to_imap_date]
41
+ opts[:from] and search.concat ['FROM', opts[:from]]
42
+ opts[:to] and search.concat ['TO', opts[:to]]
43
+ opts[:subject] and search.concat ['SUBJECT', opts[:subject]]
44
+ opts[:label] and search.concat ['LABEL', opts[:label]]
45
+ opts[:attachment] and search.concat ['HAS', 'attachment']
46
+ opts[:search] and search.concat [opts[:search]]
47
+
48
+ @gmail.mailbox(name) do
49
+ @gmail.conn.uid_search(search).collect do |uid|
50
+ message = (messages[uid] ||= Message.new(self, uid))
51
+ block.call(message) if block_given?
52
+ message
53
+ end
54
+ end
55
+ elsif args.first.is_a?(Hash)
56
+ emails(:all, args.first)
57
+ else
58
+ raise ArgumentError, "Invalid search criteria"
59
+ end
60
+ end
61
+ alias :mails :emails
62
+ alias :search :emails
63
+ alias :find :emails
64
+ alias :filter :emails
65
+
66
+ # This is a convenience method that really probably shouldn't need to exist,
67
+ # but it does make code more readable, if seriously all you want is the count
68
+ # of messages.
69
+ #
70
+ # ==== Examples
71
+ #
72
+ # gmail.inbox.count(:all)
73
+ # gmail.inbox.count(:unread, :from => "friend@gmail.com")
74
+ # gmail.mailbox("Test").count(:all, :after => Time.now-(20*24*3600))
75
+ def count(*args)
76
+ emails(*args).size
77
+ end
78
+
79
+ # Cached messages.
80
+ def messages
81
+ @messages ||= {}
82
+ end
83
+
84
+ def inspect
85
+ "#<Gmail::Mailbox#{'0x%04x' % (object_id << 1)} name=#{@name}>"
86
+ end
87
+
88
+ def to_s
89
+ name
90
+ end
91
+ end # Message
92
+ end # Gmail
@@ -0,0 +1,137 @@
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.
68
+ def delete!
69
+ @mailbox.messages.delete(uid)
70
+ flag(:Deleted)
71
+ end
72
+
73
+ # Archive this message.
74
+ def archive!
75
+ move_to('[Gmail]/All Mail')
76
+ end
77
+
78
+ # Move to given box and delete from others.
79
+ def move_to(name, from=nil)
80
+ label(name, from) && delete!
81
+ end
82
+ alias :move :move_to
83
+
84
+ # Move message to given and delete from others. When given mailbox doesn't
85
+ # exist then it will be automaticaly created.
86
+ def move_to!(name, from=nil)
87
+ label!(name, from) && delete!
88
+ end
89
+ alias :move! :move_to!
90
+
91
+ # Mark this message with given label. When given label doesn't exist then
92
+ # it will raise <tt>NoLabelError</tt>.
93
+ #
94
+ # See also <tt>Gmail::Message#label!</tt>.
95
+ def label(name, from=nil)
96
+ @gmail.mailbox(from || @mailbox.name) { @gmail.conn.uid_copy(uid, name) }
97
+ rescue Net::IMAP::NoResponseError
98
+ raise NoLabelError, "Label '#{name}' doesn't exist!"
99
+ end
100
+ alias :add_label :label
101
+
102
+ # Mark this message with given label. When given label doesn't exist then
103
+ # it will be automaticaly created.
104
+ #
105
+ # See also <tt>Gmail::Message#label</tt>.
106
+ def label!(name, from=nil)
107
+ label(name, from)
108
+ rescue NoLabelError
109
+ @gmail.labels.add(name)
110
+ label!(name, from)
111
+ end
112
+ alias :add_label! :add_label
113
+
114
+ # Remove given label from this message.
115
+ def remove_label!(name)
116
+ move_to('[Gmail]/All Mail', name)
117
+ end
118
+ alias :delete_label! :remove_label!
119
+
120
+ def inspect
121
+ "#<Gmail::Message#{'0x%04x' % (object_id << 1)} mailbox=#{@mailbox.name}#{' uid='+@uid.to_s if @uid}#{' message_id='+@message_id.to_s if @message_id}>"
122
+ end
123
+
124
+ def method_missing(meth, *args, &block)
125
+ # Delegate rest directly to the message.
126
+ message.send(meth, *args, &block)
127
+ end
128
+
129
+ private
130
+
131
+ def message
132
+ @message ||= Mail.new(@gmail.mailbox(@mailbox.name) {
133
+ @gmail.conn.uid_fetch(uid, "RFC822")[0].attr["RFC822"]
134
+ })
135
+ end
136
+ end # Message
137
+ end # Gmail