Leanest way to send an email from bash

Is there a quick and dirty utility to send emails (does not need to be part of a package to receive and view email, in fact, I'd prefer if it wasn't).

Ideally I would like to be able to send attachments as well.

Mike

---------- Post updated at 04:29 PM ---------- Previous update was at 03:22 PM ----------

OK I am shocked this actually works! I am almost there. I need to "wrap" the base64 in something so the mail client recognizes it.

(   echo "MAIL FROM: xxxxx@yyyyy.com"
    sleep 1
    echo "RCPT TO: zzzzz@yyyyy.com"
    sleep 1
    echo "DATA"                                 #thank goodness it seems to receive DATA fast!
    echo "Subject: Test email"
    echo                                        #requires two new lines for subject
    echo "This is a test"
    echo
    cat filename | base64
    echo "."                                    #end of DATA character
    sleep 1
    echo "QUIT" ) | telnet smtp.yyyyy.com 25

---------- Post updated at 04:59 PM ---------- Previous update was at 04:29 PM ----------

Getting closer although the mail is still plain text.

--MyDogHasFleas
Content-Type: text/csv; name="thing.csv"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="thing.csv"

base64 lines


--MyDogHasFleas--
  

Mike

Have you got the mail or sendmail commands available? You can feed these with input files. Have a read of the manual pages. There are configuration files to edit to get them to relay the mail correctly, but that should be pretty easy and it would make your code much simpler.

Robin

Usually, sending email from the prompt involves using a mail-transport-agent like sendmail or mailx. If you don't have one, setting up your own mail server isn't exactly "lean", so I might suggest installing ssmtpd instead -- a "stub" MTA which just forwards to whatever real mail server you configure it to.

OK. I got it! Works great. Had to read some RFC documents. Got a clue where to start from a mention of RFC 2822 in a rejection message and from there I found the RFCs for MIME.

{   echo "MAIL FROM: me@yyyy.com"
    sleep 1
    echo "RCPT TO: you@xxxx.com"
    sleep 1
    echo "DATA"
    sleep 1
    echo "Subject: Test email" # some sources say extra line after subject but it breaks MIME for some reason.
    echo "From: Me <me@yyyy.com>" # gmail will reject email without additional From: header
    echo "MIME-Version: 1.0"
    echo "Content-Type: multipart/mixed; boundary=\"XXXXboundary text\""
    echo    
    echo "This is a multipart message in MIME format."
    echo "If you can read this you need a newer mail client or you need to stop playing with your time machine."
    echo "RmVsbG93IE1hbW1hbHM6IFdlIHN0cmlrZSB0aGUgRGlub3NhdXJzIGF0IGRhd24uICBMb25nIGxpdmUgdGhlIGV2b2x1dGlvbiEK"
    echo
    echo "--XXXXboundary text"
    echo "Content-Type: text/plain"
    echo    
    echo "this is the body text"
    echo
    echo "--XXXXboundary text"
    echo "Content-Type: text/csv; name=\"thing.csv\""
    echo "Content-Transfer-Encoding: base64"
    echo "Content-Disposition: attachment; filename=\"thing.csv\""
    echo
    base64 <myfile.csv
    echo
    echo "--XXXXboundary text--"
    echo "."
    sleep 1
    echo "QUIT"; } | telnet smtp.yyyy.com 25

Sending mail with attachments from a minimal linux system with no mail client! :smiley:

Mike

2 Likes

Well done for persevering with this and thanks for updating the thread too.

I've proved it works on RHEL 6 too. We have some servers where we don't want to allow mail for users to work, but we still need to get alerts out. Just installed the telnet client and it fired up. I had to increase the sleep counts and send a HELO first, but that may just be my relay server requirements. :rolleyes:

Good job! :b:

Robin

I should say HELO or EHLO and will add that. I should also add Date: to the extended headers (beginning of the body) under RFC 2822. In addition recipients are assumed to be bcc (by my server?). Those that should be To, CC, and BCC need to be spelled out in the extended header as well. BCC is frounded on by my employer so I'm going to add some code.

