module Gmail
class Client
# Raised when connection with GMail IMAP service couldn't be established.
class ConnectionError < SocketError; end
# Raised when given username or password are invalid.
class AuthorizationError < Net::IMAP::NoResponseError; end
# Raised when delivered email is invalid.
class DeliveryError < ArgumentError; end
# GMail IMAP defaults
GMAIL_IMAP_HOST = 'imap.gmail.com'
GMAIL_IMAP_PORT = 993
# GMail SMTP defaults
GMAIL_SMTP_HOST = "smtp.gmail.com"
GMAIL_SMTP_PORT = 587
attr_reader :email
attr_reader :token
attr_reader :secret
attr_reader :options
def initialize(email, token, secret, consumer_key, consumer_secret, options={})
defaults = {}
@email = email
@username = email
@token = token
@secret = secret
@consumer_key = consumer_key
@consumer_secret = consumer_secret
@options = defaults.merge(options)
end
# Connect to gmail service.
def connect(raise_errors=false)
@imap = Net::IMAP.new(GMAIL_IMAP_HOST, GMAIL_IMAP_PORT, true, nil, false)
rescue SocketError
raise_errors and raise ConnectionError, "Couldn't establish connection with GMail IMAP service"
end
# This version of connect will raise error on failure...
def connect!
connect(true)
end
# Return current connection. Log in automaticaly to specified account if
# it is necessary.
def connection
login and at_exit { logout } unless logged_in?
@imap
end
alias :conn :connection
# Login to specified account.
def login(raise_errors=false)
@imap and @logged_in = (login = @imap.authenticate('XOAUTH', @email,
:consumer_key => @consumer_key,
:consumer_secret => @consumer_secret,
:token => @token,
:token_secret => @secret
)) && login.name == 'OK'
rescue Net::IMAP::NoResponseError
raise_errors and raise AuthorizationError, "Couldn't login to given GMail account"
end
alias :sign_in :login
# This version of login will raise error on failure...
def login!
login(true)
end
alias :sign_in! :login!
# Returns +true+ when you are logged in to specified account.
def logged_in?
!!@logged_in
end
alias :signed_in? :logged_in?
# Logout from GMail service.
def logout
@imap && logged_in? and @imap.logout
ensure
@logged_in = false
end
alias :sign_out :logout
# Return labels object, which helps you with managing your GMail labels.
# See Gmail::Labels for details.
def labels
@labels ||= Labels.new(conn)
end
# Compose new e-mail.
#
# ==== Examples
#
# mail = gmail.compose
# mail.from "test@gmail.org"
# mail.to "friend@gmail.com"
#
# ... or block style:
#
# mail = gmail.compose do
# from "test@gmail.org"
# to "friend@gmail.com"
# subject "Hello!"
# body "Hello my friend! long time..."
# end
#
# Now you can deliver your mail:
#
# gmail.deliver(mail)
def compose(mail=nil, &block)
if block_given?
mail = Mail.new(&block)
elsif !mail
mail = Mail.new
end
mail.delivery_method(*smtp_settings)
mail.from = @email unless mail.from
mail
end
alias :message :compose
# Compose (optionaly) and send given email.
#
# ==== Examples
#
# gmail.deliver do
# to "friend@gmail.com"
# subject "Hello friend!"
# body "Hi! How are you?"
# end
#
# ... or with already created message:
#
# mail = Mail.new { ... }
# gmail.deliver(mail)
#
# mail = gmail.compose { ... }
# gmail.deliver(mail)
def deliver(mail=nil, raise_errors=false, &block)
mail = compose(mail, &block) if block_given?
mail.deliver!
rescue Object => ex
raise_errors and raise DeliveryError, "Couldn't deliver email: #{ex.to_s}"
end
# This version of deliver will raise error on failure...
def deliver!(mail=nil, &block)
deliver(mail, true, &block)
end
# Do something with given mailbox or within it context.
#
# ==== Examples
#
# mailbox = gmail.mailbox("INBOX")
# mailbox.emails(:all)
# mailbox.count(:unread, :before => Time.now-(20*24*3600))
#
# ... or block style:
#
# gmail.label("Work") do |mailbox|
# mailbox.emails(:unread)
# mailbox.count(:all)
# ...
# end
def mailbox(name, &block)
name = Net::IMAP.encode_utf7(name.to_s)
mailbox = (mailboxes[name] ||= Mailbox.new(self, name))
switch_to_mailbox(name) if @current_mailbox != name
if block_given?
mailbox_stack << @current_mailbox
result = block.arity == 1 ? block.call(mailbox) : block.call
mailbox_stack.pop
switch_to_mailbox(mailbox_stack.last)
return result
end
mailbox
end
alias :in_mailbox :mailbox
alias :in_label :mailbox
alias :label :mailbox
# Alias for mailbox("INBOX"). See Gmail::Client#mailbox
# for details.
def inbox
mailbox("INBOX")
end
def mailboxes
@mailboxes ||= {}
end
def inspect
"#"
end
#def fill_username(username)
# username =~ /@/ ? username : "#{username}@gmail.com"
#end
def mail_domain
@email.split('@')[0]
end
private
def switch_to_mailbox(mailbox)
conn.select(mailbox) if mailbox
@current_mailbox = mailbox
end
def mailbox_stack
@mailbox_stack ||= []
end
def smtp_settings
[:smtp, {
:address => GMAIL_SMTP_HOST,
:port => GMAIL_SMTP_PORT,
:domain => mail_domain,
:user_name => username,
:password => secret = {
:consumer_key => 'anonymous',
:consumer_secret => 'anonymous',
:token => '4/nM2QAaunKUINb4RrXPC55F-mix_k',
:token_secret => '41r18IyXjIvuyabS/NDyW6+m'
},
:authentication => :xoauth,
:enable_starttls_auto => true
}]
end
end # Client
end # Gmail