From ca52a38bc1abe56390ebc8b2e31f6315268cc4f6 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Wed, 4 Sep 2019 20:58:17 +0200 Subject: [PATCH 1/2] sendmail --- lib/SENDMAIL/sendemail.cpp | 302 +++++++++++++++++++++++++++++++++++ lib/SENDMAIL/sendemail.h | 39 +++++ sonoff/xdrv_01_webserver.ino | 170 +++++++++++++++++++- 3 files changed, 510 insertions(+), 1 deletion(-) create mode 100755 lib/SENDMAIL/sendemail.cpp create mode 100755 lib/SENDMAIL/sendemail.h diff --git a/lib/SENDMAIL/sendemail.cpp b/lib/SENDMAIL/sendemail.cpp new file mode 100755 index 000000000..b84e1fbaa --- /dev/null +++ b/lib/SENDMAIL/sendemail.cpp @@ -0,0 +1,302 @@ +#include "sendemail.h" + +// enable serial debugging +//#define DEBUG_EMAIL_PORT Serial + +SendEmail::SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used) : + host(host), port(port), user(user), passwd(passwd), timeout(timeout), ssl(ssl), auth_used(auth_used), client(new WiFiClientSecure()) +{ + +} + + + +String SendEmail::readClient() +{ + String r = client->readStringUntil('\n'); + r.trim(); + while (client->available()) { + delay(0); + r += client->readString(); + } + return r; +} + +void SetSerialBaudrate(int baudrate); + +bool SendEmail::send(const String& from, const String& to, const String& subject, const String& msg) +{ + if (!host.length()) + { + return false; + } + client->setTimeout(timeout); + // smtp connect +#ifdef DEBUG_EMAIL_PORT + SetSerialBaudrate(115200); + DEBUG_EMAIL_PORT.print("Connecting: "); + DEBUG_EMAIL_PORT.print(host); + DEBUG_EMAIL_PORT.print(":"); + DEBUG_EMAIL_PORT.println(port); +#endif + +#ifndef ARDUINO_ESP8266_RELEASE_2_4_2 + client->setInsecure(); + bool mfln = client->probeMaxFragmentLength(host.c_str(), port, 512); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.printf("MFLN supported: %s\n", mfln ? "yes" : "no"); +#endif + if (mfln) { + client->setBufferSizes(512, 512); + } +#endif + + if (!client->connect(host.c_str(), port)) + { +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println("Connection failed"); +#endif + return false; + } + + String buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("220"))) + { + return false; + } + + + buffer = F("EHLO "); + buffer += client->localIP().toString(); + + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("250"))) + { + return false; + } + if (user.length()>0 && passwd.length()>0 ) + { + + //buffer = F("STARTTLS"); + //client->println(buffer); + +if (auth_used==1) { + // plain +#ifdef USE_PLAIN + buffer = F("AUTH PLAIN"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("334"))) + { + return false; + } + + char plainAuth[100]; + memset(plainAuth,sizeof(plainAuth),0); + plainAuth[0] = '\0'; + strcpy(&plainAuth[1], user.c_str()); + strcpy(&plainAuth[2+user.length()],passwd.c_str()); + const char* pA = (const char*)&plainAuth; + char buf[100]; + base64_encode(buf, pA, user.length()+passwd.length()+2); + client->println(buf); + +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buf); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("235"))) + { + return false; + } +#endif + +} else { + + buffer = F("AUTH LOGIN"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("334"))) + { + return false; + } + base64 b; + //buffer = user; + //buffer = b.encode(buffer); + buffer = b.encode(user); + + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + //DEBUG_EMAIL_PORT.println(user); + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("334"))) + { + return false; + } + //buffer = this->passwd; + //buffer = b.encode(buffer); + buffer = b.encode(passwd); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + //DEBUG_EMAIL_PORT.println(passwd); + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("235"))) + { + return false; + } +} + + } + + // smtp send mail + buffer = F("MAIL FROM:"); + buffer += from; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("250"))) + { + return false; + } + buffer = F("RCPT TO:"); + buffer += to; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("250"))) + { + return false; + } + + + buffer = F("DATA"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = readClient(); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + if (!buffer.startsWith(F("354"))) + { + return false; + } + buffer = F("From: "); + buffer += from; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = F("To: "); + buffer += to; + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = F("Subject: "); + buffer += subject; + buffer += F("\r\n"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = msg; + client->println(buffer); + client->println('.'); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + buffer = F("QUIT"); + client->println(buffer); +#ifdef DEBUG_EMAIL_PORT + DEBUG_EMAIL_PORT.println(buffer); +#endif + return true; +} + +#ifdef USE_PLAIN +void SendEmail::a3_to_a4(unsigned char * a4, unsigned char * a3) { + a4[0] = (a3[0] & 0xfc) >> 2; + a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); + a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); + a4[3] = (a3[2] & 0x3f); +} + +int SendEmail::base64_encode(char *output, const char *input, int inputLen) { + const char* _b64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i = 0, j = 0; + int encLen = 0; + unsigned char a3[3]; + unsigned char a4[4]; + while(inputLen--) { + a3[i++] = *(input++); + if(i == 3) { + a3_to_a4(a4, a3); + for(i = 0; i < 4; i++) { + output[encLen++] = _b64_alphabet[a4[i]]; + } + i = 0; + } + } + if(i) { + for(j = i; j < 3; j++) { + a3[j] = '\0'; + } + a3_to_a4(a4, a3); + for(j = 0; j < i + 1; j++) { + output[encLen++] = _b64_alphabet[a4[j]]; + } + while((i++ < 3)) { + output[encLen++] = '='; + } + } + output[encLen] = '\0'; + return encLen; +} +#endif diff --git a/lib/SENDMAIL/sendemail.h b/lib/SENDMAIL/sendemail.h new file mode 100755 index 000000000..f622fb4e1 --- /dev/null +++ b/lib/SENDMAIL/sendemail.h @@ -0,0 +1,39 @@ +#ifndef __SENDEMAIL_H +#define __SENDEMAIL_H + +//#define DEBUG_EMAIL_PORT + +#include +#include +#include +#include + +class SendEmail +{ + private: + const String host; + const int port; + const String user; + const String passwd; + const int timeout; + const bool ssl; + const int auth_used; +#ifndef ARDUINO_ESP8266_RELEASE_2_4_2 + // use bear ssl + // #include "WiFiClientSecureLightBearSSL.h" + // BearSSL::WiFiClientSecure_light *client; + //BearSSL::WiFiClientSecure* client; + WiFiClientSecure* client; +#else + WiFiClient* client; +#endif + String readClient(); + void a3_to_a4(unsigned char * a4, unsigned char * a3); + int base64_encode(char *output, const char *input, int inputLen); + public: + SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used); + bool send(const String& from, const String& to, const String& subject, const String& msg); + ~SendEmail() {client->stop(); delete client;} +}; + +#endif diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index 01f5cde34..00dcd0d49 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -1053,6 +1053,7 @@ bool HandleRootStatusRefresh(void) #ifdef USE_SCRIPT_WEB_DISPLAY XdrvCall(FUNC_WEB_SENSOR); #endif + WSContentSend_P(PSTR("")); if (devices_present) { @@ -2350,6 +2351,151 @@ String UrlEncode(const String& text) return encoded; } +#ifdef USE_SENDMAIL + +#include "sendemail.h" + +//SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const bool ssl); +//SendEmail::send(const String& from, const String& to, const String& subject, const String& msg) +// sendmail [server:port:user:passwd:from:to:subject] data +// sendmail [*:*:*:*:*:to:subject] data uses defines from user_config +// sendmail currently only works with core 2.4.2 + +#define SEND_MAIL_MINRAM 19*1024 + +uint16_t SendMail(char *buffer) { + uint16_t count; + char *params,*oparams; + char *mserv; + uint16_t port; + char *user; + char *pstr; + char *passwd; + char *from; + char *to; + char *subject; + char *cmd; + char secure=0,auth=0; + uint16_t status=1; + SendEmail *mail=0; + + //DebugFreeMem(); + +// return if not enough memory + uint16_t mem=ESP.getFreeHeap(); + if (mem AUTH LOGIN 1 => PLAIN LOGIN + // 2 seconds timeout + #define MAIL_TIMEOUT 2000 + mail = new SendEmail(mserv, port,user,passwd, MAIL_TIMEOUT, auth); + +#ifdef EMAIL_FROM + if (*from=='*') { + from=(char*)EMAIL_FROM; + } +#endif + +exit: + if (mail) { + bool result=mail->send(from,to,subject,cmd); + delete mail; + if (result==true) status=0; + } + + + if (oparams) free(oparams); + return status; +} + +#endif + int WebSend(char *buffer) { // [sonoff] POWER1 ON --> Sends http://sonoff/cm?cmnd=POWER1 ON @@ -2462,17 +2608,27 @@ bool JsonWebColor(const char* dataBuf) return true; } -const char kWebSendStatus[] PROGMEM = D_JSON_DONE "|" D_JSON_WRONG_PARAMETERS "|" D_JSON_CONNECT_FAILED "|" D_JSON_HOST_NOT_FOUND ; +#define D_CMND_SENDMAIL "sendmail" +#define D_JSON_MEMORY_ERROR "memory error" + + +const char kWebSendStatus[] PROGMEM = D_JSON_DONE "|" D_JSON_WRONG_PARAMETERS "|" D_JSON_CONNECT_FAILED "|" D_JSON_HOST_NOT_FOUND "|" D_JSON_MEMORY_ERROR; const char kWebCommands[] PROGMEM = "|" // No prefix #ifdef USE_EMULATION D_CMND_EMULATION "|" +#endif +#ifdef USE_SENDMAIL + D_CMND_SENDMAIL "|" #endif D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBLOG "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBCOLOR "|" D_CMND_WEBSENSOR; void (* const WebCommand[])(void) PROGMEM = { #ifdef USE_EMULATION &CmndEmulation, +#endif +#ifdef USE_SENDMAIL + &CmndSendmail, #endif &CmndWebServer, &CmndWebPassword, &CmndWeblog, &CmndWebRefresh, &CmndWebSend, &CmndWebColor, &CmndWebSensor }; @@ -2500,6 +2656,18 @@ void CmndEmulation(void) } #endif // USE_EMULATION +#ifdef USE_SENDMAIL +void CmndSendmail(void) +{ + if (XdrvMailbox.data_len > 0) { + uint8_t result = SendMail(XdrvMailbox.data); + char stemp1[20]; + ResponseCmndChar(GetTextIndexed(stemp1, sizeof(stemp1), result, kWebSendStatus)); + } +} +#endif // USE_SENDMAIL + + void CmndWebServer(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { From 1d92436877356f740e253f605b026cdd8173575f Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Thu, 5 Sep 2019 11:14:33 +0200 Subject: [PATCH 2/2] sendmail core 2.3 fix --- lib/SENDMAIL/sendemail.cpp | 4 +++- lib/SENDMAIL/sendemail.h | 10 +++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/SENDMAIL/sendemail.cpp b/lib/SENDMAIL/sendemail.cpp index b84e1fbaa..ab2c90ecd 100755 --- a/lib/SENDMAIL/sendemail.cpp +++ b/lib/SENDMAIL/sendemail.cpp @@ -40,7 +40,8 @@ bool SendEmail::send(const String& from, const String& to, const String& subject DEBUG_EMAIL_PORT.println(port); #endif -#ifndef ARDUINO_ESP8266_RELEASE_2_4_2 +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) +#else client->setInsecure(); bool mfln = client->probeMaxFragmentLength(host.c_str(), port, 512); #ifdef DEBUG_EMAIL_PORT @@ -51,6 +52,7 @@ bool SendEmail::send(const String& from, const String& to, const String& subject } #endif + if (!client->connect(host.c_str(), port)) { #ifdef DEBUG_EMAIL_PORT diff --git a/lib/SENDMAIL/sendemail.h b/lib/SENDMAIL/sendemail.h index f622fb4e1..394612fc6 100755 --- a/lib/SENDMAIL/sendemail.h +++ b/lib/SENDMAIL/sendemail.h @@ -18,14 +18,14 @@ class SendEmail const int timeout; const bool ssl; const int auth_used; -#ifndef ARDUINO_ESP8266_RELEASE_2_4_2 +#if defined(ARDUINO_ESP8266_RELEASE_2_3_0) || defined(ARDUINO_ESP8266_RELEASE_2_4_2) + WiFiClient* client; +#else // use bear ssl // #include "WiFiClientSecureLightBearSSL.h" // BearSSL::WiFiClientSecure_light *client; - //BearSSL::WiFiClientSecure* client; - WiFiClientSecure* client; -#else - WiFiClient* client; + BearSSL::WiFiClientSecure* client; + //WiFiClientSecure* client; #endif String readClient(); void a3_to_a4(unsigned char * a4, unsigned char * a3);