In doing more reseach it appears that subject is usually the last line of the extended header and that a blank line separates the extended header from the true "body" which is why many sources say you need a blank line after subject.

Mike

1 Like

For this type of script I would use expect. If you're connecting to an email host that either not too close or, has a bad connection. The sleep statements might not work to well.

Using autoexpect to create the skeleton for communications with the email server would ensure that you wait for a response. It's also very configurable as far as what you can do if the server doesn't respond as expected. I. E. the mail server responds that your recipient doesn't exist, etc...

Here's a the script autoexpect generated from a command line connection. Note that the scripts generally work route off the hop. Some "massaging" needs to be done. Also, I remove almost ALL the -exact to take into account variations on responses.

set timeout -1
spawn $env(SHELL)
match_max 100000
expect -exact "# "
send -- "telnet localhost 25\r"
expect -exact "telnet localhost 25\r
Trying 127.0.0.1...\r\r
Connected to localhost.\r\r
Escape character is '^\]'.\r\r
220 Laptop ESMTP Postfix\r
"
send -- "ehlo localhost\r"
expect -exact "ehlo localhost\r
250-Laptop\r
250-PIPELINING\r
250-SIZE 10240000\r
250-VRFY\r
250-ETRN\r
250-ENHANCEDSTATUSCODES\r
250-8BITMIME\r
250 DSN\r
"
send -- "mail from: me@mydomain.com\r"
expect -exact "mail from me@mydomain.com\r
250 2.1.0 Ok\r
"
send -- "rcpt to: you@yourdomain.com\r"
expect -exact "rcpt to: you@yourdomain.com\r
250 2.1.5 Ok\r
"
send -- "data\r"
expect -exact "data\r
354 End data with <CR><LF>.<CR><LF>\r
"
send -- "Subject: Testing again\r"
expect -exact "Subject: Testing again\r
"
send -- "\r"
expect -exact "\r
"
send -- "This is a test. only Only a test.\r
"
send -- "\r"
expect -exact "\r
"
send -- ".\r"
expect -exact ".\r
250 2.0.0 Ok: queued as EA2CCD202EA\r
"
send -- "quit\r"
expect -exact "quit\r
221 2.0.0 Bye\r
Connection closed by foreign host.\r\r
# "
send -- "^C"
expect -exact "\r
# "
send -- "^D"
expect eof

----------------------------------------------------
Forgot to mention. To make things much easier, you can use variables for the input(send).

Just 2� worth of a thought.

I agree that expect is a much more robust way to do this. In my case the mail server is on my intranet and the connection is very robust.

Mike

PS. It looks like the To: CC: and BCC: is just for the sake of the client. RECP TO: controls who actually gets the mail.

You can also use "nc" (netcat) instead of telnet. Should be a lot simpler as you don't have to spawn a subshell.

AFAIK, expect always spawns a subshell.

The script generated with expect and using netcat doesn't look much different.
Kind of a toss up, I guess.

set timeout -1
spawn $env(SHELL)
match_max 100000
expect -exact "\$ "
send -- "nc localhost 25\r"
expect -exact "nc localhost 25\r
220 Laptop ESMTP Postfix\r\r
"
send -- "ehlo localhost\r"
expect -exact "ehlo localhost\r
250-Laptop\r\r
250-PIPELINING\r\r
250-SIZE 10240000\r\r
250-VRFY\r\r
250-ETRN\r\r
250-ENHANCEDSTATUSCODES\r\r
250-8BITMIME\r\r
250 DSN\r\r
"
send -- "mail from: me\r"
expect -exact "mail from: me\r
250 2.1.0 Ok\r\r
"
send -- "rcpt to: you\r"
expect -exact "rcpt to: you\r
250 2.1.5 Ok\r\r
"
send -- "data\r"
expect -exact "data\r
354 End data with <CR><LF>.<CR><LF>\r\r
"
send -- "subject : Test\r"
expect -exact "subject : Test\r
"
send -- ".\r"
expect -exact ".\r
250 2.0.0 Ok: queued as 52D7ED2060C\r\r
"
send -- "^D"
expect -exact "\$ " 
send -- "^D"
expect eof