There didn’t seem to be a basic gloox tutorial available, and the example on their home page is out of date, so I decided to write one. This tutorial is based on their example, but extended and updated to work with gloox version 1.0.
Since you are reading this, you probably already know, but gloox is a popular library for the Extensible Messaging and Presence Protocol (XMPP), formerly known as Jabber. In this basic tutorial I will demonstrate how to create an XMPP bot that connects to a server (using TLS), listens for messages, and answers them in an annoying way. If you just want the code, get bot.cpp from GitHub, compilation instructions are at the top of the file.
The first thing we need to do is set up a MessageHandler
. This will connect to the server, and handle incoming messages.
class Bot : public MessageHandler { public: Bot() { JID jid("bot@localhost"); client = new Client( jid, "botpwd" ); connListener = new ConnListener(); client->registerMessageHandler( this ); client->registerConnectionListener(connListener); client->connect(true); }
4: The ID of our user. In my example I use a local XMPP server, with a previously created user “bot”. (See the appendix for instructions on how to install an XMPP server.)
5: Here we create the client, with our ID and password.
6: We need a ConnectionListener
to handle connections, I will get back to that later.
7: The Client
needs a MessageHandler
to handle incoming messages. Since the Bot
itself inherits from MessageHandler
, we just pass this
.
8: The ConnectionListener
must also be registered with the Client
.
9: Finally we connect to the XMPP server. There are two ways to connect, blocking and non-blocking. In our example, we use blocking connections. If you go for non-blocking, you need to call Client::recv()
at regular intervals to receive data.
To handle messages, Bot
needs to implement a method handleMessage
:
virtual void handleMessage( const Message& stanza, MessageSession* session = 0 ) { cout << "Received message: " << stanza << endl; Message msg(stanza.subtype(), stanza.from(), "Tell me more about " + stanza.body() ); client->send( msg ); }
2: Print the received message (using a custom operator<<
).
3: Create a new message to whoever sent us a message, asking them to tell us more about this fascinating subject.
4: Send the message, using the Client
we created in the Bot
constructor.
We also need a ConnectionListener
to handle connections. As a minimum it needs to override three pure virtual methods:
class ConnListener : public ConnectionListener { public: virtual void onConnect() { cout << "ConnListener::onConnect()" << endl; } virtual void onDisconnect(ConnectionError e) { cout << "ConnListener::onDisconnect() " << e << endl; } virtual bool onTLSConnect(const CertInfo& info) { cout << "ConnListener::onTLSConnect()" << endl; return true; } };
Here I just cout
the names of the methods when they are called, to see that everything works. If there is an error, onDisconnect()
displays the error code. The error codes can be found in gloox.h
.
The main method to pay attention to is onTLSConnect()
. This should check the TLS cert credentials, and return true if they are accepted. In this tutorial we accept whatever we get.
Then you can run main
like this:
int main() { Bot b; }
You can now chat with the bot using any XMPP client:
(See the appendix for instructions on installing and setting up an XMPP client.)
That’s it! Get the full code from GitHub.
To compile, you need to install gloox and pthreads. On Ubuntu, gloox is installed doing sudo apt-get install libgloox-dev
, pthreads should come with your compiler. To compile using g++, do g++ -o bot bot.cpp -lgloox -lpthread
. Other Linuxes should be similar. On Windows, download and install from http://camaya.net/gloox/download, and read the included documentation.
If you enjoyed this post, you can subscribe to my blog, or follow me on Twitter.
Appendix A: How to install a local XMPP server
To test this, I installed ejabbered. If you are using Ubuntu, there is a simple, good tutorial available. As soon as ejabberd is installed, visit http://<servername>:5280/admin/server/localhost/users/ and add the user “bot”, with password “botpwd”. Also add another user to play the other part of the conversation (I used “anders”).
Appendix B: How to install and test with an XMPP client
There are many XMPP clients available, I used Pidgin (sudo apt-get install pidgin
on Ubuntu). After installing, open the main window (“Buddy List”) and select Accounts -> Manage Accounts -> Add. Select protocol “XMPP”, the other username you created on your XMPP server (not “bot”) and a domain (“localhost” if you are running on localhost). On the Advanced page, select port “5222”, server “localhost” (or whatever host you are running on). Click “Add”, and tick the “Enabled”-box in the list of accounts. Hit “Close”. Now go to the Buddy List again, select “Buddies”, “Add Buddy”. In “Buddy’s username”, enter “bot@localhost” (or whatever host you are running on).
If you have started your bot (./bot
on the command line), you can now double-click on the bot in you Buddy List as you would normally do to chat with someone in any IM program, and type your message. If everything went according to plan, the bot will be very interested, and want to know more!
could you do a tutorial using file transfer?
thanks.
To be honest, I don’t think that will happen. I am no expert on Gloox, and have never used file transfers. I just wrote up this tutorial to sum up what I learned when I tried to figure out how to use it. Sorry!
If I someday get around to using file transfers though, I’ll make sure to write it up here.
can you write how to make it connect to a custom legacy jabberd server on port 5223?
I am sorry, I don’t use Gloox any more, and never really used it much more than what I described in this tutorial.
Did you have a look at the documentation though? At http://camaya.net/api/gloox-1.0/index.html#client_sec it says “gloox does not officially support the style of connection which is usually used on port 5223, i.e. SSL encryption before any XML is sent, because it’s a legacy method and not standard XMPP. However, gloox includes a ConnectionTLS class that, as a side-effect, allows you to establish such connections.”
i tried it and it dont even compile.
$ g++ -Wall -o main main.cpp -lgloox -lpthread
main.cpp: In member function ‘void MessageTest::start()’:
main.cpp:51:37: error: expected type-specifier before ‘ConnectionTCP’
main.cpp:51:37: error: expected ‘)’ before ‘ConnectionTCP’
main.cpp:52:49: error: no matching function for call to ‘gloox::ConnectionTLS::ConnectionTLS(gloox::Client*&, int*, gloox::LogSink&)’
main.cpp:52:49: note: candidates are:
In file included from main.cpp:18:0:
/usr/local/include/gloox/connectiontls.h:71:7: note: gloox::ConnectionTLS::ConnectionTLS(gloox::ConnectionBase*, const gloox::LogSink&)
/usr/local/include/gloox/connectiontls.h:71:7: note: candidate expects 2 arguments, 3 provided
/usr/local/include/gloox/connectiontls.h:62:7: note: gloox::ConnectionTLS::ConnectionTLS(gloox::ConnectionDataHandler*, gloox::ConnectionBase*, const gloox::LogSink&)
/usr/local/include/gloox/connectiontls.h:62:7: note: no known conversion for argument 2 from ‘int*’ to ‘gloox::ConnectionBase*’
/usr/local/include/gloox/connectiontls.h:51:19: note: gloox::ConnectionTLS::ConnectionTLS(const gloox::ConnectionTLS&)
/usr/local/include/gloox/connectiontls.h:51:19: note: candidate expects 1 argument, 3 provided
main.cpp
————-
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace gloox;
#ifndef _WIN32
# include
#endif
#include
#include
#if defined( WIN32 ) || defined( _WIN32 )
# include
#endif
class MessageTest : public MessageSessionHandler, ConnectionListener, LogHandler,
MessageEventHandler, MessageHandler, ChatStateHandler
{
public:
MessageTest() : m_session( 0 ), m_messageEventFilter( 0 ), m_chatStateFilter( 0 ) {}
virtual ~MessageTest() {}
void start()
{
JID jid( “account@server/resource” );
j = new Client( jid, “password” );
j->registerConnectionListener( this );
j->registerMessageSessionHandler( this, 0 );
j->setConnectionImpl( new ConnectionTLS( j,
new ConnectionTCP( j->logInstance(), “www.example.com”, 5223 ),
j->logInstance()) );
j->disco()->setVersion( “messageTest”, GLOOX_VERSION, “Linux” );
j->disco()->setIdentity( “client”, “bot” );
j->disco()->addFeature( XMLNS_CHAT_STATES );
//StringList ca;
//ca.push_back( “test.crt” );
//j->setCACerts( ca );
j->logInstance().registerLogHandler( LogLevelDebug, LogAreaAll, this );
if( j->connect( false ) )
{
ConnectionError ce = ConnNoError;
while( ce == ConnNoError )
{
ce = j->recv();
}
printf( “ce: %d\n”, ce );
}
delete( j );
}
virtual void onConnect()
{
printf( “connected!!!\n” );
}
virtual void onDisconnect( ConnectionError e )
{
printf( “message_test: disconnected: %d\n”, e );
if( e == ConnAuthenticationFailed )
printf( “auth failed. reason: %d\n”, j->authError() );
}
virtual bool onTLSConnect( const CertInfo& info )
{
time_t from( info.date_from );
time_t to( info.date_to );
printf( “status: %d\nissuer: %s\npeer: %s\nprotocol: %s\nmac: %s\ncipher: %s\ncompression: %s\n”
“from: %s\nto: %s\n”,
info.status, info.issuer.c_str(), info.server.c_str(),
info.protocol.c_str(), info.mac.c_str(), info.cipher.c_str(),
info.compression.c_str(), ctime( &from ), ctime( &to ) );
return true;
}
virtual void handleMessage( const Message& msg, MessageSession * /*session*/ )
{
printf( “type: %d, subject: %s, message: %s, thread id: %s\n”, msg.subtype(),
msg.subject().c_str(), msg.body().c_str(), msg.thread().c_str() );
std::string re = “You said:\n> ” + msg.body() + “\nI like that statement.”;
std::string sub;
if( !msg.subject().empty() )
sub = “Re: ” + msg.subject();
m_messageEventFilter->raiseMessageEvent( MessageEventDisplayed );
#if defined( WIN32 ) || defined( _WIN32 )
Sleep( 1000 );
#else
sleep( 1 );
#endif
m_messageEventFilter->raiseMessageEvent( MessageEventComposing );
m_chatStateFilter->setChatState( ChatStateComposing );
#if defined( WIN32 ) || defined( _WIN32 )
Sleep( 2000 );
#else
sleep( 2 );
#endif
m_session->send( re, sub );
if( msg.body() == “quit” )
j->disconnect();
}
virtual void handleMessageEvent( const JID& from, MessageEventType event )
{
printf( “received event: %d from: %s\n”, event, from.full().c_str() );
}
virtual void handleChatState( const JID& from, ChatStateType state )
{
printf( “received state: %d from: %s\n”, state, from.full().c_str() );
}
virtual void handleMessageSession( MessageSession *session )
{
printf( “got new session\n”);
// this example can handle only one session. so we get rid of the old session
j->disposeMessageSession( m_session );
m_session = session;
m_session->registerMessageHandler( this );
m_messageEventFilter = new MessageEventFilter( m_session );
m_messageEventFilter->registerMessageEventHandler( this );
m_chatStateFilter = new ChatStateFilter( m_session );
m_chatStateFilter->registerChatStateHandler( this );
}
virtual void handleLog( LogLevel level, LogArea area, const std::string& message )
{
printf(“log: level: %d, area: %d, %s\n”, level, area, message.c_str() );
}
private:
Client *j;
MessageSession *m_session;
MessageEventFilter *m_messageEventFilter;
ChatStateFilter *m_chatStateFilter;
};
int main( int /*argc*/, char** /*argv*/ )
{
MessageTest *r = new MessageTest();
r->start();
delete( r );
return 0;
}
Is ConnectionTCP a class at all? I can’t see it in the docs. It seems you need to use ConnectionTCPClient or ConnectionTCPServer. http://camaya.net/api/gloox-1.0/classgloox_1_1ConnectionTCPBase.html
Note that I have never tried any of this, and am only guessing from the docs.
Hallo,
I get errors which say :
undefined reference to `gloox::Client::Client(gloox::JID const&, std::string const&, int)’
/home/Owner/Dokumente/projects/wa-replacement/test/gloox.h:70: undefined reference to `gloox::ClientBase::registerMessageHandler(gloox::MessageHandler*)’
/home/Owner/Dokumente/projects/wa-replacement/test/gloox.h:71: undefined reference to `gloox::ClientBase::registerConnectionListener(gloox::ConnectionListener*)’
/home/Owner/Dokumente/projects/wa-replacement/test/gloox.h:72: undefined reference to `gloox::ClientBase::connect(bool)’
I am using Code:blocks on Linux
greets
Hi, I haven’t touched gloox in over a year, I just played around with it for a few nights last year. I’m sorry I cannot help you with this one. Have you asked on the mailing list? http://camaya.net/gloox/list
Can you write how to compile this program on windows, using mingw?
/usr/bin/ld: cannot find -lgloox
I am new to gloox. Please help me solve this error.
Hi Rahul,
I’m sorry, but I’m no expert on Gloox. I used it a few nights many years ago, and have never used it since. I suggest you have a look at https://camaya.net/support/, after verifying that you followed all the instructions at https://camaya.net/gloox/download/.
How can I compile this on windows? i’m triying on dev-cpp
I don’t know, unfortunately. I haven’t used Gloox since I played around with it for a few days and wrote this post many years ago.
I have the same problem with gloox it cant be used in windows. For linux yes. But i couldnt in windows.