XMPP Hawkbit-Bot Programmierung mit gloox

26 October 2019

gloox ist eine CPP Bibliothek für XMPP. In diesem Artikel werden wir einen kleinen XMPP Bot in CPP schreiben.

Vorbereitung

gloox

Die offizielle Homepage von gloox ist https://camaya.net/gloox. Hier findet ihr auch die API https://camaya.net/api/gloox-1.0.22 zur aktuellen Version.

boost

Für ein paar Dinge werden wir die boost Bibliothek verwendet. https://www.boost.org/

Build System

Als Build System verwenden wir https://cmake.org/

Installation auf einem Debian System

aptitude install libgloox17 libgloox-dev libboost-dev libboost-doc cmake g++

Projektstruktur

mkdir src doc build test
touch bootstap.sh src/CMakeLists.txt src/main.cpp
chmod a+x bootstap.sh
$ tree
 tree
.
├── bootstap.sh
├── build
├── doc
├── src
│   ├── CMakeLists.txt
│   └── main.cpp
└── test

4 directories, 3 files

In die bootstap.sh tragen wir jetzt die Ausführung von cmake im Build Verzeichnis ein. Die Inhalt der bootstap.sh Datei sieht dann wie folgt aus

cd build
cmake ../src
cmake_minimum_required(VERSION 3.13)
project (hawkbit-bot)

file(GLOB SOURCES *.cpp)

find_package(Boost 1.67 COMPONENTS log program_options)
include(FindPkgConfig)
pkg_check_modules(Gloox  gloox>=1.0.22)
if(Boost_FOUND)
  include_directories(${Boost_INCLUDE_DIRS})
  SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
  add_executable(hawkbit-bot ${SOURCES})
  target_link_libraries(hawkbit-bot ${Boost_LIBRARIES} ${Gloox_LIBRARIES} )
endif()
#include <stdlib.h>
#include <iostream>

int main(int argc, char *argv[]) {
  std::cout << "Hello" << std::endl;
  return EXIT_SUCCESS;
}
./bootstap.sh
cd build
make
./xmpp-messenger-bot

Konfiguration

Es ist sicherlich sinnvoll, dass ein Bot eine Konfigurationsdatei hat. Hier können beispielsweise JID und Passwort sowie die Räume eingetragen werden.

[account]
jid = local@domain.tld
password = mypasswd
resource = hawkbit-bot
nick = Tux

[muc]
auto.join = muc1@conference.domain.tld
auto.join = muc2@conference.domain.tld
touch src/HawkbitBotBasic.hpp src/ConfigFileParser.hpp src/ConfigFileParser.cpp
#ifndef HAWKBIT_BOT_BASIC_H_
#define HAWKBIT_BOT_BASIC_H_

// Application parameters
#define HAWKBIT_BOT_NAME "Hawkbit-Bot"
#define HAWKBIT_BOT_VERSION "0.0.1-DEV"
#define HAWKBIT_BOT_HASH "$Id: $"

// Defaults
#define HAWKBIT_BOT_DEFAULT_CONFIG "hawkbit-bot.conf"

// Boots Logging

#define BOOST_LOG_DYN_LINK 1
#include <boost/log/trivial.hpp>

#endif // HAWKBIT_BOT_BASIC_H_
#ifndef HAWKBIT_BOT_CONFIG_FILE_PARSER_H_
#define HAWKBIT_BOT_CONFIG_FILE_PARSER_H_

#include <boost/program_options.hpp>
using namespace boost::program_options;

class ConfigFileParser {

public:
  ConfigFileParser(std::string file);
  variables_map getVariablesMap();
  options_description getDescription();

private:
  variables_map vm;
  options_description desc;
};

#endif // HAWKBIT_BOT_CONFIG_FILE_PARSER_H_
#include "ConfigFileParser.hpp"
#include "HawkbitBotBasic.hpp"
#include <fstream>

