e32ae68785f0572e6111b7d255437973c9c9be61
[public/netxms.git] / src / server / core / xmpp.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2013 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
25 #if XMPP_SUPPORTED
26
27 #include <strophe.h>
28
29 /**
30 * Logger
31 */
32 static void Logger(void * const userdata, const xmpp_log_level_t level, const char * const area, const char * const msg)
33 {
34 switch(level)
35 {
36 case XMPP_LEVEL_ERROR:
37 nxlog_write(MSG_XMPP_ERROR, NXLOG_ERROR, "mm", area, msg);
38 break;
39 case XMPP_LEVEL_WARN:
40 nxlog_write(MSG_XMPP_WARNING, NXLOG_WARNING, "mm", area, msg);
41 break;
42 case XMPP_LEVEL_INFO:
43 nxlog_write(MSG_XMPP_INFO, NXLOG_INFO, "mm", area, msg);
44 break;
45 default:
46 DbgPrintf(6, _T("XMPP: %hs"), msg);
47 break;
48 }
49 }
50
51 /**
52 * Logger definition
53 */
54 static const xmpp_log_t s_logger = { Logger, NULL };
55
56 /**
57 * Version request handler
58 */
59 static int VersionHandler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
60 {
61 xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
62 DbgPrintf(5, _T("XMPP: Received version request from %hs"), xmpp_stanza_get_attribute(stanza, "from"));
63
64 xmpp_stanza_t *reply = xmpp_stanza_new(ctx);
65 xmpp_stanza_set_name(reply, "iq");
66 xmpp_stanza_set_type(reply, "result");
67 xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza));
68 xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from"));
69
70 xmpp_stanza_t *query = xmpp_stanza_new(ctx);
71 xmpp_stanza_set_name(query, "query");
72 char *ns = xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza));
73 if (ns != NULL)
74 {
75 xmpp_stanza_set_ns(query, ns);
76 }
77
78 xmpp_stanza_t *name = xmpp_stanza_new(ctx);
79 xmpp_stanza_set_name(name, "name");
80 xmpp_stanza_add_child(query, name);
81
82 xmpp_stanza_t *text = xmpp_stanza_new(ctx);
83 xmpp_stanza_set_text(text, "NetXMS Server");
84 xmpp_stanza_add_child(name, text);
85
86 xmpp_stanza_t *version = xmpp_stanza_new(ctx);
87 xmpp_stanza_set_name(version, "version");
88 xmpp_stanza_add_child(query, version);
89
90 text = xmpp_stanza_new(ctx);
91 xmpp_stanza_set_text(text, NETXMS_VERSION_STRING_A);
92 xmpp_stanza_add_child(version, text);
93
94 xmpp_stanza_add_child(reply, query);
95
96 xmpp_send(conn, reply);
97 xmpp_stanza_release(reply);
98 return 1;
99 }
100
101 /**
102 * Presence handler
103 */
104 static int PresenceHandler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
105 {
106 xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
107
108 char *type = xmpp_stanza_get_attribute(stanza, "type");
109 if ((type == NULL) || strcmp(type, "subscribe"))
110 return 1;
111
112 char *requestor = xmpp_stanza_get_attribute(stanza, "from");
113 DbgPrintf(4, _T("XMPP: presence subscribe request from %hs"), requestor);
114
115 xmpp_stanza_t *reply = xmpp_stanza_new(ctx);
116 xmpp_stanza_set_name(reply, "presence");
117 xmpp_stanza_set_attribute(reply, "to", requestor);
118 if (AuthenticateUserForXMPPSubscription(requestor))
119 {
120 xmpp_stanza_set_attribute(reply, "type", "subscribed");
121 }
122 else
123 {
124 xmpp_stanza_set_attribute(reply, "type", "unsubscribed");
125 }
126
127 xmpp_send(conn, reply);
128 xmpp_stanza_release(reply);
129 return 1;
130 }
131
132 /**
133 * Incoming message handler
134 */
135 static int MessageHandler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata)
136 {
137 xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
138
139 if (!xmpp_stanza_get_child_by_name(stanza, "body"))
140 return 1;
141 char *type = xmpp_stanza_get_attribute(stanza, "type");
142 if ((type != NULL) && !strcmp(type, "error"))
143 return 1;
144
145 char *requestor = xmpp_stanza_get_attribute(stanza, "from");
146 char *intext = xmpp_stanza_get_text(xmpp_stanza_get_child_by_name(stanza, "body"));
147 DbgPrintf(6, _T("XMPP: Incoming message from %hs: %hs"), requestor, intext);
148
149 if (AuthenticateUserForXMPPCommands(requestor))
150 {
151 #ifdef UNICODE
152 WCHAR *cmd = WideStringFromUTF8String(intext);
153 #else
154 char *cmd = strdup(intext);
155 #endif
156 TCHAR *eol = _tcschr(cmd, _T('\n'));
157 if (eol != NULL)
158 *eol = 0;
159
160 struct __console_ctx console;
161 console.hSocket = -1;
162 console.socketMutex = MutexCreate();
163 console.pMsg = NULL;
164 console.session = NULL;
165 console.output = new String();
166 ProcessConsoleCommand(cmd, &console);
167 free(cmd);
168 MutexDestroy(console.socketMutex);
169
170 if (!console.output->isEmpty())
171 {
172 xmpp_stanza_t *reply = xmpp_stanza_new(ctx);
173 xmpp_stanza_set_name(reply, "message");
174 xmpp_stanza_set_type(reply, (xmpp_stanza_get_type(stanza) != NULL) ? xmpp_stanza_get_type(stanza) : "chat");
175 xmpp_stanza_set_attribute(reply, "to", requestor);
176
177 xmpp_stanza_t *body = xmpp_stanza_new(ctx);
178 xmpp_stanza_set_name(body, "body");
179
180 xmpp_stanza_t *text = xmpp_stanza_new(ctx);
181 char *response = console.output->getUTF8String();
182 xmpp_stanza_set_text(text, response);
183 free(response);
184 xmpp_stanza_add_child(body, text);
185 xmpp_stanza_add_child(reply, body);
186
187 xmpp_send(conn, reply);
188 xmpp_stanza_release(reply);
189 }
190 delete console.output;
191 }
192 else
193 {
194 DbgPrintf(6, _T("XMPP: %hs is not authorized for XMPP commands"), requestor);
195 }
196 return 1;
197 }
198
199 /**
200 * Connection status
201 */
202 static bool s_xmppConnected = false;
203
204 /**
205 * Connection handler
206 */
207 static void ConnectionHandler(xmpp_conn_t * const conn, const xmpp_conn_event_t status,
208 const int error, xmpp_stream_error_t * const stream_error,
209 void * const userdata)
210 {
211 xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata;
212
213 if (status == XMPP_CONN_CONNECT)
214 {
215 DbgPrintf(3, _T("XMPP: connected"));
216
217 xmpp_handler_add(conn, VersionHandler, "jabber:iq:version", "iq", NULL, ctx);
218 xmpp_handler_add(conn, MessageHandler, NULL, "message", NULL, ctx);
219 xmpp_handler_add(conn, PresenceHandler, NULL, "presence", NULL, ctx);
220
221 // Send initial <presence/> so that we appear online to contacts
222 xmpp_stanza_t *presence = xmpp_stanza_new(ctx);
223 xmpp_stanza_set_name(presence, "presence");
224 xmpp_send(conn, presence);
225 xmpp_stanza_release(presence);
226
227 s_xmppConnected = true;
228 }
229 else
230 {
231 s_xmppConnected = false;
232 DbgPrintf(3, _T("XMPP: disconnected"));
233 xmpp_stop(ctx);
234 }
235 }
236
237 /**
238 * XMPP context
239 */
240 xmpp_ctx_t *s_xmppContext = NULL;
241 xmpp_conn_t *s_xmppConnection = NULL;
242
243 /**
244 * XMPP thread
245 */
246 THREAD_RESULT THREAD_CALL XMPPConnectionManager(void *arg)
247 {
248 xmpp_initialize();
249
250 s_xmppContext = xmpp_ctx_new(NULL, &s_logger);
251 s_xmppConnection = xmpp_conn_new(s_xmppContext);
252
253 char login[64], password[64];
254 ConfigReadStrA(_T("XMPPLogin"), login, 64, "netxms@localhost");
255 ConfigReadStrA(_T("XMPPPassword"), password, 64, "netxms");
256 xmpp_conn_set_jid(s_xmppConnection, login);
257 xmpp_conn_set_pass(s_xmppConnection, password);
258 xmpp_connect_client(s_xmppConnection, NULL, 0, ConnectionHandler, s_xmppContext);
259
260 DbgPrintf(1, _T("XMPP connection manager started"));
261
262 // outer loop - try to reconnect after disconnect
263 do
264 {
265 // enter the event loop - connection handler will trigger an exit
266 xmpp_run(s_xmppContext);
267 } while(!SleepAndCheckForShutdown(30));
268
269 xmpp_conn_release(s_xmppConnection);
270 xmpp_ctx_free(s_xmppContext);
271 s_xmppContext = NULL;
272
273 xmpp_shutdown();
274 DbgPrintf(1, _T("XMPP connection manager stopped"));
275 return THREAD_OK;
276 }
277
278 /**
279 * Stop XMPP connector
280 */
281 void StopXMPPConnector()
282 {
283 if (s_xmppContext != NULL)
284 {
285 if (s_xmppConnected)
286 xmpp_disconnect(s_xmppConnection);
287 else
288 xmpp_stop(s_xmppContext);
289 }
290 }
291
292 /**
293 * Send message to XMPP recipient
294 */
295 bool SendXMPPMessage(const TCHAR *rcpt, const TCHAR *message)
296 {
297 if ((s_xmppContext == NULL) || (s_xmppConnection == NULL) || !s_xmppConnected)
298 return false;
299
300 #ifdef UNICODE
301 char *_rcpt = UTF8StringFromWideString(rcpt);
302 char *_message = UTF8StringFromWideString(message);
303 #else
304 const char *_rcpt = rcpt;
305 const char *_message = message;
306 #endif
307
308 xmpp_stanza_t *msg = xmpp_stanza_new(s_xmppContext);
309 xmpp_stanza_set_name(msg, "message");
310 xmpp_stanza_set_type(msg, "chat");
311 xmpp_stanza_set_attribute(msg, "to", _rcpt);
312
313 xmpp_stanza_t *body = xmpp_stanza_new(s_xmppContext);
314 xmpp_stanza_set_name(body, "body");
315
316 xmpp_stanza_t *text = xmpp_stanza_new(s_xmppContext);
317 xmpp_stanza_set_text(text, _message);
318 xmpp_stanza_add_child(body, text);
319 xmpp_stanza_add_child(msg, body);
320
321 xmpp_send(s_xmppConnection, msg);
322 xmpp_stanza_release(msg);
323
324 #ifdef UNICODE
325 free(_rcpt);
326 free(_message);
327 #endif
328
329 return true;
330 }
331
332 #endif