added HTTPS support to SMSEagle driver
[public/netxms.git] / src / smsdrv / smseagle / main.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** SMS driver for SMSEagle Hardware SMS Gateway
4 **
5 ** SMSEagle API documentation - https://www.smseagle.eu/api/
6 **
7 ** Copyright (C) 2014-2016 Raden Solutions
8 ** Copyright (C) 2016 TEMPEST a.s.
9 **
10 ** This program is free software; you can redistribute it and/or modify
11 ** it under the terms of the GNU Lesser General Public License as published by
12 ** the Free Software Foundation; either version 3 of the License, or
13 ** (at your option) any later version.
14 **
15 ** This program is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ** GNU General Public License for more details.
19 **
20 ** You should have received a copy of the GNU Lesser General Public License
21 ** along with this program; if not, write to the Free Software
22 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 **
24 ** File: main.cpp
25 **
26 **/
27
28 #include <nms_common.h>
29 #include <nms_util.h>
30 #include <nxconfig.h>
31 #include <curl/curl.h>
32
33 #ifdef _WIN32
34 #define EXPORT __declspec(dllexport)
35 #else
36 #define EXPORT
37 #endif
38
39 #ifndef CURL_MAX_HTTP_HEADER
40 // workaround for older cURL versions
41 #define CURL_MAX_HTTP_HEADER CURL_MAX_WRITE_SIZE
42 #endif
43
44 /**
45 * Request data for cURL call
46 */
47 struct RequestData
48 {
49 size_t size;
50 size_t allocated;
51 char *data;
52 };
53
54 /**
55 * Configuration
56 */
57 static char s_hostname[128] = "127.0.0.1";
58 static int s_port = 80;
59 static char s_login[128] = "user";
60 static char s_password[128] = "password";
61 static bool s_useHttps = false;
62
63 /**
64 * Init driver
65 */
66 extern "C" bool EXPORT SMSDriverInit(const TCHAR *initArgs, Config *config)
67 {
68 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
69 {
70 nxlog_debug(1, _T("SMSEagle: cURL initialization failed"));
71 return false;
72 }
73
74 nxlog_debug(1, _T("SMSEagle: driver loaded"));
75 nxlog_debug(3, _T("cURL version: %hs"), curl_version());
76 #if defined(_WIN32) || HAVE_DECL_CURL_VERSION_INFO
77 curl_version_info_data *version = curl_version_info(CURLVERSION_NOW);
78 char protocols[1024] = {0};
79 const char * const *p = version->protocols;
80 while (*p != NULL)
81 {
82 strncat(protocols, *p, strlen(protocols) - 1);
83 strncat(protocols, " ", strlen(protocols) - 1);
84 p++;
85 }
86 nxlog_debug(3, _T("cURL supported protocols: %hs"), protocols);
87 #endif
88
89 #ifdef UNICODE
90 char initArgsA[1024];
91 WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, initArgs, -1, initArgsA, 1024, NULL, NULL);
92 #define realInitArgs initArgsA
93 #else
94 #define realInitArgs initArgs
95 #endif
96
97 ExtractNamedOptionValueA(realInitArgs, "host", s_hostname, 128);
98 s_port = (int)ExtractNamedOptionValueAsIntA(realInitArgs, "port", s_port);
99 ExtractNamedOptionValueA(realInitArgs, "login", s_login, 128);
100 ExtractNamedOptionValueA(realInitArgs, "password", s_password, 128);
101 s_useHttps = ExtractNamedOptionValueAsBoolA(realInitArgs, "https", false);
102
103 return true;
104 }
105
106 /**
107 * Callback for processing data received from cURL
108 */
109 static size_t OnCurlDataReceived(char *ptr, size_t size, size_t nmemb, void *userdata)
110 {
111 RequestData *data = (RequestData *)userdata;
112 if ((data->allocated - data->size) < (size * nmemb))
113 {
114 char *newData = (char *)realloc(data->data, data->allocated + CURL_MAX_HTTP_HEADER);
115 if (newData == NULL)
116 {
117 return 0;
118 }
119 data->data = newData;
120 data->allocated += CURL_MAX_HTTP_HEADER;
121 }
122
123 memcpy(data->data + data->size, ptr, size * nmemb);
124 data->size += size * nmemb;
125
126 return size * nmemb;
127 }
128
129 /**
130 * Send SMS
131 */
132 extern "C" bool EXPORT SMSDriverSend(const TCHAR *phoneNumber, const TCHAR *text)
133 {
134 bool success = false;
135
136 nxlog_debug(4, _T("SMSEagle: phone/group=\"%s\", text=\"%s\""), phoneNumber, text);
137
138 CURL *curl = curl_easy_init();
139 if (curl != NULL)
140 {
141 #if HAVE_DECL_CURLOPT_NOSIGNAL
142 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, (long)1);
143 #endif
144
145 curl_easy_setopt(curl, CURLOPT_HEADER, (long)0); // do not include header in data
146 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);
147 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &OnCurlDataReceived);
148
149 RequestData *data = (RequestData *)malloc(sizeof(RequestData));
150 memset(data, 0, sizeof(RequestData));
151 curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
152
153 bool intlPrefix = (phoneNumber[0] == _T('+')); // this will be used to decide whether it is number or group name
154 #ifdef UNICODE
155 char *mbphone = MBStringFromWideString(phoneNumber);
156 char *mbmsg = MBStringFromWideString(text);
157 char *phone = curl_easy_escape(curl, mbphone, 0);
158 char *msg = curl_easy_escape(curl, mbmsg, 0);
159 free(mbphone);
160 free(mbmsg);
161 #else
162 char *phone = curl_easy_escape(curl, phoneNumber, 0);
163 char *msg = curl_easy_escape(curl, text, 0);
164 #endif
165
166 char url[4096];
167 snprintf(url, 4096,
168 intlPrefix ? "%s://%s:%d/index.php/http_api/send_sms?login=%s&pass=%s&to=%s&message=%s" : "%s://%s:%d/index.php/http_api/send_togroup?login=%s&pass=%s&groupname=%s&message=%s",
169 s_useHttps ? "https" : "http", s_hostname, s_port, s_login, s_password, phone, msg);
170 nxlog_debug(4, _T("SMSEagle: URL set to \"%hs\""), url);
171
172 curl_free(phone);
173 curl_free(msg);
174
175 if (curl_easy_setopt(curl, CURLOPT_URL, url) == CURLE_OK)
176 {
177 if (curl_easy_perform(curl) == CURLE_OK)
178 {
179 nxlog_debug(4, _T("SMSEagle: %d bytes received"), data->size);
180 if (data->allocated > 0)
181 data->data[data->size] = 0;
182
183 long response = 500;
184 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
185 nxlog_debug(4, _T("SMSEagle: response code %03d"), (int)response);
186 if (response == 200)
187 {
188 success = true;
189 }
190 }
191 else
192 {
193 nxlog_debug(4, _T("SMSEagle: call to curl_easy_perform() failed"));
194 }
195 }
196 else
197 {
198 nxlog_debug(4, _T("SMSEagle: call to curl_easy_setopt(CURLOPT_URL) failed"));
199 }
200 safe_free(data->data);
201 free(data);
202 curl_easy_cleanup(curl);
203 }
204 else
205 {
206 nxlog_debug(4, _T("SMSEagle: call to curl_easy_init() failed"));
207 }
208
209 return success;
210 }
211
212 /**
213 * Unload driver
214 */
215 extern "C" void EXPORT SMSDriverUnload()
216 {
217 }
218
219 #ifdef _WIN32
220
221 /**
222 * DLL Entry point
223 */
224 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
225 {
226 if (dwReason == DLL_PROCESS_ATTACH)
227 DisableThreadLibraryCalls(hInstance);
228 return TRUE;
229 }
230
231 #endif