ConfigFileParser::ConfigFileParser(std::string file) {
  BOOST_LOG_TRIVIAL(info) << "Reading config file " << file;
  try {
    desc.add_options()("account.jid", value<std::string>()->required(), "JID")(
        "account.password", value<std::string>()->required(),
        "Password")("account.resource", value<std::string>(), "Resource")(
        "account.nick", value<std::string>(), "Nickname")(
        "muc.auto.join", value<std::vector<std::string>>()->composing(),
        "Auto Join MUCs");

    std::ifstream ifs(file.c_str());
    store(parse_config_file(ifs, desc), vm);
    notify(vm);
    ifs.close();

  } catch (const error &ex) {
    BOOST_LOG_TRIVIAL(error) << ex.what();
  }
}
variables_map ConfigFileParser::getVariablesMap() { return vm; }
#include "HawkbitBotBasic.hpp"
#include "ConfigFileParser.hpp"

#include <stdlib.h>
#include <iostream>
#include <boost/algorithm/string.hpp>

int main(int argc, char *argv[]) {
  BOOST_LOG_TRIVIAL(info) << "Starting " << HAWKBIT_BOT_NAME
                          << " Version: " HAWKBIT_BOT_VERSION << " ("
                          << HAWKBIT_BOT_HASH << ")";

  ConfigFileParser conf(HAWKBIT_BOT_DEFAULT_CONFIG);

  std::vector<std::string> strs;
  boost::split(strs, conf.getVariablesMap()["account.jid"].as<std::string>(),
               boost::is_any_of("@"));
  std::string user = strs.at(0);
  std::string host = strs.at(1);
  std::string resource =
      conf.getVariablesMap()["account.resource"].as<std::string>();
  std::string passwd =
      conf.getVariablesMap()["account.password"].as<std::string>();

  BOOST_LOG_TRIVIAL(info) << "Config for " << user << "@" << host << "/"
                          << resource;
  return EXIT_SUCCESS;
}
touch hawkbit-bot.conf
[account]
jid = local@domain.tld
password = mypasswd
resource = hawkbit-bot
nick = Tux

[muc]
auto.join = muc1@conference.domain.tld
auto.join = muc2@conference.domain.tld
$ ./hawkbit-bot
[2019-10-25 21:39:21.998635] [0x00007fc02f4c3980] [info]    Starting Hawkbit-Bot Version: 0.0.1-DEV ($Id$)
[2019-10-25 21:39:21.998664] [0x00007fc02f4c3980] [info]    Reading config file hawkbit-bot.conf
[2019-10-25 21:39:21.998819] [0x00007fc02f4c3980] [info]    Config for local@domain.tld/hawkbit-bot

XMPP Client

#ifndef XMPP_CLIENT_SESSION_H___
#define XMPP_CLIENT_SESSION_H___
#include "HawkbitBotBasic.hpp"

#include <gloox/chatstatehandler.h>
#include <gloox/client.h>
#include <gloox/connectionlistener.h>
#include <gloox/disco.h>
#include <gloox/message.h>
#include <gloox/messageeventhandler.h>
#include <gloox/messagehandler.h>
#include <gloox/messagesessionhandler.h>
#include <gloox/mucroom.h>
#include <gloox/rostermanager.h>

using namespace gloox;

class XmppClientSession : public MessageHandler,
                          ConnectionListener,
                          MessageEventHandler,
                          MessageSessionHandler,
                          MUCRoomHandler,
			  LogHandler {

public:
  bool setup(std::string user, std::string host, std::string resource,
             std::string password);
  void connect();
  void join(std::string mucroom);

  virtual void handleMessage(const Message &stanza, MessageSession *session);
  virtual void handleMessageEvent(const JID &from, MessageEventType event);
  virtual void handleMessageSession(MessageSession *session);
  virtual void onConnect();
  virtual void onDisconnect(ConnectionError e);
  virtual bool onTLSConnect(const CertInfo &info);
  virtual void onResourceBind(const std::string &resource);
  virtual void onResourceBindError(const Error *error);
  virtual void onSessionCreateError(const Error *error);

  virtual void
  handleMUCParticipantPresence(MUCRoom *room,
                               const MUCRoomParticipant participant,
                               const Presence &presence);
  virtual void handleMUCMessage(MUCRoom *room, const Message &msg, bool priv);

  virtual bool handleMUCRoomCreation(MUCRoom *room);

  virtual void handleMUCSubject(MUCRoom *room, const std::string &nick,
                                const std::string &subject);

  virtual void handleMUCInviteDecline(MUCRoom *room, const JID &invitee,
                                      const std::string &reason);

  virtual void handleMUCError(MUCRoom *room, StanzaError error);
  virtual void handleMUCInfo(MUCRoom *room, int features,
                             const std::string &name, const DataForm *infoForm);

  virtual void handleMUCItems(MUCRoom *room, const Disco::ItemList &items);
  virtual void handleLog( LogLevel level, LogArea area, const std::string& message );
private:
  Client *j;
};

