9aafc27c5aa171952c8dea1bbc3661e9651d2cf8
[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
72
73 //
74 // Receive bytes from network or fail on timeout
75 //
76
77 int RecvWithTimeout(SOCKET hSocket, char *pszBuffer, int iBufSize)
78 {
79 fd_set rdfs;
80 struct timeval timeout;
81
82 // Wait for data
83 FD_ZERO(&rdfs);
84 FD_SET(hSocket, &rdfs);
85 timeout.tv_sec = 10;
86 timeout.tv_usec = 0;
87 if (select(hSocket + 1, &rdfs, NULL, NULL, &timeout) == 0)
88 return 0; // Timeout
89
90 return recv(hSocket, pszBuffer, iBufSize, 0);
91 }
92
93
94 //
95 // Send e-mail
96 //
97
98 static DWORD SendMail(char *pszRcpt, char *pszSubject, char *pszText)
99 {
100 SOCKET hSocket;
101 struct hostent *hs;
102 struct sockaddr_in sa;
103 char szBuffer[256];
104 int iErr, iState = STATE_INITIAL;
105 DWORD dwRetCode;
106
107 // Fill in address structure
108 memset(&sa, 0, sizeof(sa));
109 sa.sin_family = AF_INET;
110 sa.sin_port = htons(m_wSmtpPort);
111
112 // Resolve hostname
113 hs = gethostbyname(m_szSmtpServer);
114 if (hs != NULL)
115 {
116 memcpy(&sa.sin_addr.s_addr, hs->h_addr, sizeof(DWORD));
117 }
118 else
119 {
120 sa.sin_addr.s_addr = inet_addr(m_szSmtpServer);
121 }
122
123 if ((sa.sin_addr.s_addr == INADDR_ANY) || (sa.sin_addr.s_addr == INADDR_NONE))
124 return SMTP_ERR_BAD_SERVER_NAME;
125
126 // Create socket
127 hSocket = socket(AF_INET, SOCK_STREAM, 0);
128 if (hSocket == -1)
129 return SMTP_ERR_COMM_FAILURE;
130
131 // Connect to server
132 if (connect(hSocket, (struct sockaddr *)&sa, sizeof(sa)) == 0)
133 {
134 while((iState != STATE_FINISHED) && (iState != STATE_ERROR))
135 {
136 iErr = RecvWithTimeout(hSocket, szBuffer, 255);
137 if (iErr > 0)
138 {
139 szBuffer[iErr] = 0;
140 DbgPrintf(AF_DEBUG_ACTIONS, "SMTP: %s", szBuffer);
141
142 switch(iState)
143 {
144 case STATE_INITIAL:
145 // Server should send 220 text after connect
146 if (!memcmp(szBuffer,"220",3))
147 {
148 iState = STATE_HELLO;
149 send(hSocket, "HELO netxms\r\n", 13, 0);
150 }
151 else
152 {
153 iState = STATE_ERROR;
154 }
155 break;
156 case STATE_HELLO:
157 // Server should respond with 250 text to our HELO command
158 if (!memcmp(szBuffer,"250",3))
159 {
160 iState = STATE_FROM;
161 sprintf(szBuffer, "MAIL FROM: <%s>\r\n", m_szFromAddr);
162 send(hSocket, szBuffer, strlen(szBuffer), 0);
163 }
164 else
165 {
166 iState = STATE_ERROR;
167 }
168 break;
169 case STATE_FROM:
170 // Server should respond with 250 text to our MAIL FROM command
171 if (!memcmp(szBuffer,"250",3))
172 {
173 iState = STATE_RCPT;
174 sprintf(szBuffer, "RCPT TO: <%s>\r\n", pszRcpt);
175 send(hSocket, szBuffer, strlen(szBuffer), 0);
176 }
177 else
178 {
179 iState = STATE_ERROR;
180 }
181 break;
182 case STATE_RCPT:
183 // Server should respond with 250 text to our RCPT TO command
184 if (!memcmp(szBuffer,"250",3))
185 {
186 iState = STATE_DATA;
187 send(hSocket, "DATA\r\n", 6, 0);
188 }
189 else
190 {
191 iState = STATE_ERROR;
192 }
193 break;
194 case STATE_DATA:
195 // Server should respond with 354 text to our DATA command
196 if (!memcmp(szBuffer,"354",3))
197 {
198 iState = STATE_MAIL_BODY;
199
200 // Mail header
201 sprintf(szBuffer, "From: NetXMS Server <%s>\r\n", m_szFromAddr);
202 send(hSocket, szBuffer, strlen(szBuffer), 0);
203 sprintf(szBuffer, "To: <%s>\r\n", pszRcpt);
204 send(hSocket, szBuffer, strlen(szBuffer), 0);
205 sprintf(szBuffer, "Subject: <%s>\r\n", pszSubject);
206 send(hSocket, szBuffer, strlen(szBuffer), 0);
207
208 // Mail body
209 send(hSocket, pszText, strlen(pszText), 0);
210 send(hSocket, "\r\n.\r\n", 5, 0);
211 }
212 else
213 {
214 iState = STATE_ERROR;
215 }
216 break;
217 case STATE_MAIL_BODY:
218 // Server should respond with 250 to our mail body
219 if (!memcmp(szBuffer,"250",3))
220 {
221 iState = STATE_QUIT;
222 send(hSocket, "QUIT\r\n", 6, 0);
223 }
224 else
225 {
226 iState = STATE_ERROR;
227 }
228 break;
229 case STATE_QUIT:
230 // Server should respond with 221 text to our QUIT command
231 if (!memcmp(szBuffer,"221",3))
232 {
233 iState = STATE_FINISHED;
234 }
235 else
236 {
237 iState = STATE_ERROR;
238 }
239 break;
240 default:
241 iState = STATE_ERROR;
242 break;
243 }
244 }
245 else
246 {
247 iState = STATE_ERROR;
248 }
249 }
250
251 // Shutdown communication channel
252 shutdown(hSocket, SHUT_RDWR);
253 closesocket(hSocket);
254
255 dwRetCode = (iState == STATE_FINISHED) ? SMTP_ERR_SUCCESS : SMTP_ERR_PROTOCOL_FAILURE;
256 }
257 else
258 {
259 dwRetCode = SMTP_ERR_COMM_FAILURE;
260 }
261
262 return dwRetCode;
263 }
264
265
266 //
267 // Mailer thread
268 //
269
270 static THREAD_RESULT THREAD_CALL MailerThread(void *pArg)
271 {
272 MAIL_ENVELOPE *pEnvelope;
273 DWORD dwResult;
274 static char *m_szErrorText[] =
275 {
276 "Sended successfully",
277 "Unable to resolve SMTP server name",
278 "Communication failure",
279 "SMTP conversation failure"
280 };
281
282 while(1)
283 {
284 pEnvelope = (MAIL_ENVELOPE *)m_pMailerQueue->GetOrBlock();
285 if (pEnvelope == INVALID_POINTER_VALUE)
286 break;
287
288 dwResult = SendMail(pEnvelope->szRcptAddr, pEnvelope->szSubject, pEnvelope->pszText);
289 if (dwResult != SMTP_ERR_SUCCESS)
290 PostEvent(EVENT_SMTP_FAILURE, g_dwMgmtNode, "dsss", dwResult,
291 m_szErrorText[dwResult], pEnvelope->szRcptAddr, pEnvelope->szSubject);
292
293 free(pEnvelope->pszText);
294 free(pEnvelope);
295 }
296 return THREAD_OK;
297 }
298
299
300 //
301 // Initialize mailer subsystem
302 //
303
304 void InitMailer(void)
305 {
306 ConfigReadStr("SMTPServer", m_szSmtpServer, MAX_PATH, "localhost");
307 ConfigReadStr("SMTPFromAddr", m_szFromAddr, MAX_PATH, "netxms@localhost");
308 m_wSmtpPort = (WORD)ConfigReadInt("SMTPPort", 25);
309
310 m_pMailerQueue = new Queue;
311
312 ThreadCreate(MailerThread, 0, NULL);
313 }
314
315
316 //
317 // Shutdown mailer
318 //
319
320 void ShutdownMailer(void)
321 {
322 m_pMailerQueue->Clear();
323 m_pMailerQueue->Put(INVALID_POINTER_VALUE);
324 }
325
326
327 //
328 // Post e-mail to queue
329 //
330
331 void NXCORE_EXPORTABLE PostMail(char *pszRcpt, char *pszSubject, char *pszText)
332 {
333 MAIL_ENVELOPE *pEnvelope;
334
335 pEnvelope = (MAIL_ENVELOPE *)malloc(sizeof(MAIL_ENVELOPE));
336 strncpy(pEnvelope->szRcptAddr, pszRcpt, MAX_RCPT_ADDR_LEN);
337 strncpy(pEnvelope->szSubject, pszSubject, MAX_EMAIL_SUBJECT_LEN);
338 pEnvelope->pszText = strdup(pszText);
339 m_pMailerQueue->Put(pEnvelope);
340 }