"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