#endif // XMPP_CLIENT_SESSION_H___
#include "XmppClientSession.hpp"

#include <gloox/client.h>
#include <gloox/connectionlistener.h>
#include <gloox/disco.h>
#include <gloox/message.h>
#include <gloox/messagehandler.h>
#include <gloox/messagesession.h>
#include <gloox/mucroom.h>
#include <gloox/rostermanager.h>

using namespace gloox;

bool XmppClientSession::setup(std::string user, std::string host,
                              std::string resource, std::string password) {
  std::string id(user + '@' + host + '/' + resource);
  JID jid(id);
  j = new Client(jid, password);
  j->disco()->setVersion(HAWKBIT_BOT_NAME, HAWKBIT_BOT_VERSION);
  j->disco()->setIdentity("client", HAWKBIT_BOT_NAME);
  j->registerConnectionListener(this);
  j->registerMessageHandler(this);
  j->logInstance().registerLogHandler(LogLevel::LogLevelDebug, LogArea::LogAreaAll, this);
  return true;
}

void XmppClientSession::connect() {
  BOOST_LOG_TRIVIAL(info) << "Connecting... ";
j->connect();
  BOOST_LOG_TRIVIAL(info) << "Connecting... done!";
}

void XmppClientSession::join(std::string mucroom) {
  JID roomJID(mucroom);
  MUCRoom *m_room = new MUCRoom(j, roomJID, this, 0);
  BOOST_LOG_TRIVIAL(info) << "Join room: " << mucroom;
  m_room->join();
}

void XmppClientSession::handleMessageSession(MessageSession *session) {
  BOOST_LOG_TRIVIAL(info) << "handleMessageSession";
}

void XmppClientSession::handleMessage(const Message &stanza,
                                      MessageSession *session = 0) {
  BOOST_LOG_TRIVIAL(info) << "Message: " << stanza.id() << " "
                          << stanza.xmlLang();
}

void XmppClientSession::handleMessageEvent(const JID &from,
                                           MessageEventType event) {
  BOOST_LOG_TRIVIAL(info) << "Message: ";
}

void XmppClientSession::onConnect() { BOOST_LOG_TRIVIAL(info) << "onConnect"; }

void XmppClientSession::onDisconnect(ConnectionError e) {
  BOOST_LOG_TRIVIAL(error) << "onDisconnect:" << e;
  BOOST_LOG_TRIVIAL(error) << this->j->streamError();
}

bool XmppClientSession::onTLSConnect(const CertInfo &info) {
  BOOST_LOG_TRIVIAL(info) << "onTLSConnect:";
 	BOOST_LOG_TRIVIAL(info) << "Cert Status: "  << info.status;
 	BOOST_LOG_TRIVIAL(info) << "Cert Chain: " <<  info.chain;
 	BOOST_LOG_TRIVIAL(info) << "Cert Issuer: " << info.issuer;
 	BOOST_LOG_TRIVIAL(info) << "Cert Server: " << info.server;
	time_t date_from_t = (time_t) info.date_from;
	time_t date_to_t = (time_t) info.date_to;
	BOOST_LOG_TRIVIAL(info) << "Cert from: " << ctime(&date_from_t);
	BOOST_LOG_TRIVIAL(info) << "Cert to: " << ctime(&date_to_t);
 	BOOST_LOG_TRIVIAL(info) << "Cert protocol: " << info.protocol;
 	BOOST_LOG_TRIVIAL(info) << "Cert cipher: " << info.cipher;
 	BOOST_LOG_TRIVIAL(info) << "Cert mac: " << info.mac;
 	BOOST_LOG_TRIVIAL(info) << "Cert compression: " << info.compression;

  return true;
}

void XmppClientSession::onResourceBind(const std::string &resource) {
  BOOST_LOG_TRIVIAL(info) << "onResourceBind:" << resource;
}

void XmppClientSession::onResourceBindError(const Error *error) {
  BOOST_LOG_TRIVIAL(error) << "SessionCreateError:" << error;
}
void XmppClientSession::onSessionCreateError(const Error *error) {
  BOOST_LOG_TRIVIAL(error) << "SessionCreateError:" << error;
}

