tweak to allow switch between PDU and text modes
[public/netxms.git] / src / server / smsdrv / generic / main.cpp
CommitLineData
9f24efb3
VK
1/*
2** NetXMS - Network Management System
3** Generic SMS driver
4** Copyright (C) 2003-2010 Alex 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**/
5039dede
AK
23
24#include "main.h"
25
7653c4fb 26bool SMSCreatePDUString(const char* phoneNumber, const char* message, char* pduBuffer, const int pduBufferSize);
770f8c64 27
17a4c8ab
AK
28static BOOL SMSDriverSendPDU(const TCHAR *pszPhoneNumber, const TCHAR *pszText);
29
5039dede 30static Serial m_serial;
17a4c8ab 31static enum { OM_TEXT, OM_PDU } omode = OM_PDU;
5039dede
AK
32
33// pszInitArgs format: portname,speed,databits,parity,stopbits
9f24efb3 34extern "C" BOOL EXPORT SMSDriverInit(const TCHAR *pszInitArgs)
5039dede
AK
35{
36 bool bRet = false;
9f24efb3 37 TCHAR *portName;
5039dede
AK
38
39 if (pszInitArgs == NULL || *pszInitArgs == 0)
40 {
41#ifdef _WIN32
9f24efb3 42 portName = _tcsdup(_T("COM1:"));
5039dede 43#else
9f24efb3 44 portName = _tcsdup(_T("/dev/ttyS0"));
5039dede
AK
45#endif
46 }
47 else
48 {
9f24efb3 49 portName = _tcsdup(pszInitArgs);
5039dede
AK
50 }
51
9f24efb3 52 DbgPrintf(1, _T("Loading Generic SMS Driver (configuration: %s)"), pszInitArgs);
5039dede 53
9f24efb3 54 TCHAR *p;
5039dede
AK
55 int portSpeed = 9600;
56 int dataBits = 8;
57 int parity = NOPARITY;
58 int stopBits = ONESTOPBIT;
59
9f24efb3 60 if ((p = _tcschr(portName, _T(','))) != NULL)
5039dede
AK
61 {
62 *p = 0; p++;
9f24efb3 63 int tmp = _tcstol(p, NULL, 10);
5039dede
AK
64 if (tmp != 0)
65 {
66 portSpeed = tmp;
67
9f24efb3 68 if ((p = _tcschr(p, _T(','))) != NULL)
5039dede
AK
69 {
70 *p = 0; p++;
9f24efb3 71 tmp = _tcstol(p, NULL, 10);
5039dede
AK
72 if (tmp >= 5 && tmp <= 8)
73 {
74 dataBits = tmp;
75
76 // parity
9f24efb3 77 if ((p = _tcschr(p, _T(','))) != NULL)
5039dede
AK
78 {
79 *p = 0; p++;
80 switch (tolower(*p))
81 {
9f24efb3 82 case _T('n'): // none
5039dede
AK
83 parity = NOPARITY;
84 break;
9f24efb3 85 case _T('o'): // odd
5039dede
AK
86 parity = ODDPARITY;
87 break;
9f24efb3 88 case _T('e'): // even
5039dede
AK
89 parity = EVENPARITY;
90 break;
91 }
92
93 // stop bits
9f24efb3 94 if ((p = _tcschr(p, _T(','))) != NULL)
5039dede
AK
95 {
96 *p = 0; p++;
97
9f24efb3 98 if (*p == _T('2'))
5039dede
AK
99 {
100 stopBits = TWOSTOPBITS;
101 }
17a4c8ab
AK
102
103 // Text or PDU mode
104 if ((p = _tcschr(p, _T(','))) != NULL)
105 {
106 *p = 0; p++;
107 if (*p == _T('X'))
108 omode = OM_TEXT;
109 else if (*p == _T('P'))
110 omode = OM_PDU;
111 }
5039dede
AK
112 }
113 }
114 }
115 }
116 }
117 }
118
119 switch (parity)
120 {
9f24efb3
VK
121 case ODDPARITY:
122 p = (TCHAR *)_T("ODD");
123 break;
124 case EVENPARITY:
125 p = (TCHAR *)_T("EVEN");
126 break;
127 default:
128 p = (TCHAR *)_T("NONE");
129 break;
5039dede 130 }
9f24efb3
VK
131 DbgPrintf(2, _T("SMS init: port={%s}, speed=%d, data=%d, parity=%s, stop=%d"),
132 portName, portSpeed, dataBits, p, stopBits == TWOSTOPBITS ? 2 : 1);
5039dede
AK
133
134 bRet = m_serial.Open(portName);
135 if (bRet)
136 {
9f24efb3 137 DbgPrintf(4, _T("SMS: port opened"));
5039dede
AK
138 m_serial.SetTimeout(1000);
139 m_serial.Set(portSpeed, dataBits, parity, stopBits);
140
141 // enter PIN: AT+CPIN="xxxx"
142 // register network: AT+CREG1
143
144 char szTmp[128];
145 m_serial.Write("ATZ\r\n", 5); // init modem && read user prefs
146 m_serial.Read(szTmp, 128); // read OK
9f24efb3 147 DbgPrintf(4, _T("SMS init: ATZ sent, got {%hs}"), szTmp);
5039dede
AK
148 m_serial.Write("ATE0\r\n", 6); // disable echo
149 m_serial.Read(szTmp, 128); // read OK
9f24efb3 150 DbgPrintf(4, _T("SMS init: ATE0 sent, got {%hs}"), szTmp);
5039dede
AK
151 m_serial.Write("ATI3\r\n", 6); // read vendor id
152 m_serial.Read(szTmp, 128); // read version
9f24efb3 153 DbgPrintf(4, _T("SMS init: ATI3 sent, got {%hs}"), szTmp);
5039dede 154
9f24efb3 155 if (stricmp(szTmp, "ERROR") != 0)
5039dede
AK
156 {
157 char *sptr, *eptr;
158
159 for(sptr = szTmp; (*sptr != 0) && ((*sptr == '\r') || (*sptr == '\n') || (*sptr == ' ') || (*sptr == '\t')); sptr++);
160 for(eptr = sptr; (*eptr != 0) && (*eptr != '\r') && (*eptr != '\n'); eptr++);
161 *eptr = 0;
9f24efb3
VK
162#ifdef UNICODE
163 WCHAR *wdata = WideStringFromMBString(sptr);
164 nxlog_write(MSG_GSM_MODEM_INFO, EVENTLOG_INFORMATION_TYPE, "ss", pszInitArgs, wdata);
165 free(wdata);
166#else
5039dede 167 nxlog_write(MSG_GSM_MODEM_INFO, EVENTLOG_INFORMATION_TYPE, "ss", pszInitArgs, sptr);
9f24efb3 168#endif
5039dede
AK
169 }
170 }
171 else
172 {
9f24efb3 173 DbgPrintf(1, _T("SMS: Unable to open serial port (%s)"), portName);
5039dede
AK
174 }
175
176 if (portName != NULL)
177 {
178 free(portName);
179 }
180
181 return bRet;
182}
183
17a4c8ab 184extern "C" BOOL EXPORT SMSDriverSend(const TCHAR *pszPhoneNumber, const TCHAR *pszText)
5039dede 185{
17a4c8ab
AK
186 if (omode == OM_PDU)
187 return SMSDriverSendPDU(pszPhoneNumber, pszText);
188
5039dede
AK
189 if (pszPhoneNumber != NULL && pszText != NULL)
190 {
191 char szTmp[128];
192
9f24efb3 193 DbgPrintf(3, _T("SMS send: to {%s}: {%s}"), pszPhoneNumber, pszText);
5039dede
AK
194
195 m_serial.Write("ATZ\r\n", 5); // init modem && read user prefs
196 m_serial.Read(szTmp, 128); // read OK
9f24efb3 197 DbgPrintf(4, _T("SMS send: ATZ sent, got {%hs}"), szTmp);
5039dede
AK
198 m_serial.Write("ATE0\r\n", 5); // disable echo
199 m_serial.Read(szTmp, 128); // read OK
9f24efb3 200 DbgPrintf(4, _T("SMS send: ATE0 sent, got {%hs}"), szTmp);
5039dede
AK
201 m_serial.Write("AT+CMGF=1\r\n", 11); // =1 - text message
202 m_serial.Read(szTmp, 128); // read OK
9f24efb3
VK
203 DbgPrintf(4, _T("SMS send: AT+CMGF=1 sent, got {%hs}"), szTmp);
204#ifdef UNICODE
205 char mbPhoneNumber[256];
206 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, pszPhoneNumber, -1, mbPhoneNumber, 256, NULL, NULL);
207 mbPhoneNumber[255] = 0;
208 snprintf(szTmp, sizeof(szTmp), "AT+CMGS=\"%s\"\r\n", mbPhoneNumber);
209#else
5039dede 210 snprintf(szTmp, sizeof(szTmp), "AT+CMGS=\"%s\"\r\n", pszPhoneNumber);
9f24efb3 211#endif
e24544f3 212 m_serial.Write(szTmp, (int)strlen(szTmp)); // set number
9f24efb3
VK
213#ifdef UNICODE
214 char mbText[161];
215 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, pszText, -1, mbText, 161, NULL, NULL);
216 mbText[160] = 0;
217 snprintf(szTmp, sizeof(szTmp), "%s%c\r\n", mbText, 0x1A);
218#else
5039dede 219 snprintf(szTmp, sizeof(szTmp), "%s%c\r\n", pszText, 0x1A);
9f24efb3 220#endif
e24544f3 221 m_serial.Write(szTmp, (int)strlen(szTmp)); // send text, end with ^Z
5039dede 222 m_serial.Read(szTmp, 128); // read +CMGS:ref_num
9f24efb3 223 DbgPrintf(4, _T("SMS send: AT+CMGS + message body sent, got {%hs}"), szTmp);
5039dede
AK
224 }
225
226 return true;
227}
228
17a4c8ab 229static BOOL SMSDriverSendPDU(const TCHAR *pszPhoneNumber, const TCHAR *pszText)
770f8c64
AK
230{
231 if (pszPhoneNumber != NULL && pszText != NULL)
232 {
7653c4fb
AK
233 const int bufferSize = 512;
234 char szTmp[bufferSize];
235 char phoneNumber[bufferSize], text[bufferSize];
770f8c64
AK
236
237 DbgPrintf(3, _T("SMS send: to {%s}: {%s}"), pszPhoneNumber, pszText);
238
cbc290b0
VK
239#ifdef UNICODE
240 if (WideCharToMultiByte(CP_ACP, 0, pszPhoneNumber, -1, phoneNumber, bufferSize, NULL, NULL) == 0 ||
241 WideCharToMultiByte(CP_ACP, 0, pszText, -1, text, bufferSize, NULL, NULL) == 0 )
7653c4fb 242 {
cbc290b0
VK
243 DbgPrintf(2, _T("Failed to convert phone number or text to multibyte string"));
244 return FALSE;
7653c4fb 245 }
cbc290b0
VK
246#else
247 nx_strncpy(phoneNumber, pszPhoneNumber, bufferSize);
248 nx_strncpy(text, pszText, bufferSize);
249#endif
7653c4fb 250
770f8c64
AK
251 m_serial.Write("ATZ\r\n", 5); // init modem && read user prefs
252 m_serial.Read(szTmp, 128); // read OK
253 DbgPrintf(4, _T("SMS send: ATZ sent, got {%hs}"), szTmp);
7653c4fb 254 m_serial.Write("ATE0\r\n", 6); // disable echo
770f8c64
AK
255 m_serial.Read(szTmp, 128); // read OK
256 DbgPrintf(4, _T("SMS send: ATE0 sent, got {%hs}"), szTmp);
257 m_serial.Write("AT+CMGF=0\r\n", 11); // =0 - send text in PDU mode
258 m_serial.Read(szTmp, 128); // read OK
259 DbgPrintf(4, _T("SMS send: AT+CMGF=0 sent, got {%hs}"), szTmp);
260
7653c4fb
AK
261 m_serial.Write("AT+CSCA?\r\n", 10); // send SMSC query
262 m_serial.Read(szTmp, 128); // read OK
263 DbgPrintf(4, _T("SMS send: AT+CSCA? sent, got {%hs}"), szTmp);
264 if (strlen(szTmp) > 10 && !strncmp(szTmp, "\r\n+CSCA: ", 9))
770f8c64 265 {
7653c4fb
AK
266 char szTmp2[128];
267 snprintf(szTmp2, 128, "AT+CSCA=%s\r\n", szTmp + 9);
268 szTmp2[127] = '\0';
269 m_serial.Write(szTmp2, strlen(szTmp2)); // set SMSC
270 m_serial.Read(szTmp, 128); // read OK
271 DbgPrintf(4, _T("SMS send: %hs sent, got {%hs}"), szTmp2, szTmp);
770f8c64 272 }
7653c4fb
AK
273
274 char pduBuffer[bufferSize];
275 SMSCreatePDUString(phoneNumber, text, pduBuffer, bufferSize);
276
277 snprintf(szTmp, sizeof(szTmp), "AT+CMGS=%d\r\n", strlen(pduBuffer)/2-1);
770f8c64 278 m_serial.Write(szTmp, (int)strlen(szTmp));
7653c4fb 279 DbgPrintf(4, _T("SMS send: %hs sent"), szTmp);
770f8c64 280 snprintf(szTmp, sizeof(szTmp), "%s%c\r\n", pduBuffer, 0x1A);
7653c4fb 281 DbgPrintf(4, _T("SMS about to send: %hs sent"), szTmp);
770f8c64
AK
282 m_serial.Write(szTmp, (int)strlen(szTmp)); // send text, end with ^Z
283 m_serial.Read(szTmp, 128); // read +CMGS:ref_num
284 DbgPrintf(4, _T("SMS send: AT+CMGS + message body sent, got {%hs}"), szTmp);
285 }
286
287 return true;
288}
289
9f24efb3 290extern "C" void EXPORT SMSDriverUnload()
5039dede
AK
291{
292 m_serial.Close();
293}
294
295
296//
297// DLL Entry point
298//
299
300#ifdef _WIN32
301
302BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
303{
304 if (dwReason == DLL_PROCESS_ATTACH)
305 DisableThreadLibraryCalls(hInstance);
306 return TRUE;
307}
308
309#endif