condTimedWait fixed
[public/netxms.git] / src / server / core / email.cpp
CommitLineData
b86ba1c0
VK
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
a551fe4d 23#include "nxcore.h"
b86ba1c0
VK
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
55typedef 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
67static char m_szSmtpServer[MAX_PATH] = "localhost";
68static WORD m_wSmtpPort = 25;
69static char m_szFromAddr[MAX_PATH] = "netxms@localhost";
70static Queue *m_pMailerQueue = NULL;
71
72
73//
74// Receive bytes from network or fail on timeout
75//
76
77int RecvWithTimeout(SOCKET hSocket, char *pszBuffer, int iBufSize)
78{
96ecd557 79 fd_set rdfs;
b86ba1c0
VK
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
98static 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;
1216cc73 149 SendEx(hSocket, "HELO netxms\r\n", 13, 0);
b86ba1c0
VK
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);
1216cc73 162 SendEx(hSocket, szBuffer, strlen(szBuffer), 0);
b86ba1c0
VK
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);
1216cc73 175 SendEx(hSocket, szBuffer, strlen(szBuffer), 0);
b86ba1c0
VK
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;
1216cc73 187 SendEx(hSocket, "DATA\r\n", 6, 0);
b86ba1c0
VK
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);
1216cc73 202 SendEx(hSocket, szBuffer, strlen(szBuffer), 0);
b86ba1c0 203 sprintf(szBuffer, "To: <%s>\r\n", pszRcpt);
1216cc73 204 SendEx(hSocket, szBuffer, strlen(szBuffer), 0);
b86ba1c0 205 sprintf(szBuffer, "Subject: <%s>\r\n", pszSubject);
1216cc73 206 SendEx(hSocket, szBuffer, strlen(szBuffer), 0);
b86ba1c0
VK
207
208 // Mail body
1216cc73
AK
209 SendEx(hSocket, pszText, strlen(pszText), 0);
210 SendEx(hSocket, "\r\n.\r\n", 5, 0);
b86ba1c0
VK
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;
1216cc73 222 SendEx(hSocket, "QUIT\r\n", 6, 0);
b86ba1c0
VK
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
ccdbbb52 270static THREAD_RESULT THREAD_CALL MailerThread(void *pArg)
b86ba1c0
VK
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 }
ccdbbb52 296 return THREAD_OK;
b86ba1c0
VK
297}
298
299
300//
301// Initialize mailer subsystem
302//
303
304void 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
320void 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
a551fe4d 331void NXCORE_EXPORTABLE PostMail(char *pszRcpt, char *pszSubject, char *pszText)
b86ba1c0
VK
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}