# Email with attachments?

**URL:** <https://rubytalk.org/t/email-with-attachments/7750>\
**Category:** ruby-talk\
**Created:** [1 September 2003 23:29 UTC](https://rubytalk.org/t/email-with-attachments/7750 "2003-09-01T23:29:39Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Shashank\_Date2](https://avatars.discourse-cdn.com/v4/letter/s/e99b99/32.png) [@Shashank\_Date2](https://rubytalk.org/u/Shashank_Date2)\
**Post date:** [1 September 2003 23:29 UTC](https://rubytalk.org/t/email-with-attachments/7750/1 "2003-09-01T23:29:39Z")

</div>

I have searched RAA and ruby-talk (esp. [40428]) and I have  
still not managed to find a simple solution to “sending email with  
binary attachments” on Windows. The code given in ruby-talk  
[40429] is badly mangled and I tried resurrecting it , but am not  
sure about lines 37 thru 57 (if they do not get further mangled).

I get the following error:

error on sending: The remote adapter is not compatible - connect(2)

Also, does the ability to “create MIME e-mail” automatically  
imply attachment functionality ? If yes, then I guess I need to  
figure it out with Tmail and RubyMail.

Any suggestions?  
TIA,  
– shanko

# ---------------------- line 0 --------------------------------

# File: CyOSendMimeMailSmtp.rb

# Author: Oliver Mensinger

# Version: 2002-01-22

# Status: Usable and stable

# (for simple mails incl. binary attachm.)

> **···**
>
> # 
> 
> # Description:
> 
> # Create Mime mail with text and attachments and
> 
> # send it via SMTP
> 
> # 
> 
> # Changes:
> 
> # 2002-01-22 After Ian reported a problem, the mail
> 
> # server address is now given to Net::SMTP::new
> 
> # instead to Net:SMTP::start
> 
> # 
> 
> # Supported functions: text part, binary attachments
> 
> # read from file
> 
> # To do: html part, binary attachment given to method
> 
> # as parameter, several recipients(cc, bcc)
> 
> require "net/smtp"  
> require “getoptlong”
> 
> class CyOSendMimeMailSmtp  
> attr\_accessor :text, :server,:from,:to,:subject
> 
> def initialize()  
> @boundary = createBoundary()  
> # attachments are stored in an array,  
> # each index is an hash see attach method  
> @attachments = []  
> @serverlocalhost = “”  
> @subject = “no subject”  
> @from = @to = @text = ""  
> end
> 
> # ---------------- line 37 -------------
> 
> def createBoundary()  
> return ["----=_CyOSendMimeMailSmtp\_Part_"] +  
> uniqueNumber()  
> end
> 
> private :createBoundary
> 
> # create an unique number, length variables
> 
> def uniqueNumber()  
> return [  
> sprintf("%02X", rand(999999990000000)), # random part  
> sprintf("%02X", Time.new.to\_i), # machine time  
> sprintf("%02X", $\_), # process  
> number  
> sprintf("%02X", Time.new.usec()) # micro seconds of machine  
> ]  
> end
> 
> private :uniqueNumber
> 
> # ---------------- line 57 -------------
> 
> # attachBinaryFile(phy\_filename, real\_filename)
> 
> # adds a file and converts it to base64
> 
> # phy\_filename is the physical filenameincl. path) of a file that must
> 
> exist
> 
> # real\_filename will be the name of the file in the mail message
> 
> # if real\_filename _is not given_, it will be the physical filename
> 
> # returns true if file exists and was attached to mail, otherwise false
> 
> def attachBinaryFile(phy\_filename, real\_filename)  
> # read file into string and convert it to base64  
> begin  
> f = File.new(phy\_filename);  
> data = f.read()  
> f.close()  
> rescue  
> return false  
> end
> 
> ```
> data = [data].pack("m*");
> 
> real_filename = phy_filename if (real_filename=="")
> 
> # the very special problem of phy_filename and real_filename:
> # the physical filename could be something like
> # tmp/12367672647342342,
> # as an external binary file stored outside of a database, where the
> # real filename is the original filename which is stored in the database
> # so we take the real_filename for determining the files type
> 
> attachment = {"type" => contentType(real_filename),
> "name" => File.basename(real_filename),
> "data" => data }
> 
> @attachments.push(attachment)
> 
> ```
> 
> end
> 
> def contentType(filename)  
> filename = File.basename(filename).downcase  
> return “image/jpg” if (filename =~ /.jp(e?)g$/)  
> return “image/gif” if (filename =~ /.gif$/)  
> return “text/html” if (filename =~ /.htm(l?)$/)  
> return “text/plain” if (filename =~ /.txt$/ )  
> return “application/zip” if (filename =~ /.zip$/)  
> # more types?!  
> return "application/octet-stream"  
> end
> 
> private :contentType
> 
> def sendMail()
> 
> ```
> raise "mail server not specified" if(@server.length == 0)
> raise "sender address not specified" if(@from.length == 0)
> raise "receiver address not specified" if(@to.length == 0)
> 
> #raise "nothing to send" if((@text.length==0)&(@attachments.length==0))
> 
> smtp = Net::SMTP.new(@server)
> smtp.start()
> smtp.ready(@from, @to) do |wa|
> wa.write("Reply-To:{@from}\r\n")
> wa.write("To:{@to}\r\n")
> wa.write("Subject:{@subject}\r\n")
> wa.write("MIME-Version:.0\r\n")
> # add multipart header if we have got attachments
> if(@attachments.length)
> wa.write("Content-Type: multipart/mixed;
> 
> ```
> 
> boundary="#{@boundary}"\r\n")  
> wa.write("\r\n")  
> wa.write(“This is a multi-part message in MIME format.\r\n”)  
> wa.write("\r\n")  
> end
> 
> ```
> # add text part if given
> if(@text.length)
> # add boundary if we are multiparted, otherwise just add text
> if(@attachments.length)
> wa.write("--#{@boundary}\r\n")
> wa.write("Content-Type: text/plain; charset=\"iso-8859-1\"\r\n")
> wa.write("Content-Transfer-Encoding:bit\r\n")
> # we don't take care of very old mail servers with bit only
> else
> # if only text and no attachm. we give the encoding
> wa.write("Content-Type: text/plain; charset=iso-8859-1\r\n")
> wa.write("Content-Transfer-Encoding:bit\r\n")
> end
> wa.write("\r\n")
> wa.write("#{@text}\r\n")
> wa.write("\r\n")
> end
> 
> # add attachments if given
> if(@attachments.length)
> @attachments.each do |part|
> wa.write("--#{@boundary}\r\n")
> wa.write("Content-Type:{part['type']};
> 
> ```
> 
> name="#{part[‘name’]}"\r\n")  
> wa.write(“Content-Transfer-Encoding: base64\r\n”)  
> wa.write(“Content-Disposition: attachment;  
> filename=”#{part[‘name’]}"\r\n")  
> wa.write("\r\n")  
> wa.write("#{part[‘data’]}") # no more need for \r\n here!  
> wa.write("\r\n")  
> end  
> end
> 
> ```
> # closing boundary if multiparted
> wa.write("--#{@boundary}--\r\n") if(@attachments.length)
> end # smtp.ready(...)
> 
> ```
> 
> end # def sendMail()  
> end # class CyOSendMimeMailSmtp
> 
> if $0 == **FILE**
> 
> # example for using CyOSendMimeMailSmtp class
> 
> # require “CyOSendMimeMailSmtp.rb”
> 
> mail = CyOSendMimeMailSmtp.new()  
> mail.server = "[pop3.everestkc.net](http://pop3.everestkc.net)"  
> mail.from = "sdate@everestkc.net"  
> [mail.to](http://mail.to) = "shanko\_date@yahoo.com"  
> mail.subject = "1st test using class CyOSendMimeMailSmtp"  
> mail.text = “I love Ruby!\n2nd line.”
> 
> #if(!mail.attachBinaryFile("/home/cyo/ruby.jpg"))
> 
> # puts “could not attach file”
> 
> #end
> 
> begin  
> mail.sendMail()  
> rescue  
> puts "error on sending: #{$!}"  
> end
> 
> end

---

<div class="post-metadata">

**Author:** ![Carlos](https://avatars.discourse-cdn.com/v4/letter/c/ecae2f/32.png) [@Carlos](https://rubytalk.org/u/Carlos)\
**Post date:** [2 September 2003 01:51 UTC](https://rubytalk.org/t/email-with-attachments/7750/2 "2003-09-02T01:51:41Z")

</div>

> I have searched RAA and ruby-talk (esp. [40428]) and I have  
> still not managed to find a simple solution to “sending email with  
> binary attachments” on Windows. The code given in ruby-talk  
> [40429] is badly mangled and I tried resurrecting it , but am not  
> sure about lines 37 thru 57 (if they do not get further mangled).
> 
> I get the following error:
> 
> error on sending: The remote adapter is not compatible - connect(2)
> 
> Also, does the ability to “create MIME e-mail” automatically  
> imply attachment functionality ? If yes, then I guess I need to  
> figure it out with Tmail and RubyMail.

I don’t know what generates the error you get, but you have some bugs here:

> def sendMail()
> 
> ```
> raise "mail server not specified" if(@server.length == 0)
> raise "sender address not specified" if(@from.length == 0)
> raise "receiver address not specified" if(@to.length == 0)
> 
> #raise "nothing to send" 
> 
> ```
> 
> if((@text.length==0)&(@attachments.length==0))
> 
> ```
> smtp = Net::SMTP.new(@server)
> smtp.start()
> smtp.ready(@from, @to) do |wa|
> wa.write("Reply-To:{@from}\r\n")
> 
> ```

```
                        ^^^^^^--- should be #{@from}

```

> ```
> wa.write("To:{@to}\r\n")
> 
> ```

```
                  ^^^^^--- should be #{@to}

```

> ```
> wa.write("Subject:{@subject}\r\n")
> 
> ```

```
                       ^^^^^^^^^^-- #{@subject}

```

> ```
> wa.write("MIME-Version:.0\r\n")
> 
> ```

Add the ‘#’ and maybe will work.

---

<div class="post-metadata">

**Author:** ![Shashank\_Date2](https://avatars.discourse-cdn.com/v4/letter/s/e99b99/32.png) [@Shashank\_Date2](https://rubytalk.org/u/Shashank_Date2)\
**Post date:** [2 September 2003 04:50 UTC](https://rubytalk.org/t/email-with-attachments/7750/3 "2003-09-02T04:50:46Z")

</div>

“Carlos” [angus@quovadis.com.ar](mailto:angus@quovadis.com.ar) wrote in message

> > ```
> > wa.write("Reply-To:{@from}\r\n")
> > 
> > ```
> 
> ```
> ^^^^^^--- should be #{@from}
> 
> ```
> 
> > ```
> > wa.write("To:{@to}\r\n")
> > 
> > ```
> 
> ```
> ^^^^^--- should be #{@to}
> 
> ```
> 
> > ```
> > wa.write("Subject:{@subject}\r\n")
> > 
> > ```
> 
> ```
> ^^^^^^^^^^-- #{@subject}
> 
> ```
> 
> > ```
> > wa.write("MIME-Version:.0\r\n")
> > 
> > ```

Thanks for pointing that out … I corrected them and some more  
minor issues and still not able to make it work. :-((

– shanko

---

<div class="post-metadata">

**Author:** ![Carlos](https://avatars.discourse-cdn.com/v4/letter/c/ecae2f/32.png) [@Carlos](https://rubytalk.org/u/Carlos)\
**Post date:** [2 September 2003 11:07 UTC](https://rubytalk.org/t/email-with-attachments/7750/4 "2003-09-02T11:07:30Z")

</div>

> Thanks for pointing that out … I corrected them and some more  
> minor issues and still not able to make it work. :-((

Here is the original script, with superfluous linefeeds removed (and  
different sender, etc., for testing purposes). It works for me…

require “net/smtp”  
require “getoptlong”

# File: CyOSendMimeMailSmtp.rb

# Author: Oliver Mensinger

# Version: 2002-01-22

# Status: Usable and stable for simple mails (incl. binary attachm.)

> **···**
>
> # 
> 
> # Description:
> 
> # Create Mime mail with text and attachments and send it via SMTP
> 
> # 
> 
> # Changes:
> 
> # 2002-01-22 After Ian reported a problem, the mail server address is now given
> 
> # to Net::SMTP::new instead to Net:SMTP::start
> 
> # 
> 
> # Supported functions: text part, binary attachments read from file
> 
> # To do: html part, binary attachment given to method as parameter, several recipients (cc, bcc)
> 
> class CyOSendMimeMailSmtp  
> attr\_accessor :text  
> attr\_accessor :server, :from, :to, :subject
> 
> def initialize()  
> @boundary = createBoundary()  
> @attachments = # or Array.new, attachments are stored in an array, each index is an hash (see attach method)  
> @server = “localhost”  
> @subject = “no subject”  
> @from = @to = @text = “”  
> end
> 
> def createBoundary()  
> return “----=_CyOSendMimeMailSmtp\_Part_” + uniqueNumber()  
> end
> 
> private :createBoundary
> 
> # create an unique number, length variables
> 
> def uniqueNumber()  
> return sprintf(“%02X”, rand(99999999 - 10000000) + 10000000) + # random part  
> sprintf(“%02X”, Time.new.to\_i) + # machine time  
> sprintf(“%02X”, $$) + # process number  
> sprintf(“%02X”, Time.new.usec()) # micro seconds of machine  
> end
> 
> private :uniqueNumber
> 
> # attachBinaryFile(phy\_filename, real\_filename = “”)
> 
> # adds a file and converts it to base64
> 
> # phy\_filename is the physical filename (incl. path) of a file that must exist
> 
> # real\_filename will be the name of the file in the mail message
> 
> # if real\_filename _is not given_, it will be the physical filename
> 
> # returns true if file exists and was attached to mail, otherwise false
> 
> def attachBinaryFile(phy\_filename, real\_filename = “”)  
> # read file into string and convert it to base64  
> begin  
> f = File.new(phy\_filename);  
> data = f.read()  
> f.close()  
> rescue  
> return false  
> end
> 
> ```
> data = [data].pack("m*");
> 
> real_filename = phy_filename if real_filename == ""
> 
> # the very special problem of phy_filename and real_filename:
> # the physical filename could by something like /tmp/12367672647342342,
> # as an external binary file stored outside of a database, where the
> # real filename is the original filename which is stored in the database.
> # so we take the real_filename for determining the files type
> attachment = { "type" => contentType(real_filename), "name" => File.basename(real_filename), "data" => data }
> @attachments.push(attachment)
> 
> ```
> 
> end
> 
> def contentType(filename)  
> filename = File.basename(filename).downcase  
> if filename =~ /.jp(e?)g$/ then return “image/jpg” end  
> if filename =~ /.gif$/ then return “image/gif” end  
> if filename =~ /.htm(l?)$/ then return “text/html” end  
> if filename =~ /.txt$/ then return “text/plain” end  
> if filename =~ /.zip$/ then return “application/zip” end  
> # more types?!  
> return “application/octet-stream”  
> end
> 
> private :contentType
> 
> def sendMail()  
> raise “mail server not specified” if @server.length == 0  
> raise “sender address not specified” if @from.length == 0  
> raise “receiver address not specified” if @to.length == 0  
> #raise “nothing to send” if (@text.length == 0) && (@attachments.length == 0)
> 
> ```
> smtp = Net::SMTP.new(@server)
> smtp.start()
> smtp.ready(@from, @to) do |wa|
> wa.write("Reply-To: #{@from}\r\n")
> wa.write("To: #{@to}\r\n")
> wa.write("Subject: #{@subject}\r\n")
> wa.write("MIME-Version: 1.0\r\n")
> # add multipart header if we have got attachments
> if (@attachments.length > 0)
> wa.write("Content-Type: multipart/mixed; boundary=\"#{@boundary}\"\r\n")
> wa.write("\r\n")
> wa.write("This is a multi-part message in MIME format.\r\n")
> wa.write("\r\n")
> end
> 
> # add text part if given
> if (@text.length > 0)
> # add boundary if we are multiparted, otherwise just add text
> if (@attachments.length > 0)
> wa.write("--#{@boundary}\r\n")
> wa.write("Content-Type: text/plain; charset=\"iso-8859-1\"\r\n")
> wa.write("Content-Transfer-Encoding: 8bit\r\n") # we don't take care of very old mail servers with 7 bit only
> else
> # if only text and no attachm. we give the encoding
> wa.write("Content-Type: text/plain; charset=iso-8859-1\r\n")
> wa.write("Content-Transfer-Encoding: 8bit\r\n")
> end
> wa.write("\r\n")
> wa.write("#{@text}\r\n")
> wa.write("\r\n")
> end
> 
> # add attachments if given
> if (@attachments.length > 0)
> @attachments.each do |part|
> wa.write("--#{@boundary}\r\n")
> wa.write("Content-Type: #{part['type']}; name=\"#{part['name']}\"\r\n")
> wa.write("Content-Transfer-Encoding: base64\r\n")
> wa.write("Content-Disposition: attachment; filename=\"#{part['name']}\"\r\n")
> wa.write("\r\n")
> wa.write("#{part['data']}") # no more need for \r\n here!
> wa.write("\r\n")
> end
> end
> 
> # closing boundary if multiparted
> wa.write("--#{@boundary}--\r\n") if (@attachments.length > 0)
> end # smtp.ready(...)
> 
> ```
> 
> end # def sendMail()  
> end # class CyOSendMimeMailSmtp
> 
> #=begin
> 
> # example for using CyOSendMimeMailSmtp class
> 
> #require “CyOSendMimeMailSmtp.rb”
> 
> mail = CyOSendMimeMailSmtp.new()  
> mail.server = “localhost” # localhost is also default  
> mail.from = “angus@quovadis.com.ar”  
> mail.to = “angus@other.address”  
> mail.subject = “1st test using class CyOSendMimeMailSmtp” # default is “no subject”  
> mail.text = “I love Ruby!\n2nd line.”
> 
> if (!mail.attachBinaryFile(“/home/angus/desktop0901.png”))  
> puts “could not attach file”  
> end
> 
> begin  
> mail.sendMail()  
> rescue  
> puts “error on sending: #{$!}”  
> end  
> #=end

---

<div class="post-metadata">

**Author:** ![Shashank\_Date2](https://avatars.discourse-cdn.com/v4/letter/s/e99b99/32.png) [@Shashank\_Date2](https://rubytalk.org/u/Shashank_Date2)\
**Post date:** [2 September 2003 13:11 UTC](https://rubytalk.org/t/email-with-attachments/7750/5 "2003-09-02T13:11:50Z")

</div>

“Carlos” [angus@quovadis.com.ar](mailto:angus@quovadis.com.ar) wrote in message

> Here is the original script, with superfluous linefeeds removed (and  
> different sender, etc., for testing purposes). It works for me…

Great ! I will take your word for it … until I figure out how to  
start a SMTP server on my machine (Windows XP Pro).

[snip]

> def uniqueNumber()  
> return sprintf(“%02X”, rand(99999999 - 10000000) + 10000000) + #  
> random part  
> sprintf(“%02X”, Time.new.to\_i) + # machine time  
> sprintf(“%02X”, $$) + # process number  
> sprintf(“%02X”, Time.new.usec()) # micro seconds of machine  
> end

This was the part which I had got all messed up. Everything else seemed  
to be OK (after you corrected me the first time).

[snip]

> mail = CyOSendMimeMailSmtp.new()  
> mail.server = “localhost” # localhost is also default  
> ^^^^^^^^^^^^^^^^^^^^  
> And this is what I am still struggling with. Any ideas are highly welcome.  
> Again thank you very much, Carlos !

– shanko

---

<div class="post-metadata">

**Author:** ![Carlos](https://avatars.discourse-cdn.com/v4/letter/c/ecae2f/32.png) [@Carlos](https://rubytalk.org/u/Carlos)\
**Post date:** [2 September 2003 13:40 UTC](https://rubytalk.org/t/email-with-attachments/7750/6 "2003-09-02T13:40:32Z")

</div>

> > mail = CyOSendMimeMailSmtp.new()  
> > mail.server = “localhost” # localhost is also default  
> > ^^^^^^^^^^^^^^^^^^^^  
> > And this is what I am still struggling with. Any ideas are highly welcome.

Just put the name of the smtp server you normally use to send mail. I guess  
it should be ‘[mail.everestkc.net](http://mail.everestkc.net)’, or something like that.

---

<div class="post-metadata">

**Author:** ![Shashank\_Date2](https://avatars.discourse-cdn.com/v4/letter/s/e99b99/32.png) [@Shashank\_Date2](https://rubytalk.org/u/Shashank_Date2)\
**Post date:** [3 September 2003 00:33 UTC](https://rubytalk.org/t/email-with-attachments/7750/7 "2003-09-03T00:33:58Z")

</div>

“Carlos” [angus@quovadis.com.ar](mailto:angus@quovadis.com.ar) wrote in message  
news:20030902134023.GA4771@quovadis.com.ar…

> > > mail = CyOSendMimeMailSmtp.new()  
> > > mail.server = “localhost” # localhost is also default  
> > > ^^^^^^^^^^^^^^^^^^^^  
> > > And this is what I am still struggling with. Any ideas are highly  
> > > welcome.
> 
> Just put the name of the smtp server you normally use to send mail. I  
> guess  
> it should be ‘[mail.everestkc.net](http://mail.everestkc.net)’, or something like that.

That was the first thing I tried … used “[smtp.everestkc.net](http://smtp.everestkc.net)” but it gave  
me the following error:  
so I thought it must be something else. What do you say ?

C:/ruby/lib/ruby/1.8/net/protocol.rb:188:in `on_read_timeout': socket read timeout (60 sec) (TimeoutError) from C:/ruby/lib/ruby/1.8/net/protocol.rb:182:in `rbuf\_fill’  
from C:/ruby/lib/ruby/1.8/net/protocol.rb:160:in `readuntil' from C:/ruby/lib/ruby/1.8/net/protocol.rb:171:in `readline’  
from C:/ruby/lib/ruby/1.8/net/smtp.rb:519:in `recv_response' from C:/ruby/lib/ruby/1.8/net/smtp.rb:414:in `initialize’  
from C:/ruby/lib/ruby/1.8/net/smtp.rb:414:in `critical' from C:/ruby/lib/ruby/1.8/net/smtp.rb:414:in `initialize’  
from C:/ruby/lib/ruby/1.8/net/smtp.rb:349:in `new' from C:/ruby/lib/ruby/1.8/net/smtp.rb:349:in `do\_start’  
from C:/ruby/lib/ruby/1.8/net/smtp.rb:330:in `start' from C:/atest/tst_email.rb:101:in `sendMail’  
from C:/atest/tst\_email.rb:171

Tool completed with exit code 1

---

<div class="post-metadata">

**Author:** ![Shashank\_Date2](https://avatars.discourse-cdn.com/v4/letter/s/e99b99/32.png) [@Shashank\_Date2](https://rubytalk.org/u/Shashank_Date2)\
**Post date:** [4 September 2003 01:04 UTC](https://rubytalk.org/t/email-with-attachments/7750/8 "2003-09-04T01:04:13Z")

</div>

“Shashank Date” [sdate@everestkc.net](mailto:sdate@everestkc.net) wrote in message

> That was the first thing I tried … used “[smtp.everestkc.net](http://smtp.everestkc.net)” but it gave  
> me the following error:  
> so I thought it must be something else. What do you say ?
> 
> C:/ruby/lib/ruby/1.8/net/protocol.rb:188:in `on\_read\_timeout’: socket read  
> timeout (60 sec) (TimeoutError)

[snip]

I tried changing the read timeout variable @read\_timeout (line 270 of  
C:/ruby/lib/ruby/1.8/net/smtp.rb) to 120 (from 60) but got the same error.

Any ideas, gurus ?

TIA,  
– shanko
