Sabtu, 03 Desember 2016

Tutorial NodeMCU: Send email with attachment using gmail smtp (ESP8266, WeMos, NodeMCU, Lua)

The following coding can be used to send a multipart email with an attachment via smtp.gmail.com. This code can be executed in firmware NodeMCU that has been customized to incorporate some of the following modules:
NodeMCU custom build by frightanic.com
    branch: master
    commit: ec265a6c21db22640795f190bdcb8a4f014cdced
    SSL: true
    modules: crypto,dht,file,gpio,http,net,node,rtctime,sntp,tmr,uart,wifi
 build     built on: 2016-12-02 13:05
 powered by Lua 5.1.4 on SDK 1.5.4.1(39cb9a32)
In order to use gmail smtp to send mail through nodemcu, then you need to switch the mode less secure apps.

  
Lua code for send multipart message with attachment.
-- Add Sent multipart message with attachment by Hendra Soewarno (0119067305)
 ---- Modification for GMAIL by Hendra Soewarno, for a better performance version
 ----- Using response code from GMAIL to determine next step, and not try
 ----- all the step when error response from GMAIL. The original version using
 ----- timer and counter to determine each request to server.
 ------- Modifications for GMAIL by Andreas "Andy" Reischle: www.AReResearch.net
 ------- See https://support.google.com/a/answer/176600?hl=de for details on smtp with gmail
 ------- Now that NodeMCU has working SSL support, we can also talk to email services that
 ------- require encryption. 
 ------- Caveat: I have not looked into the SSL implementation, but I suspect it is vulnerable
 ------- to man-in-the-middle attacks as the client doesn't check the server's certificate.
 ------- 20160415 ARe
 -------------Original Credits:
 -------------
 ------------ Working Example: https://www.youtube.com/watch?v=CcRbFIJ8aeU
 ------------ @description a basic SMTP email example. You must use an account which can provide unencrypted authenticated access.
 ------------ This example was tested with an AOL and Time Warner email accounts. GMail does not offer unecrypted authenticated access.
 ------------ To obtain your email's SMTP server and port simply Google it e.g. [my email domain] SMTP settings
 ------------ For example for timewarner you'll get to this page http://www.timewarnercable.com/en/support/faqs/faqs-internet/e-mailacco/incoming-outgoing-server-addresses.html
 ------------ To Learn more about SMTP email visit:
 ------------ SMTP Commands Reference - http://www.samlogic.net/articles/smtp-commands-reference.htm
 ------------ See "SMTP transport example" in this page http://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol
 ------------ @author Miguel
 --no longer required because it is part of the crypto module: require("base64")
 -- The email and password from the account you want to send emails from
 local MY_EMAIL = "***********@gmail.com"
 local EMAIL_PASSWORD = "e*********"
 -- The SMTP server and port of your email provider.
 -- If you don't know it google [my email provider] SMTP settings
 local SMTP_SERVER = "smtp.gmail.com"
 local SMTP_PORT = "465"
 -- The account you want to send email to
 local mail_to = "hendra-it@******.co.id"
 -- These are global variables. Don't change their values
 -- they will be changed in the functions below
 local email_subject = ""
 local email_body = ""
 local count = 0 -- will be used to determine next action
 local good_news = {"220", "250", "334", "334", "235", "250", "250", "354", "250", "221"}
 
 local smtp_socket = nil -- will be used as socket to email server

  -- The display() function will be used to print the SMTP server's response
 function display(sck,response)
    print("Got a response: ")
    print(response)
    response_code = string.sub(response,1,3)
    --print(response_code)
    --print(good_news[count+1])
    if(response_code~=good_news[count+1])then
        print("Send email failed!")
        count = 8 --Quit
    end  
    do_next()
 end
 -- The do_next() function is used to send the SMTP commands to the SMTP server in the required sequence.
 -- I was going to use socket callbacks but the code would not run callbacks after the first 3.
 function do_next()
       if(count == 0)then
         local IP_ADDRESS = wifi.sta.getip()
         print ("Send my IP: " .. IP_ADDRESS)
         smtp_socket:send("HELO "..IP_ADDRESS.."\r\n")
       elseif(count==1) then
         smtp_socket:send("AUTH LOGIN\r\n")
       elseif(count == 2) then
         smtp_socket:send(crypto.toBase64(MY_EMAIL).."\r\n")
       elseif(count == 3) then
         smtp_socket:send(crypto.toBase64(EMAIL_PASSWORD).."\r\n")
       elseif(count==4) then
         smtp_socket:send("MAIL FROM:<" .. MY_EMAIL .. ">\r\n")
       elseif(count==5) then
         smtp_socket:send("RCPT TO:<" .. mail_to ..">\r\n")
       elseif(count==6) then
         smtp_socket:send("DATA\r\n")
       elseif(count==7) then
         fin=file.open("temp","r")

         -- read chunk by chunk from temp file to prevent 1460 hard limit
         local function send(localSocket)
            local msg_chunk=file.read(1024)
            if msg_chunk ~= nil then
               localSocket:send(string.gsub(msg_chunk,"\r\n.\r\n",""))
               print(msg_chunk)
            else
               smtp_socket:on("sent", function() end)
               localSocket:send("\r\n.\r\n") --end of message
            end
         end

         -- triggers the send() function again once the first chunk of data was sent
         smtp_socket:on("sent", send) -- trigger next chunck
         send(smtp_socket) -- send first chunck
       
       elseif(count==8) then
          tmr.stop(0)
          smtp_socket:send("QUIT\r\n")
       else
         smtp_socket:close()
         print("Disconnected.")
       end
       count = count + 1
 end
 -- The connectted() function is executed when the SMTP socket is connected to the SMTP server.
 -- This function will create a timer to call the do_next function which will send the SMTP commands
 -- in sequence, one by one, every 5000 seconds. 
 -- You can change the time to be smaller if that works for you, I used 5000ms just because.
 function connected(sck)
   print("Connected - Starting...")
 end
 -- @name send_email
 -- @description Will initiated a socket connection to the SMTP server and trigger the connected() function
 -- @param subject The email's subject
 -- @param body The email's body
 function send_email(subject, body_plain, body_html, attachment)
    count = 0
    content = {}
    fin=file.open(attachment,"r")
    if fin then
       repeat
          local line=file.read(1024)
          if line then table.insert(content, line) end
       until not line      
       fin.close()      
    else
        print("File :" .. attachment .. " not found!")
        return 0
    end
   
    email_subject = subject
    -- put message at a temp file
    fout = file.open("temp","w")
    if fout then      
       fout:write("From: \"".. MY_EMAIL .."\"<"..MY_EMAIL..">\r\n")
       fout:write("To: \"".. mail_to .. "\"<".. mail_to..">\r\n")      
       fout:write("Subject: ".. email_subject .. "\r\n")
       fout:write("MIME-Version: 1.0\r\n")   
       fout:write("Content-Type: multipart/mixed; boundary=\"----=_MixPart_920403024\"\r\n\r\n")
       fout:write("------=_MixPart_920403024\r\n")
       fout:write("Content-Type: multipart/alternative; boundary=\"----=_AltPart_920403024\"\r\n\r\n")
       fout:write("------=_AltPart_920403024\r\n")
       fout:write("Content-type: text/plain; charset=iso-8859-1\r\n")
       fout:write("Content-Transfer-Encoding: quoted-printable\r\n\r\n")
       fout:write(body_plain .. "\r\n")
       fout:write("------=_AltPart_920403024\r\n")
       fout:write("Content-type: text/html; charset=iso-8859-1\r\n")
       fout:write("Content-Transfer-Encoding: quoted-printable\r\n\r\n")
       fout:write(body_html .. "\r\n")
       fout:write("------=_AltPart_920403024--\r\n")
       fout:write("------=_MixPart_920403024\r\n")
       fout:write("Content-Type: text/plain; name=" .. attachment .. "\r\n")
       fout:write("Content-Transfer-Encoding: 8bit\r\n")
       fout:write("Content-Disposition: attachment; filename=" .. attachment .. "\r\n\r\n")
       local i = 1
       while table.getn(content) > 0 do
           fout:write(table.remove(content,1))
       end
       fout:write("\r\n")
       fout:write("------=_MixPart_920403024--\r\n")
       fout:close()
    end
    print ("Open Connection")
    smtp_socket = net.createConnection(net.TCP,1)
    smtp_socket:on("connection",connected)
    smtp_socket:on("receive",display)
    smtp_socket:connect(SMTP_PORT,SMTP_SERVER)
 end
 -- Send an email
 print ("Sending started...")
 send_email("ESP8266-GMailSender with attachment","Please refer to attachment","<p>Please refer to attachment!</p>","init.lua")

Sample of multipart message:

From: "hendra.soewarno@gmail.com"<hendra.soewarno@gmail.com>
To: "hendra-it@*****.co.id"<hendra-it@******.co.id>
Subject: ESP8266-GMailSender with attachment
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="----=_MixPart_920403024"

------=_MixPart_920403024
Content-Type: multipart/alternative; boundary="----=_AltPart_920403024"

------=_AltPart_920403024
Content-type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: quoted-printable

Please refer to attachment
------=_AltPart_920403024
Content-type: text/html; charset=iso-8859-1
Content-Transfer-Encoding: quoted-printable

<p>Please refer to attachment!</p>
------=_AltPart_920403024--
------=_MixPart_920403024
Content-Type: text/plain; name=init.lua
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename=init.lua

Attachment file content
------=_MixPart_920403024--
.

Tidak ada komentar:

Posting Komentar