"Fossies" - the Fresh Open Source Software Archive 
Member "netxms-3.8.166/src/server/core/xmpp.cpp" (23 Feb 2021, 12861 Bytes) of package /linux/misc/netxms-3.8.166.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "xmpp.cpp" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
3.6.300_vs_3.7.95.
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2019 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 ** File: xmpp.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24 #include <netxms-version.h>
25
26 #define DEBUG_TAG _T("xmpp")
27
28 #if XMPP_SUPPORTED
29
30 #include <strophe.h>
31
32 /**
33 * Logger
34 */
35 static void Logger(void * const userdata, const xmpp_log_level_t level, const char * const area, const char * const msg)
36 {
37 switch(level)
38 {
39 case XMPP_LEVEL_ERROR:
40 nxlog_write_tag(NXLOG_ERROR, DEBUG_TAG, _T("%hs %hs"), area, msg);
41 break;
42 case XMPP_LEVEL_WARN:
43 nxlog_write_tag(NXLOG_WARNING, DEBUG_TAG, _T("%hs %hs"), area, msg);
44 break;
45 case XMPP_LEVEL_INFO:
46 nxlog_write_tag(NXLOG_INFO, DEBUG_TAG, _T("%hs %hs"), area, msg);
47 break;
48 default:
49 nxlog_debug_tag(DEBUG_TAG, 6, _T("%hs"), msg);
50 break;
51 }
52 }
53
54 /**
55 * Logger definition
56 */
57 static const xmpp_log_t s_logger = { Logger, NULL };
58
59 /**
60 * Version request handler
61 */
62 static int VersionHandler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
63 {
64 xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
65 nxlog_debug_tag(DEBUG_TAG, 5, _T("Received version request from %hs"), xmpp_stanza_get_attribute(stanza, "from"));
66
67 xmpp_stanza_t *reply = xmpp_stanza_new(ctx);
68 xmpp_stanza_set_name(reply, "iq");
69 xmpp_stanza_set_type(reply, "result");
70 xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza));
71 xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from"));
72
73 xmpp_stanza_t *query = xmpp_stanza_new(ctx);
74 xmpp_stanza_set_name(query, "query");
75 const char *ns = xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza));
76 if (ns != NULL)
77 {
78 xmpp_stanza_set_ns(query, ns);
79 }
80
81 xmpp_stanza_t *name = xmpp_stanza_new(ctx);
82 xmpp_stanza_set_name(name, "name");
83 xmpp_stanza_add_child_ex(query, name, FALSE);
84
85 xmpp_stanza_t *text = xmpp_stanza_new(ctx);
86 xmpp_stanza_set_text(text, "NetXMS Server");
87 xmpp_stanza_add_child_ex(name, text, FALSE);
88
89 xmpp_stanza_t *version = xmpp_stanza_new(ctx);
90 xmpp_stanza_set_name(version, "version");
91 xmpp_stanza_add_child_ex(query, version, FALSE);
92
93 text = xmpp_stanza_new(ctx);
94 xmpp_stanza_set_text(text, NETXMS_VERSION_STRING_A);
95 xmpp_stanza_add_child_ex(version, text, FALSE);
96
97 xmpp_stanza_add_child_ex(reply, query, FALSE);
98
99 xmpp_send(conn, reply);
100 xmpp_stanza_release(reply);
101 return 1;
102 }
103
104 /**
105 * Presence handler
106 */
107 static int PresenceHandler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
108 {
109 xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
110
111 const char *type = xmpp_stanza_get_attribute(stanza, "type");
112 if ((type == NULL) || strcmp(type, "subscribe"))
113 return 1;
114
115 const char *requestor = xmpp_stanza_get_attribute(stanza, "from");
116 nxlog_debug_tag(DEBUG_TAG, 4, _T("Presence subscribe request from %hs"), requestor);
117
118 xmpp_stanza_t *reply = xmpp_stanza_new(ctx);
119 xmpp_stanza_set_name(reply, "presence");
120 xmpp_stanza_set_attribute(reply, "to", requestor);
121 if (AuthenticateUserForXMPPSubscription(requestor))
122 {
123 xmpp_stanza_set_attribute(reply, "type", "subscribed");
124 }
125 else
126 {
127 xmpp_stanza_set_attribute(reply, "type", "unsubscribed");
128 }
129
130 xmpp_send(conn, reply);
131 xmpp_stanza_release(reply);
132 return 1;
133 }
134
135 /**
136 * Incoming message handler
137 */
138 static int MessageHandler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
139 {
140 xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
141
142 if (!xmpp_stanza_get_child_by_name(stanza, "body"))
143 return 1;
144 const char *type = xmpp_stanza_get_attribute(stanza, "type");
145 if ((type != NULL) && !strcmp(type, "error"))
146 return 1;
147
148 const char *requestor = xmpp_stanza_get_attribute(stanza, "from");
149 char *intext = xmpp_stanza_get_text(xmpp_stanza_get_child_by_name(stanza, "body"));
150 nxlog_debug_tag(DEBUG_TAG, 6, _T("Incoming message from %hs: %hs"), requestor, intext);
151
152 if (AuthenticateUserForXMPPCommands(requestor))
153 {
154 #ifdef UNICODE
155 WCHAR *cmd = WideStringFromUTF8String(intext);
156 #else
157 char *cmd = strdup(intext);
158 #endif
159 TCHAR *eol = _tcschr(cmd, _T('\n'));
160 if (eol != NULL)
161 *eol = 0;
162
163 StringBufferConsole console;
164 ProcessConsoleCommand(cmd, &console);
165 free(cmd);
166
167 if (!console.getOutput().isEmpty())
168 {
169 xmpp_stanza_t *reply = xmpp_stanza_new(ctx);
170 xmpp_stanza_set_name(reply, "message");
171 xmpp_stanza_set_type(reply, (xmpp_stanza_get_type(stanza) != NULL) ? xmpp_stanza_get_type(stanza) : "chat");
172 xmpp_stanza_set_attribute(reply, "to", requestor);
173
174 xmpp_stanza_t *body = xmpp_stanza_new(ctx);
175 xmpp_stanza_set_name(body, "body");
176
177 xmpp_stanza_t *text = xmpp_stanza_new(ctx);
178 char *response = console.getOutput().getUTF8String();
179 xmpp_stanza_set_text(text, response);
180 MemFree(response);
181 xmpp_stanza_add_child_ex(body, text, FALSE);
182 xmpp_stanza_add_child_ex(reply, body, FALSE);
183
184 xmpp_send(conn, reply);
185 xmpp_stanza_release(reply);
186 }
187 }
188 else
189 {
190 nxlog_debug_tag(DEBUG_TAG, 6, _T("%hs is not authorized for XMPP commands"), requestor);
191 }
192 xmpp_free(ctx, intext);
193 return 1;
194 }
195
196 /**
197 * Connection status
198 */
199 static bool s_xmppConnected = false;
200
201 /**
202 * Connection handler
203 */
204 static void ConnectionHandler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
205 const int error, xmpp_stream_error_t * const stream_error,
206 void * const userdata)
207 {
208 xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
209
210 if (status == XMPP_CONN_CONNECT)
211 {
212 nxlog_debug_tag(DEBUG_TAG, 3, _T("Connected to XMPP server"));
213
214 xmpp_handler_add(conn, VersionHandler, "jabber:iq:version", "iq", NULL, ctx);
215 xmpp_handler_add(conn, MessageHandler, NULL, "message", NULL, ctx);
216 xmpp_handler_add(conn, PresenceHandler, NULL, "presence", NULL, ctx);
217
218 // Send initial <presence/> so that we appear online to contacts
219 xmpp_stanza_t *presence = xmpp_stanza_new(ctx);
220 xmpp_stanza_set_name(presence, "presence");
221 xmpp_send(conn, presence);
222 xmpp_stanza_release(presence);
223
224 s_xmppConnected = true;
225 }
226 else
227 {
228 s_xmppConnected = false;
229 nxlog_debug_tag(DEBUG_TAG, 3, _T("Disconnected from XMPP server"));
230 xmpp_stop(ctx);
231 }
232 }
233
234 /**
235 * XMPP context
236 */
237 static xmpp_ctx_t *s_xmppContext = NULL;
238 static xmpp_conn_t *s_xmppConnection = NULL;
239 static MUTEX s_xmppMutex = MutexCreate();
240
241 /**
242 * XMPP thread
243 */
244 static THREAD_RESULT THREAD_CALL XMPPConnectionManager(void *arg)
245 {
246 xmpp_initialize();
247
248 s_xmppContext = xmpp_ctx_new(NULL, &s_logger);
249
250 TCHAR tmpLogin[64];
251 TCHAR tmpPassword[MAX_PASSWORD];
252
253 ConfigReadStr(_T("XMPPLogin"), tmpLogin, 64, _T("netxms@localhost"));
254 ConfigReadStr(_T("XMPPPassword"), tmpPassword, MAX_PASSWORD, _T("netxms"));
255 DecryptPassword(tmpLogin, tmpPassword, tmpPassword, MAX_PASSWORD);
256
257 char login[64], password[MAX_PASSWORD];
258 #ifdef UNICODE
259 ConfigReadStrA(_T("XMPPLogin"), login, 64, "netxms@localhost");
260 char *_tmpPassword = UTF8StringFromWideString(tmpPassword);
261 strlcpy(password, _tmpPassword, MAX_PASSWORD);
262 free(_tmpPassword);
263 #else
264 strlcpy(password, tmpPassword, MAX_PASSWORD);
265 strlcpy(login, tmpPassword, 64);
266 #endif // UNICODE
267 nxlog_debug_tag(DEBUG_TAG, 1, _T("XMPP connection manager started"));
268
269 char server[256];
270 ConfigReadStrA(_T("XMPPServer"), server, 256, "");
271 UINT16 port = static_cast<UINT16>(ConfigReadInt(_T("XMPPPort"), 5222));
272
273 // outer loop - try to reconnect after disconnect
274 do
275 {
276 MutexLock(s_xmppMutex);
277 s_xmppConnection = xmpp_conn_new(s_xmppContext);
278 xmpp_conn_set_jid(s_xmppConnection, login);
279 xmpp_conn_set_pass(s_xmppConnection, password);
280 xmpp_connect_client(s_xmppConnection, (server[0] != 0) ? server : NULL, (server[0] != 0) ? port : 0, ConnectionHandler, s_xmppContext);
281 MutexUnlock(s_xmppMutex);
282
283 xmpp_set_loop_status(s_xmppContext, XMPP_LOOP_RUNNING);
284 while(xmpp_get_loop_status(s_xmppContext) == XMPP_LOOP_RUNNING)
285 {
286 MutexLock(s_xmppMutex);
287 xmpp_run_once(s_xmppContext, 100);
288 MutexUnlock(s_xmppMutex);
289 }
290 MutexLock(s_xmppMutex);
291 xmpp_conn_release(s_xmppConnection);
292 s_xmppConnection = NULL;
293 MutexUnlock(s_xmppMutex);
294 } while(!SleepAndCheckForShutdown(30));
295
296 xmpp_ctx_free(s_xmppContext);
297 s_xmppContext = NULL;
298
299 xmpp_shutdown();
300 nxlog_debug_tag(DEBUG_TAG, 1, _T("XMPP connection manager stopped"));
301 return THREAD_OK;
302 }
303
304 /**
305 * Outgoing message queue
306 */
307 static Queue s_xmppMessageQueue;
308
309 /**
310 * Message to send
311 */
312 class XMPPMessage
313 {
314 private:
315 char *m_rcpt;
316 char *m_text;
317 time_t m_timestamp;
318
319 public:
320
321 XMPPMessage(const TCHAR *rcpt, const TCHAR *text)
322 {
323 #ifdef UNICODE
324 m_rcpt = UTF8StringFromWideString(rcpt);
325 m_text = UTF8StringFromWideString(text);
326 #else
327 m_rcpt = UTF8StringFromMBString(rcpt);
328 m_text = UTF8StringFromMBString(text);
329 #endif
330 m_timestamp = time(NULL);
331 }
332
333 ~XMPPMessage()
334 {
335 free(m_rcpt);
336 free(m_text);
337 }
338
339 const char *getRecipient() { return m_rcpt; }
340 const char *getText() { return m_text; }
341 time_t getAge() { return time(NULL) - m_timestamp; }
342 };
343
344 /**
345 * Message sender
346 */
347 static THREAD_RESULT THREAD_CALL XMPPMessageSender(void *arg)
348 {
349 nxlog_debug_tag(DEBUG_TAG, 1, _T("XMPP message sender started"));
350
351 while(true)
352 {
353 XMPPMessage *m = (XMPPMessage *)s_xmppMessageQueue.getOrBlock();
354 if (m == INVALID_POINTER_VALUE)
355 break;
356
357 MutexLock(s_xmppMutex);
358 if ((s_xmppContext == NULL) || (s_xmppConnection == NULL) || !s_xmppConnected)
359 {
360 MutexUnlock(s_xmppMutex);
361 if (m->getAge() < 3600)
362 {
363 s_xmppMessageQueue.insert(m);
364 nxlog_debug_tag(DEBUG_TAG, 6, _T("XMPPMessageSender: XMPP connection unavailable, will retry in 30 seconds"));
365 if (SleepAndCheckForShutdown(30)) // retry message sending in 30 seconds
366 break;
367 }
368 else
369 {
370 nxlog_debug_tag(DEBUG_TAG, 6, _T("XMPPMessageSender: XMPP connection unavailable, dropping undelivered message to %hs"), m->getRecipient());
371 delete m;
372 }
373 continue;
374 }
375
376 xmpp_stanza_t *msg = xmpp_stanza_new(s_xmppContext);
377 xmpp_stanza_set_name(msg, "message");
378 xmpp_stanza_set_type(msg, "chat");
379 xmpp_stanza_set_attribute(msg, "to", m->getRecipient());
380
381 xmpp_stanza_t *body = xmpp_stanza_new(s_xmppContext);
382 xmpp_stanza_set_name(body, "body");
383
384 xmpp_stanza_t *text = xmpp_stanza_new(s_xmppContext);
385 xmpp_stanza_set_text(text, m->getText());
386 xmpp_stanza_add_child_ex(body, text, FALSE);
387 xmpp_stanza_add_child_ex(msg, body, FALSE);
388
389 xmpp_send(s_xmppConnection, msg);
390 xmpp_stanza_release(msg);
391
392 MutexUnlock(s_xmppMutex);
393 delete m;
394 }
395
396 nxlog_debug_tag(DEBUG_TAG, 1, _T("XMPP message sender stopped"));
397 return THREAD_OK;
398 }
399
400 /**
401 * XMPP threads
402 */
403 static THREAD s_connManagerThread = INVALID_THREAD_HANDLE;
404 static THREAD s_msgSenderThread = INVALID_THREAD_HANDLE;
405
406 /**
407 * Start XMPP connector
408 */
409 void StartXMPPConnector()
410 {
411 s_connManagerThread = ThreadCreateEx(XMPPConnectionManager, 0, NULL);
412 s_msgSenderThread = ThreadCreateEx(XMPPMessageSender, 0, NULL);
413 }
414
415 /**
416 * Stop XMPP connector
417 */
418 void StopXMPPConnector()
419 {
420 XMPPMessage *m;
421 while((m = (XMPPMessage *)s_xmppMessageQueue.get()) != NULL)
422 delete m;
423 s_xmppMessageQueue.put(INVALID_POINTER_VALUE);
424
425 MutexLock(s_xmppMutex);
426 if (s_xmppContext != NULL)
427 {
428 if (s_xmppConnected)
429 xmpp_disconnect(s_xmppConnection);
430 else
431 xmpp_stop(s_xmppContext);
432 }
433 MutexUnlock(s_xmppMutex);
434
435 ThreadJoin(s_connManagerThread);
436 ThreadJoin(s_msgSenderThread);
437 }
438
439 /**
440 * Send message to XMPP recipient
441 */
442 void SendXMPPMessage(const TCHAR *rcpt, const TCHAR *text)
443 {
444 s_xmppMessageQueue.put(new XMPPMessage(rcpt, text));
445 }
446
447 #endif