void XmppClientSession::handleMUCParticipantPresence(
    MUCRoom *room, const MUCRoomParticipant participant,
    const Presence &presence) {
  BOOST_LOG_TRIVIAL(info) << "handleMUCParticipantPresence";
}

void XmppClientSession::handleMUCMessage(MUCRoom *room, const Message &msg,
                                         bool priv) {
  BOOST_LOG_TRIVIAL(info) << "handleMUCMessage: " << msg.body();
}

bool XmppClientSession::handleMUCRoomCreation(MUCRoom *room) {
  BOOST_LOG_TRIVIAL(info) << "handleMUCRoomCreation";
  return handleMUCRoomCreation(room);
}

void XmppClientSession::handleMUCSubject(MUCRoom *room, const std::string &nick,
                                         const std::string &subject) {
  BOOST_LOG_TRIVIAL(info) << "handleMUCSubject";
}

void XmppClientSession::handleMUCInviteDecline(MUCRoom *room,
                                               const JID &invitee,
                                               const std::string &reason) {
  BOOST_LOG_TRIVIAL(info) << "handleMUCInviteDecline";
}

void XmppClientSession::handleMUCError(MUCRoom *room, StanzaError error) {
  BOOST_LOG_TRIVIAL(info) << "handleMUCError";
}

void XmppClientSession::handleMUCInfo(MUCRoom *room, int features,
                                      const std::string &name,
                                      const DataForm *infoForm) {
  BOOST_LOG_TRIVIAL(info) << "handleMUCInfo";
}

void XmppClientSession::handleMUCItems(MUCRoom *room,
                                       const Disco::ItemList &items) {
  BOOST_LOG_TRIVIAL(info) << "handleMUCItems";
}

void XmppClientSession::handleLog( LogLevel level, LogArea area, const std::string& message ) {
	BOOST_LOG_TRIVIAL(info) << message;
}
void run_session(XmppClientSession *session) {
  BOOST_LOG_TRIVIAL(info) << "Starting Xmpp Client Session ...";
  session->connect();
}
  XmppClientSession *session = new XmppClientSession();
  session->setup(user, host, resource, passwd);

  std::thread session_thread(run_session, session);
  int wait_sec = 60;
  BOOST_LOG_TRIVIAL(info) << "Waiting " << wait_sec << " sec to connect befor join MUCs...";
  sleep(wait_sec);
  BOOST_LOG_TRIVIAL(info) << "Waiting " << wait_sec << " sec to join MUCs... done!";

  std::vector<std::string> auto_join_mucs =
      conf.getVariablesMap()["muc.auto.join"].as<std::vector<std::string>>();

  for (std::vector<std::string>::iterator it = auto_join_mucs.begin();
       it != auto_join_mucs.end(); ++it) {
    session->join(*it + "/" +
                  conf.getVariablesMap()["account.nick"].as<std::string>());
  }

  session_thread.join();

Profanity 0.7

19 October 2019

Ein ncurses basierter XMPP Client für fortgeschrittene Benutzer.

Profanity ist ein XMPP Client für die Konsole. Der Client ist sicherlich weniger für den normalen Anwender, aber Linux/Unix Fans die auf der Konsole unterwegs sind, wird der Client bestimmt gefallen.

Die offizielle Homepage vom Projekt ist https://profanity-im.github.io/. Der Client ist in C geschrieben und verwendet als XMPP lib libstrophe.

Der Client hat eine sehr gute integrierte Hilfe. Einfach mal /help eingeben. Sehr viele Informationen findet ihr auch auf der Homepage: https://profanity-im.github.io/guide/070/userguide.html

Die aktuelle Version 0.7 bietet jetzt neben OpenPGP auch OMEMO Verschlüsselung an. Die unterstützten XEPs findet ihr mit Release Informationen auf der Homepage: https://profanity-im.github.io/xeps.html

profanity001

profanity002

Willkommen auf XMPP-Messenger

19 October 2019

Das Protokoll XMPP und die XMPP Anwendungen unterstützen und Benutzer zu helfen.

Die Idee ist eine zentrale Stelle für XMPP relevante Informationen bereitzustellen, um die Verwendung, Entwicklung von XMPP aktiv im deutschsprachigen Raum zu unterstützen.


Older posts are available in the archive.