There are several tutorials on how to send gmail directly from a rails app. Presented here is a method for doing it from a standalone script. This method assumes rails is installed, but the script itself runs outside of any rails application. In my case in the wee hours of the morning as a cron job.
Usage of this script would be something like:
require 'send_gmail'
hsh={:to=>'name@domain.com', :subject=>'subject', :body=>'body'}
SendGMail.send_gmail(hsh)
The script can also handle attachments, albeit in an unusual way. Instead of giving it a file, you give it the data directly, along with a filename, mime_type, and transfer encoding:
raw_attachment={:mime_type=>'application/x-gzip', :body=>make_body, :transfer_encoding=>'base64', :filename=>'backup.tar.gz' }
hsh[:raw_attachment]=raw_attachment
It should be easy enough to modify the script to handle files as attachments directly, but I didn't have such a need.
Here is the complete listing. My thanks to Stephun Chu, as most of this is cribbed from his aforementioned post.
Edit: Fixed syntax error - Thanks Nolan!
send_gmail.rb
#!/usr/bin/env ruby
require 'rubygems'
gem 'actionmailer'
require 'action_mailer'
require 'openssl'
require 'net/smtp'
module SendGMail
@user_name='username@domain.com'
@domain='domain.com'
@password='password'
def SendGMail.send_gmail(hsh)
raw_attachments=hsh.fetch(:raw_attachements, [])
if hsh.has_key?(:raw_attachment)
raw_attachments.push(hsh[:raw_attachment])
end
mail=TMail::Mail.new
mail.to=hsh[:to]
mail.date=Time.now
mail.from=@user_name
mail.subject=hsh[:subject]
main=mail
main=TMail::Mail.new
main.body = hsh[:body]
puts main.body
main.set_content_type('text/plain', nil, 'charset'=>'utf-8')
mail.parts.push(main)
for raw_attachment in raw_attachments
part = TMail::Mail.new
transfer_encoding=raw_attachment[:transfer_encoding]
body=raw_attachment[:body]
case (transfer_encoding || "").downcase
when "base64" then
part.body = TMail::Base64.folding_encode(body)
when "quoted-printable"
part.body = [body].pack("M*")
else
part.body = body
end
part.transfer_encoding = transfer_encoding
part.set_content_type(raw_attachment[:mime_type], nil, 'name' => raw_attachment[:filename])
part.set_content_disposition("attachment", "filename"=>raw_attachment[:filename])
mail.parts.push(part)
end
mail.set_content_type('multipart', 'mixed')
ActionMailer::Base.deliver(mail)
end
ActionMailer::Base.smtp_settings = {
:address => 'smtp.gmail.com',
:domain => @domain,
:authentication => :plain,
:port => 587,
:user_name => @user_name,
:password => @password
}
Net::SMTP.class_eval do
private
def do_start(helodomain, user, secret, authtype)
raise IOError, 'SMTP session already started' if @started
check_auth_args user, secret, authtype if user or secret
sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
@socket = Net::InternetMessageIO.new(sock)
@socket.read_timeout = 60 #@read_timeout
@socket.debug_output = STDERR #@debug_output
check_response(critical { recv_response() })
do_helo(helodomain)
raise 'openssl library not installed' unless defined?(OpenSSL)
starttls
ssl = OpenSSL::SSL::SSLSocket.new(sock)
ssl.sync_close = true
ssl.connect
@socket = Net::InternetMessageIO.new(ssl)
@socket.read_timeout = 60 #@read_timeout
@socket.debug_output = STDERR #@debug_output
do_helo(helodomain)
authenticate user, secret, authtype if user
@started = true
ensure
unless @started
# authentication failed, cancel connection.
@socket.close if not @started and @socket and not @socket.closed?
@socket = nil
end
end
def do_helo(helodomain)
begin
if @esmtp
ehlo helodomain
else
helo helodomain
end
rescue Net::ProtocolError
if @esmtp
@esmtp = false
@error_occured = false
retry
end
raise
end
end
def starttls
getok('STARTTLS')
end
def quit
begin
getok('QUIT')
rescue EOFError, OpenSSL::SSL::SSLError
end
end
end
end
12 comments:
I suppose that's a typo.
":domain => @domain.com,"
Great script, I couldn't figure out how to get ssl to work.
Nice code..
but i try to attach file of 1.5MB (.rar extension) file and send it to a email address.
Email is working fine.I am receiving the mail but its showing the attached file size as 1Kb..when I decompress I can't able to do that..
Any suggestion ????
How will i attach the file and email it ???
In send_gmail try changing
raw_attachments=
hsh.fetch(:raw_attachements, [])
to:
raw_attachments = []
and attachments should work. At least for one attachment. But from there you should be able to figure the rest out.
And in the last comment change all misspellings of "attachement" to "attachment"
Thanks for your suggestion.
But still I am facing the same problem.
One interesting issue i found is whatever the name of file i am giving for attachment its taking that as attachment irrespective of that file existence.
My Code is like this raw_attachment={:mime_type=>'application/pdf', :body=> 'make_body', :transfer_encoding=>'base64', :filename=> "somename.pdf" }
hsh[:raw_attachment]=raw_attachment
I did some mistake in above code as i am not able to understand what the ":body=>" is doing.i think its the data part..may be anyway
MY requirement is
script to handle files as attachments directly
I want to send a .rar file as attachment.(files as attachments directly)
Means i will specify the path and the filename the script will automatically take that file as attachment.
Thanks in Advance
When I put both my script and send_gmail.rb in my home dir and call mine, everything works fine.
But when I try to make that a cron task, I get an error when I call SendGMail.send_gmail I get the following error: "Broken pipe".
What could be that?
Some puts here, some exception handling there, and I found out the error occurs in this exact line:
"ActionMailer::Base.deliver(mail)"
Error is "broken pipe".
What is the reason for that? It only happens when called by cron.
Thanks in advance.
Thank you. it works! (didn't try the attachment feature yet... not needed at the moment) Very Easy! So cool! I love you!
Thanks SO MUCH !!
Finally an easy way to do it without having to understand how it works !
Thank you so much. I've tried a few things and only yours work with Ruby 1.9. Awesome!
Alex
The formatting of your listing is that it cannot be copied. Could you provide a text-only version of your script?
Post a Comment