5e6c015785c2edbb8318778b4964f86b9a02dcf1
[public/netxms.git] / src / server / smsdrv / portech / main.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** SMS driver for Portech MV-37x VoIP GSM gateways
4 ** Copyright (C) 2011-2013 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU Lesser General Public License as published by
8 ** the Free Software Foundation; either version 3 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: main.cpp
21 **
22 **/
23
24 #include "portech.h"
25
26 #ifdef _WIN32
27 #define EXPORT __declspec(dllexport)
28 #else
29 #define EXPORT
30 #endif
31
32 /**
33 * Static data
34 */
35 static TCHAR s_primaryHostName[MAX_PATH] = _T("10.0.0.1");
36 static TCHAR s_secondaryHostName[MAX_PATH] = _T("");
37 static const TCHAR *s_hostName = s_primaryHostName;
38 static char s_login[MAX_PATH] = "admin";
39 static char s_password[MAX_PATH] = "admin";
40 static enum { OM_TEXT, OM_PDU } s_mode = OM_PDU;
41
42 /**
43 * Initialize driver
44 * option string format: option1=value1;option2=value2;...
45 * Valid options are: host, login, password, mode
46 */
47 extern "C" BOOL EXPORT SMSDriverInit(const TCHAR *options)
48 {
49 DbgPrintf(1, _T("Loading Portech MV-72x SMS Driver (configuration: %s)"), options);
50
51 // Parse arguments
52 ExtractNamedOptionValue(options, _T("host"), s_primaryHostName, MAX_PATH);
53 ExtractNamedOptionValue(options, _T("secondaryHost"), s_secondaryHostName, MAX_PATH);
54
55 #ifdef UNICODE
56 WCHAR temp[MAX_PATH];
57 ExtractNamedOptionValue(options, _T("login"), temp, MAX_PATH);
58 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, temp, -1, s_login, MAX_PATH, NULL, NULL);
59 #else
60 ExtractNamedOptionValue(options, _T("login"), s_login, MAX_PATH);
61 #endif
62
63 #ifdef UNICODE
64 ExtractNamedOptionValue(options, _T("password"), temp, MAX_PATH);
65 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, temp, -1, s_password, MAX_PATH, NULL, NULL);
66 #else
67 ExtractNamedOptionValue(options, _T("password"), s_password, MAX_PATH);
68 #endif
69
70 TCHAR mode[256] = _T("");
71 if (ExtractNamedOptionValue(options, _T("mode"), mode, 256))
72 {
73 if (!_tcsicmp(mode, _T("PDU")))
74 s_mode = OM_PDU;
75 else if (!_tcsicmp(mode, _T("TEXT")))
76 s_mode = OM_TEXT;
77 else
78 {
79 nxlog_write(MSG_SMSDRV_INVALID_OPTION, NXLOG_ERROR, "s", _T("mode"));
80 return FALSE;
81 }
82 }
83
84 return TRUE;
85 }
86
87 /**
88 * Login to unit
89 */
90 static bool DoLogin(SocketConnection *conn)
91 {
92 if (!conn->waitForText("username: ", 5000))
93 return false;
94
95 if (!conn->writeLine(s_login))
96 return false;
97
98 if (!conn->waitForText("password: ", 5000))
99 return false;
100
101 if (!conn->writeLine(s_password))
102 return false;
103
104 if (!conn->waitForText("]", 5000))
105 return false;
106
107 if (!conn->writeLine("module"))
108 return false;
109
110 if (!conn->waitForText("got!! press 'ctrl-x' to release module", 2000))
111 return false;
112
113 return true;
114 }
115
116 /**
117 * Logout from unit
118 */
119 static void DoLogout(SocketConnection *conn)
120 {
121 if (conn->write("\x18", 1) > 0)
122 {
123 if (conn->waitForText("]", 5000))
124 conn->writeLine("logout");
125 }
126 }
127
128 /**
129 * Return immediately if given operation failed
130 */
131 #define __chk(x) if (!(x)) { return FALSE; }
132
133 /**
134 * Send SMS in text mode
135 */
136 static BOOL SendText(SocketConnection *conn, const TCHAR *pszPhoneNumber, const TCHAR *pszText)
137 {
138 char szTmp[128];
139
140 DbgPrintf(3, _T("Sending SMS (text mode): rcpt=\"%s\" text=\"%s\""), pszPhoneNumber, pszText);
141
142 __chk(conn->writeLine("ATZ")); // init modem
143 __chk(conn->waitForText("OK", 10000));
144 DbgPrintf(4, _T("SMS: ATZ sent"));
145
146 ThreadSleep(1);
147 __chk(conn->writeLine("ATE0")); // disable echo
148 __chk(conn->waitForText("OK", 10000));
149 DbgPrintf(4, _T("SMS: ATE0 sent"));
150
151 ThreadSleep(1);
152 __chk(conn->writeLine("AT+CMGF=1")); // text mode
153 __chk(conn->waitForText("OK", 5000));
154 DbgPrintf(4, _T("SMS: AT+CMGF=1 sent"));
155
156 #ifdef UNICODE
157 char mbPhoneNumber[256];
158 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, pszPhoneNumber, -1, mbPhoneNumber, 256, NULL, NULL);
159 mbPhoneNumber[255] = 0;
160 snprintf(szTmp, sizeof(szTmp), "AT+CMGS=%s\r\n", mbPhoneNumber);
161 #else
162 snprintf(szTmp, sizeof(szTmp), "AT+CMGS=%s\r\n", pszPhoneNumber);
163 #endif
164 __chk(conn->writeLine(szTmp)); // set number
165 __chk(conn->waitForText(">", 10000));
166
167 #ifdef UNICODE
168 char mbText[161];
169 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, pszText, -1, mbText, 161, NULL, NULL);
170 mbText[160] = 0;
171 snprintf(szTmp, sizeof(szTmp), "%s%c\r\n", mbText, 0x1A);
172 #else
173 snprintf(szTmp, sizeof(szTmp), "%s%c\r\n", pszText, 0x1A);
174 #endif
175 __chk(conn->write(szTmp, (int)strlen(szTmp)) > 0); // send text, end with ^Z
176 __chk(conn->waitForText("+CMGS", 45000));
177 __chk(conn->waitForText("OK", 5000));
178 DbgPrintf(4, _T("SMS sent successfully"));
179
180 return TRUE;
181 }
182
183 /**
184 * Send SMS in PDU mode
185 */
186 static BOOL SendPDU(SocketConnection *conn, const TCHAR *pszPhoneNumber, const TCHAR *pszText)
187 {
188 const int bufferSize = 512;
189 char szTmp[bufferSize];
190 char phoneNumber[bufferSize], text[bufferSize];
191
192 DbgPrintf(3, _T("Sending SMS (PDU mode): rcpt=\"%s\" text=\"%s\""), pszPhoneNumber, pszText);
193
194 #ifdef UNICODE
195 if (WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, pszPhoneNumber, -1, phoneNumber, bufferSize, NULL, NULL) == 0 ||
196 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, pszText, -1, text, bufferSize, NULL, NULL) == 0)
197 {
198 DbgPrintf(2, _T("SMS: Failed to convert phone number or text to multibyte string"));
199 return FALSE;
200 }
201 #else
202 nx_strncpy(phoneNumber, pszPhoneNumber, bufferSize);
203 nx_strncpy(text, pszText, bufferSize);
204 #endif
205
206 __chk(conn->writeLine("ATZ")); // init modem
207 __chk(conn->waitForText("OK", 10000));
208 DbgPrintf(4, _T("SMS: ATZ sent"));
209
210 ThreadSleep(1);
211 __chk(conn->writeLine("ATE0")); // disable echo
212 __chk(conn->waitForText("OK", 10000));
213 DbgPrintf(4, _T("SMS: ATE0 sent"));
214
215 ThreadSleep(1);
216 __chk(conn->writeLine("AT+CMGF=0")); // PDU mode
217 __chk(conn->waitForText("OK", 10000));
218 DbgPrintf(4, _T("SMS: AT+CMGF=0 sent"));
219
220 char pduBuffer[bufferSize];
221 SMSCreatePDUString(phoneNumber, text, pduBuffer, bufferSize);
222
223 snprintf(szTmp, sizeof(szTmp), "AT+CMGS=%d\r\n", (int)strlen(pduBuffer) / 2 - 1);
224 __chk(conn->write(szTmp, (int)strlen(szTmp)) > 0);
225 __chk(conn->waitForText(">", 10000));
226 DbgPrintf(4, _T("SMS: %hs sent"), szTmp);
227
228 snprintf(szTmp, sizeof(szTmp), "%s%c\r\n", pduBuffer, 0x1A);
229 __chk(conn->write(szTmp, (int)strlen(szTmp)) > 0);
230 __chk(conn->waitForText("+CMGS", 45000));
231 __chk(conn->waitForText("OK", 2000));
232 DbgPrintf(4, _T("SMS sent successfully"));
233
234 return TRUE;
235 }
236
237 /**
238 * Send SMS
239 */
240 extern "C" BOOL EXPORT SMSDriverSend(const TCHAR *pszPhoneNumber, const TCHAR *pszText)
241 {
242 SocketConnection *conn;
243 BOOL success = FALSE;
244
245 if ((pszPhoneNumber == NULL) || (pszText == NULL))
246 return FALSE;
247
248 bool canRetry = true;
249
250 retry:
251 conn = SocketConnection::createTCPConnection(s_hostName, 23, 10000);
252 if (conn != NULL)
253 {
254 conn->write("\xFF\xFE\x01\xFF\xFC\x01", 6);
255 if (DoLogin(conn))
256 {
257 success = (s_mode == OM_PDU) ? SendPDU(conn, pszPhoneNumber, pszText) : SendText(conn, pszPhoneNumber, pszText);
258 }
259 DoLogout(conn);
260 conn->disconnect();
261 delete conn;
262 }
263
264 if (!success && canRetry)
265 {
266 const TCHAR *newName = (s_hostName == s_primaryHostName) ? s_secondaryHostName : s_primaryHostName;
267 if (newName[0] != 0)
268 {
269 s_hostName = newName;
270 DbgPrintf(4, _T("Portech SMS driver: switched to host %s"), s_hostName);
271 canRetry = false;
272 goto retry;
273 }
274 }
275
276 return success;
277 }
278
279 /**
280 * Unload SMS driver
281 */
282 extern "C" void EXPORT SMSDriverUnload()
283 {
284 }
285
286 #ifdef _WIN32
287
288 /**
289 * DLL Entry point
290 */
291 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
292 {
293 if (dwReason == DLL_PROCESS_ATTACH)
294 DisableThreadLibraryCalls(hInstance);
295 return TRUE;
296 }
297
298 #endif