varro
varro

Reputation: 2492

Sending an e-mail with an attachment using ruby net/smtp

I've been trying to figure out how to send an e-mail to my gmail account with a binary attachment using the standard net/smtp. So far, I've succeeded in attaching a text file successfully - the following (based on what others have done) works for this:

#!/usr/bin/env ruby

require 'net/smtp'

addressee = '[email protected]'
server    = 'smtp.gmail.com'
port      = 587
account   = 'ACCOUNT'
from      = addressee
name      = 'NAME'
domain    = 'gmail.com'
subject   = 'test of smtp using ruby'
body      = 'Test of SMTP using Ruby.'
marker    = "PART_SEPARATOR"
filename  = "test-attachment"
filetext  = "attachment contents"

print "Enter password for #{account}: "
password  = $stdin.gets.chomp

# Define the main headers.
part1 = <<EOF
From: #{name} <#{from}>
To: <#{addressee}>
Subject: #{subject}
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=#{marker}
--#{marker}
EOF

# Define the message action
part2 = <<EOF
Content-Type: text/plain
Content-Transfer-Encoding:8bit

#{body}
--#{marker}
EOF

# Define the attachment section
part3 = <<EOF
Content-Type: text/plain
Content-Disposition: attachment; filename="#{File.basename(filename)}"

#{filetext}
--#{marker}--
EOF

message = part1 + part2 + part3

puts message

smtp = Net::SMTP.new server, port
smtp.enable_starttls

smtp.start(domain, account, password, :login) do
  smtp.send_message message, from, addressee
end

The problem is replacing a text attachment with an encoded binary attachment. The following variation of the above looks like it should work based on what I've been able to google, but does not send the attachment correctly:

#!/usr/bin/env ruby

require 'net/smtp'

addressee = '[email protected]'
server    = 'smtp.gmail.com'
port      = 587
account   = 'ACCOUNT'
from      = addressee
name      = 'NAME'
domain    = 'gmail.com'
subject   = 'test of smtp using ruby'
body      = 'Test of SMTP using Ruby.'
marker    = "PART_SEPARATOR"
filename  = "test-attachment"
filetext  = "attachment contents"

print "Enter password for #{account}: "
password  = $stdin.gets.chomp

# Encode contents into base64 format
encodedcontent = [filetext].pack("m")

# Define the main headers.
part1 = <<EOF
From: #{name} <#{from}>
To: <#{addressee}>
Subject: #{subject}
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=#{marker}
--#{marker}
EOF

# Define the message action
part2 = <<EOF
Content-Type: text/plain
Content-Transfer-Encoding:8bit

#{body}
--#{marker}
EOF

# Define the attachment section
part3 = <<EOF
Content-Type: multipart/mixed; name="#{File.basename(filename)}"
Content-Transfer-Encoding:base64
Content-Disposition: attachment; filename="#{File.basename(filename)}"

#{encodedcontent}
--#{marker}--
EOF

message = part1 + part2 + part3

puts message

smtp = Net::SMTP.new server, port
smtp.enable_starttls

smtp.start(domain, account, password, :login) do
  smtp.send_message message, from, addressee
end

Can anyone tell me what I'm doing wrong?

Upvotes: 4

Views: 1493

Answers (1)

varro
varro

Reputation: 2492

I've finally managed to send a binary attachment - the secret was using

Content-Type: application/octet-stream; name="#{filename}"
Content-Disposition: attachment; filename="#{filename}"; size=#{size}

in the attachment portion (part 3) of the message.

One other thing, this worked fine for a small test attachment, but when I tried a larger (140K) attachment, the attachment was truncated. Using

filecontent = File.binread(pathname)

rather than

filecontent = File.read(pathname)

seems to solve the problem. (I'm not quite sure why.)

Upvotes: 1

Related Questions