tweak to allow switch between PDU and text modes
[public/netxms.git] / src / server / smsdrv / generic / main.cpp
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 **/
23
24 #include "main.h"
25
26 bool SMSCreatePDUString(const char* phoneNumber, const char* message, char* pduBuffer, const int pduBufferSize);
27
28 static BOOL SMSDriverSendPDU(const TCHAR *pszPhoneNumber, const TCHAR *pszText);
29
30 static Serial m_serial;
31 static enum { OM_TEXT, OM_PDU } omode = OM_PDU;
32
33 // pszInitArgs format: portname,speed,databits,parity,stopbits
34 extern "C" BOOL EXPORT SMSDriverInit(const TCHAR *pszInitArgs)
35 {
36 bool bRet = false;
37 TCHAR *portName;
38
39 if (pszInitArgs == NULL || *pszInitArgs == 0)
40 {
41 #ifdef _WIN32
42 portName = _tcsdup(_T("COM1:"));
43 #else
44 portName = _tcsdup(_T("/dev/ttyS0"));
45 #endif
46 }
47 else
48 {
49 portName = _tcsdup(pszInitArgs);
50 }
51
52 DbgPrintf(1, _T("Loading Generic SMS Driver (configuration: %s)"), pszInitArgs);
53
54 TCHAR *p;
55 int portSpeed = 9600;
56 int dataBits = 8;
57 int parity = NOPARITY;
58 int stopBits = ONESTOPBIT;
59
60 if ((p = _tcschr(portName, _T(','))) != NULL)
61 {
62 *p = 0; p++;
63 int tmp = _tcstol(p, NULL, 10);
64 if (tmp != 0)
65 {
66 portSpeed = tmp;
67
68 if ((p = _tcschr(p, _T(','))) != NULL)
69 {
70 *p = 0; p++;
71 tmp = _tcstol(p, NULL, 10);
72 if (tmp >= 5 && tmp <= 8)
73 {
74 dataBits = tmp;
75
76 // parity
77 if ((p = _tcschr(p, _T(','))) != NULL)
78 {
79 *p = 0; p++;
80 switch (tolower(*p))
81 {
82 case _T('n'): // none
83 parity = NOPARITY;
84 break;
85 case _T('o'): // odd
86 parity = ODDPARITY;
87 break;
88 case _T('e'): // even
89 parity = EVENPARITY;
90 break;
91 }
92
93 // stop bits
94 if ((p = _tcschr(p, _T(','))) != NULL)
95 {
96 *p = 0; p++;
97
98 if (*p == _T('2'))
99 {
100 stopBits = TWOSTOPBITS;
101 }
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 }
112 }
113 }
114 }
115 }
116 }
117 }
118
119 switch (parity)
120 {
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;
130 }
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);
133
134 bRet = m_serial.Open(portName);
135 if (bRet)
136 {
137 DbgPrintf(4, _T("SMS: port opened"));
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
147 DbgPrintf(4, _T("SMS init: ATZ sent, got {%hs}"), szTmp);
148 m_serial.Write("ATE0\r\n", 6); // disable echo
149 m_serial.Read(szTmp, 128); // read OK
150 DbgPrintf(4, _T("SMS init: ATE0 sent, got {%hs}"), szTmp);
151 m_serial.Write("ATI3\r\n", 6); // read vendor id
152 m_serial.Read(szTmp, 128); // read version
153 DbgPrintf(4, _T("SMS init: ATI3 sent, got {%hs}"), szTmp);
154
155 if (stricmp(szTmp, "ERROR") != 0)
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;
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
167 nxlog_write(MSG_GSM_MODEM_INFO, EVENTLOG_INFORMATION_TYPE, "ss", pszInitArgs, sptr);
168 #endif
169 }
170 }
171 else
172 {
173 DbgPrintf(1, _T("SMS: Unable to open serial port (%s)"), portName);
174 }
175
176 if (portName != NULL)
177 {
178 free(portName);
179 }
180
181 return bRet;
182 }
183
184 extern "C" BOOL EXPORT SMSDriverSend(const TCHAR *pszPhoneNumber, const TCHAR *pszText)
185 {
186 if (omode == OM_PDU)
187 return SMSDriverSendPDU(pszPhoneNumber, pszText);
188
189 if (pszPhoneNumber != NULL && pszText != NULL)
190 {
191 char szTmp[128];
192
193 DbgPrintf(3, _T("SMS send: to {%s}: {%s}"), pszPhoneNumber, pszText);
194
195 m_serial.Write("ATZ\r\n", 5); // init modem && read user prefs
196 m_serial.Read(szTmp, 128); // read OK
197 DbgPrintf(4, _T("SMS send: ATZ sent, got {%hs}"), szTmp);
198 m_serial.Write("ATE0\r\n", 5); // disable echo
199 m_serial.Read(szTmp, 128); // read OK
200 DbgPrintf(4, _T("SMS send: ATE0 sent, got {%hs}"), szTmp);
201 m_serial.Write("AT+CMGF=1\r\n", 11); // =1 - text message
202 m_serial.Read(szTmp, 128); // read OK
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
210 snprintf(szTmp, sizeof(szTmp), "AT+CMGS=\"%s\"\r\n", pszPhoneNumber);
211 #endif
212 m_serial.Write(szTmp, (int)strlen(szTmp)); // set number
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
219 snprintf(szTmp, sizeof(szTmp), "%s%c\r\n", pszText, 0x1A);
220 #endif
221 m_serial.Write(szTmp, (int)strlen(szTmp)); // send text, end with ^Z
222 m_serial.Read(szTmp, 128); // read +CMGS:ref_num
223 DbgPrintf(4, _T("SMS send: AT+CMGS + message body sent, got {%hs}"), szTmp);
224 }
225
226 return true;
227 }
228
229 static BOOL SMSDriverSendPDU(const TCHAR *pszPhoneNumber, const TCHAR *pszText)
230 {
231 if (pszPhoneNumber != NULL && pszText != NULL)
232 {
233 const int bufferSize = 512;
234 char szTmp[bufferSize];
235 char phoneNumber[bufferSize], text[bufferSize];
236
237 DbgPrintf(3, _T("SMS send: to {%s}: {%s}"), pszPhoneNumber, pszText);
238
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 )
242 {
243 DbgPrintf(2, _T("Failed to convert phone number or text to multibyte string"));
244 return FALSE;
245 }
246 #else
247 nx_strncpy(phoneNumber, pszPhoneNumber, bufferSize);
248 nx_strncpy(text, pszText, bufferSize);
249 #endif
250
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);
254 m_serial.Write("ATE0\r\n", 6); // disable echo
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
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))
265 {
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);
272 }
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);
278 m_serial.Write(szTmp, (int)strlen(szTmp));
279 DbgPrintf(4, _T("SMS send: %hs sent"), szTmp);
280 snprintf(szTmp, sizeof(szTmp), "%s%c\r\n", pduBuffer, 0x1A);
281 DbgPrintf(4, _T("SMS about to send: %hs sent"), szTmp);
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
290 extern "C" void EXPORT SMSDriverUnload()
291 {
292 m_serial.Close();
293 }
294
295
296 //
297 // DLL Entry point
298 //
299
300 #ifdef _WIN32
301
302 BOOL 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