-
Notifications
You must be signed in to change notification settings - Fork 216
/
smtp.cpp
181 lines (155 loc) · 6.39 KB
/
smtp.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// Simple SMTP client for sending email message with attachment
//
// Only tested with Gmail sender account
//
// Prereqs for Gmail sender account:
// - recommended to create a dedicated email account
// - create an app password - https://support.google.com/accounts/answer/185833
// - set smtpUse in web config page to true, and enter account details on web page
//
// s60sc 2022
#include "appGlobals.h"
#if INCLUDE_SMTP
#if (!INCLUDE_CERTS)
const char* smtp_rootCACertificate = "";
#endif
// SMTP connection params, setup via web page
char smtp_login[MAX_HOST_LEN]; // sender email account
char SMTP_Pass[MAX_PWD_LEN]; // 16 digit app password, not account password
char smtp_email[MAX_HOST_LEN]; // receiver, can be same as smtp_login, or be any other email account
char smtp_server[MAX_HOST_LEN]; // the email service provider, eg smtp.gmail.com"
uint16_t smtp_port; // gmail SSL port 465;
#define MIME_TYPE "image/jpg"
#define ATTACH_NAME "frame.jpg"
// SMTP control
// Calling function has to populate SMTPbuffer and set smtpBufferSize for attachment data
TaskHandle_t emailHandle = NULL;
static char rspBuf[256]; // smtp response buffer
static char respCodeRx[4]; // smtp response code
static char subject[50];
static char message[100];
bool smtpUse = false; // whether or not to send email alerts
int emailCount = 0;
int alertMax = 10; // only applied to emails
static bool sendSmtpCommand(NetworkClientSecure& client, const char* cmd, const char* respCode) {
// wait from smtp server response, check response code and extract response data
LOG_VRB("Cmd: %s", cmd);
if (strlen(cmd)) client.println(cmd);
uint32_t start = millis();
while (!client.available() && millis() < start + (responseTimeoutSecs * 1000)) delay(1);
if (!client.available()) {
LOG_WRN("SMTP server response timeout");
return false;
}
// read in response code and message
client.read((uint8_t*)respCodeRx, 3);
respCodeRx[3] = 0; // terminator
int readLen = client.read((uint8_t*)rspBuf, 255);
rspBuf[readLen] = 0;
while (client.available()) client.read(); // bin the rest of response
// check response code with expected
LOG_VRB("Rx code: %s, resp: %s", respCodeRx, rspBuf);
if (strcmp(respCodeRx, respCode) != 0) {
// incorrect response code
LOG_ERR("Command %s got wrong response: %s", cmd, rspBuf);
return false;
}
return true;
}
static bool emailSend(const char* mimeType = MIME_TYPE, const char* fileName = ATTACH_NAME) {
// send email to defined smtp server
char content[100];
NetworkClientSecure client;
bool res = remoteServerConnect(client, smtp_server, smtp_port, smtp_rootCACertificate, EMAILCONN);
if (!res) return false;
while (true) { // fake non loop to enable breaks
res = false;
if (!sendSmtpCommand(client, "", "220")) break;
sprintf(content, "HELO %s: ", APP_NAME);
if (!sendSmtpCommand(client, content, "250")) break;
if (!sendSmtpCommand(client, "AUTH LOGIN", "334")) break;
if (!sendSmtpCommand(client, encode64(smtp_login), "334")) break;
if (!sendSmtpCommand(client, encode64(SMTP_Pass), "235")) break;
// send email header
sprintf(content, "MAIL FROM: <%s>", APP_NAME);
if (!sendSmtpCommand(client, content, "250")) break;
sprintf(content, "RCPT TO: <%s>", smtp_email);
if (!sendSmtpCommand(client, content, "250")) break;
// send message body header
if (!sendSmtpCommand(client, "DATA", "354")) break;
sprintf(content, "From: \"%s\" <%s>", APP_NAME, smtp_login);
client.println(content);
sprintf(content, "To: <%s>", smtp_email);
client.println(content);
sprintf(content, "Subject: %s", subject);
client.println(content);
// send message
client.println("MIME-Version: 1.0");
sprintf(content, "Content-Type: Multipart/mixed; boundary=%s", BOUNDARY_VAL);
client.println(content);
sprintf(content, "--%s", BOUNDARY_VAL);
client.println(content);
client.println("Content-Type: text/plain; charset=UTF-8");
client.println("Content-Transfer-Encoding: quoted-printable");
client.println("Content-Disposition: inline");
client.println();
client.println(message);
client.println();
if (alertBufferSize) {
// send attachment
client.println(content); // boundary
sprintf(content, "Content-Type: %s", mimeType);
client.println(content);
client.println("Content-Transfer-Encoding: base64");
sprintf(content, "Content-Disposition: attachment; filename=\"%s\"", fileName);
client.println(content);
// base64 encode attachment and send out in chunks
size_t chunkSize = 3;
for (size_t i = 0; i < alertBufferSize; i += chunkSize)
client.write(encode64chunk(alertBuffer + i, min(alertBufferSize - i, chunkSize)), 4);
}
client.println("\n"); // two lines to finish header
// close message data and quit
if (!sendSmtpCommand(client, ".", "250")) break;
if (!sendSmtpCommand(client, "QUIT", "221")) break;
res = true;
break;
}
// cleanly terminate connection
remoteServerClose(client);
alertBufferSize = 0;
return res;
}
static void emailTask(void* parameter) {
// send email
if (emailCount < alertMax) {
// send email if under daily limit
if (emailSend()) LOG_ALT("Sent daily email %u", emailCount + 1);
else LOG_WRN("Failed to send email");
}
if (++emailCount >= alertMax) LOG_WRN("Daily email limit %u reached", alertMax);
emailHandle = NULL;
vTaskDelete(NULL);
}
void emailAlert(const char* _subject, const char* _message) {
// send email to alert on required event
if (smtpUse) {
if (alertBuffer != NULL) {
if (emailHandle == NULL) {
strncpy(subject, _subject, sizeof(subject)-1);
snprintf(subject+strlen(subject), sizeof(subject)-strlen(subject), " from %s", hostName);
strncpy(message, _message, sizeof(message)-1);
xTaskCreate(&emailTask, "emailTask", EMAIL_STACK_SIZE, NULL, EMAIL_PRI, &emailHandle);
debugMemory("emailAlert");
} else LOG_WRN("Email alert already in progress");
} else LOG_WRN("Need to restart to setup email");
}
}
void prepSMTP() {
if (smtpUse) {
emailCount = 0;
if (alertBuffer == NULL) alertBuffer = (byte*)ps_malloc(MAX_JPEG);
LOG_INF("Email alerts active");
}
}
#endif