cca51589534825423b6fdd24a8479c647695fd95
[public/netxms.git] / src / server / core / email.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** $module: email.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25
26 //
27 // Sender errors
28 //
29
30 #define SMTP_ERR_SUCCESS 0
31 #define SMTP_ERR_BAD_SERVER_NAME 1
32 #define SMTP_ERR_COMM_FAILURE 2
33 #define SMTP_ERR_PROTOCOL_FAILURE 3
34
35
36 //
37 // Mail sender states
38 //
39
40 #define STATE_INITIAL 0
41 #define STATE_HELLO 1
42 #define STATE_FROM 2
43 #define STATE_RCPT 3
44 #define STATE_DATA 4
45 #define STATE_MAIL_BODY 5
46 #define STATE_QUIT 6
47 #define STATE_FINISHED 7
48 #define STATE_ERROR 8
49
50
51 //
52 // Mail envelope structure
53 //
54
55 typedef struct
56 {
57 char szRcptAddr[MAX_RCPT_ADDR_LEN];
58 char szSubject[MAX_EMAIL_SUBJECT_LEN];
59 char *pszText;
60 } MAIL_ENVELOPE;
61
62
63 //
64 // Static data
65 //
66
67 static char m_szSmtpServer[MAX_PATH] = "localhost";
68 static WORD m_wSmtpPort = 25;
69 static char m_szFromAddr[MAX_PATH] = "netxms@localhost";
70 static Queue *m_pMailerQueue = NULL;
71 static THREAD m_hThread = INVALID_THREAD_HANDLE;
72
73
74 //
75 // Receive bytes from network or fail on timeout
76 //
77
78 int RecvWithTimeout(SOCKET hSocket, char *pszBuffer, int iBufSize)
79 {
80 fd_set rdfs;
81 struct timeval timeout;
82
83 // Wait for data
84 FD_ZERO(&rdfs);
85 FD_SET(hSocket, &rdfs);
86 timeout.tv_sec = 10;
87 timeout.tv_usec = 0;
88 if (select(hSocket + 1, &rdfs, NULL, NULL, &timeout) == 0)
89 return 0; // Timeout
90
91 return recv(hSocket, pszBuffer, iBufSize, 0);
92 }
93
94
95 //
96 // Send e-mail
97 //
98
99 static DWORD SendMail(char *pszRcpt, char *pszSubject, char *pszText)
100 {
101 SOCKET hSocket;
102 struct hostent *hs;
103 struct sockaddr_in sa;
104 char szBuffer[256];
105 int iErr, iState = STATE_INITIAL;
106 DWORD dwRetCode;
107
108 // Fill in address structure
109 memset(&sa, 0, sizeof(sa));
110 sa.sin_family = AF_INET;
111 sa.sin_port = htons(m_wSmtpPort);
112
113 // Resolve hostname
114 hs = gethostbyname(m_szSmtpServer);
115 if (hs != NULL)
116 {
117 memcpy(&sa.sin_addr.s_addr, hs->h_addr, sizeof(DWORD));
118 }
119 else
120 {
121 sa.sin_addr.s_addr = inet_addr(m_szSmtpServer);
122 }
123
124 if ((sa.sin_addr.s_addr == INADDR_ANY) || (sa.sin_addr.s_addr == INADDR_NONE))
125 return SMTP_ERR_BAD_SERVER_NAME;
126
127 // Create socket
128 hSocket = socket(AF_INET, SOCK_STREAM, 0);
129 if (hSocket == -1)
130 return SMTP_ERR_COMM_FAILURE;
131
132 // Connect to server
133 if (connect(hSocket, (struct sockaddr *)&sa, sizeof(sa)) == 0)
134 {
135 while((iState != STATE_FINISHED) && (iState != STATE_ERROR))
136 {
137 iErr = RecvWithTimeout(hSocket, szBuffer, 255);
138 if (iErr > 0)
139 {
140 szBuffer[iErr] = 0;
141 DbgPrintf(AF_DEBUG_ACTIONS, "SMTP: %s", szBuffer);
142
143 switch(iState)
144 {
145 case STATE_INITIAL:
146 // Server should send 220 text after connect
147 if (!memcmp(szBuffer,"220",3))
148 {
149 iState = STATE_HELLO;
150 SendEx(hSocket, "HELO netxms\r\n", 13, 0);
151 }
152 else
153 {
154 iState = STATE_ERROR;
155 }
156 break;
157 case STATE_HELLO:
158 // Server should respond with 250 text to our HELO command
159 if (!memcmp(szBuffer,"250",3))
160 {
161 iState = STATE_FROM;
162 sprintf(szBuffer, "MAIL FROM: <%s>\r\n", m_szFromAddr);
163 SendEx(hSocket, szBuffer, strlen(szBuffer), 0);
164 }
165 else
166 {
167 iState = STATE_ERROR;
168 }
169 break;
170 case STATE_FROM:
171 // Server should respond with 250 text to our MAIL FROM command
172 if (!memcmp(szBuffer,"250",3))
173 {
174 iState = STATE_RCPT;
175 sprintf(szBuffer, "RCPT TO: <%s>\r\n", pszRcpt);
176 SendEx(hSocket, szBuffer, strlen(szBuffer), 0);
177 }
178 else
179 {
180 iState = STATE_ERROR;
181 }
182 break;
183 case STATE_RCPT:
184 // Server should respond with 250 text to our RCPT TO command
185 if (!memcmp(szBuffer,"250",3))
186 {
187 iState = STATE_DATA;
188 SendEx(hSocket, "DATA\r\n", 6, 0);
189 }
190 else
191 {
192 iState = STATE_ERROR;
193 }
194 break;
195 case STATE_DATA:
196 // Server should respond with 354 text to our DATA command
197 if (!memcmp(szBuffer,"354",3))
198 {
199 iState = STATE_MAIL_BODY;
200
201 // Mail header
202 sprintf(szBuffer, "From: NetXMS Server <%s>\r\n", m_szFromAddr);
203 SendEx(hSocket, szBuffer, strlen(szBuffer), 0);
204 sprintf(szBuffer, "To: <%s>\r\n", pszRcpt);
205 SendEx(hSocket, szBuffer, strlen(szBuffer), 0);
206 sprintf(szBuffer, "Subject: <%s>\r\n\r\n", pszSubject);
207 SendEx(hSocket, szBuffer, strlen(szBuffer), 0);
208
209 // Mail body
210 SendEx(hSocket, pszText, strlen(pszText), 0);
211 SendEx(hSocket, "\r\n.\r\n", 5, 0);
212 }
213 else
214 {
215 iState = STATE_ERROR;
216 }
217 break;
218 case STATE_MAIL_BODY:
219 // Server should respond with 250 to our mail body
220 if (!memcmp(szBuffer,"250",3))
221 {
222 iState = STATE_QUIT;
223 SendEx(hSocket, "QUIT\r\n", 6, 0);
224 }
225 else
226 {
227 iState = STATE_ERROR;
228 }
229 break;
230 case STATE_QUIT:
231 // Server should respond with 221 text to our QUIT command
232 if (!memcmp(szBuffer,"221",3))
233 {
234 iState = STATE_FINISHED;
235 }
236 else
237 {
238 iState = STATE_ERROR;
239 }
240 break;
241 default:
242 iState = STATE_ERROR;
243 break;
244 }
245 }
246 else
247 {
248 iState = STATE_ERROR;
249 }
250 }
251
252 // Shutdown communication channel
253 shutdown(hSocket, SHUT_RDWR);
254 closesocket(hSocket);
255
256 dwRetCode = (iState == STATE_FINISHED) ? SMTP_ERR_SUCCESS : SMTP_ERR_PROTOCOL_FAILURE;
257 }
258 else
259 {
260 dwRetCode = SMTP_ERR_COMM_FAILURE;
261 }
262
263 return dwRetCode;
264 }
265
266
267 //
268 // Mailer thread
269 //
270
271 static THREAD_RESULT THREAD_CALL MailerThread(void *pArg)
272 {
273 MAIL_ENVELOPE *pEnvelope;
274 DWORD dwResult;
275 static char *m_szErrorText[] =
276 {
277 "Sended successfully",
278 "Unable to resolve SMTP server name",
279 "Communication failure",
280 "SMTP conversation failure"
281 };
282
283 while(1)
284 {
285 pEnvelope = (MAIL_ENVELOPE *)m_pMailerQueue->GetOrBlock();
286 if (pEnvelope == INVALID_POINTER_VALUE)
287 break;
288
289 dwResult = SendMail(pEnvelope->szRcptAddr, pEnvelope->szSubject, pEnvelope->pszText);
290 if (dwResult != SMTP_ERR_SUCCESS)
291 PostEvent(EVENT_SMTP_FAILURE, g_dwMgmtNode, "dsss", dwResult,
292 m_szErrorText[dwResult], pEnvelope->szRcptAddr, pEnvelope->szSubject);
293
294 free(pEnvelope->pszText);
295 free(pEnvelope);
296 }
297 return THREAD_OK;
298 }
299
300
301 //
302 // Initialize mailer subsystem
303 //
304
305 void InitMailer(void)
306 {
307 ConfigReadStr("SMTPServer", m_szSmtpServer, MAX_PATH, "localhost");
308 ConfigReadStr("SMTPFromAddr", m_szFromAddr, MAX_PATH, "netxms@localhost");
309 m_wSmtpPort = (WORD)ConfigReadInt("SMTPPort", 25);
310
311 m_pMailerQueue = new Queue;
312
313 m_hThread = ThreadCreateEx(MailerThread, 0, NULL);
314 }
315
316
317 //
318 // Shutdown mailer
319 //
320
321 void ShutdownMailer(void)
322 {
323 m_pMailerQueue->Clear();
324 m_pMailerQueue->Put(INVALID_POINTER_VALUE);
325 if (m_hThread != INVALID_THREAD_HANDLE)
326 ThreadJoin(m_hThread);
327 delete m_pMailerQueue;
328 }
329
330
331 //
332 // Post e-mail to queue
333 //
334
335 void NXCORE_EXPORTABLE PostMail(char *pszRcpt, char *pszSubject, char *pszText)
336 {
337 MAIL_ENVELOPE *pEnvelope;
338
339 pEnvelope = (MAIL_ENVELOPE *)malloc(sizeof(MAIL_ENVELOPE));
340 nx_strncpy(pEnvelope->szRcptAddr, pszRcpt, MAX_RCPT_ADDR_LEN);
341 nx_strncpy(pEnvelope->szSubject, pszSubject, MAX_EMAIL_SUBJECT_LEN);
342 pEnvelope->pszText = strdup(pszText);
343 m_pMailerQueue->Put(pEnvelope);
344 }