123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600 |
- /*
- * Copyright (C) 2008-2012 The QXmpp developers
- *
- * Author:
- * Manjeet Dahiya
- *
- * Source:
- * http://code.google.com/p/qxmpp
- *
- * This file is a part of QXmpp library.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- */
- #include <QSslSocket>
- #include <QTimer>
- #include "QXmppClient.h"
- #include "QXmppClientExtension.h"
- #include "QXmppConstants.h"
- #include "QXmppLogger.h"
- #include "QXmppOutgoingClient.h"
- #include "QXmppMessage.h"
- #include "QXmppUtils.h"
- #include "QXmppRosterManager.h"
- #include "QXmppVCardManager.h"
- #include "QXmppVersionManager.h"
- #include "QXmppEntityTimeManager.h"
- #include "QXmppDiscoveryManager.h"
- #include "QXmppDiscoveryIq.h"
- class QXmppClientPrivate
- {
- public:
- QXmppClientPrivate(QXmppClient *qq);
- QXmppPresence clientPresence; ///< Current presence of the client
- QList<QXmppClientExtension*> extensions;
- QXmppLogger *logger;
- QXmppOutgoingClient *stream; ///< Pointer to the XMPP stream
- // reconnection
- bool receivedConflict;
- int reconnectionTries;
- QTimer *reconnectionTimer;
- // managers
- QXmppRosterManager *rosterManager;
- QXmppVCardManager *vCardManager;
- QXmppVersionManager *versionManager;
- void addProperCapability(QXmppPresence& presence);
- int getNextReconnectTime() const;
- private:
- QXmppClient *q;
- };
- QXmppClientPrivate::QXmppClientPrivate(QXmppClient *qq)
- : clientPresence(QXmppPresence::Available)
- , logger(0)
- , stream(0)
- , receivedConflict(false)
- , reconnectionTries(0)
- , reconnectionTimer(0)
- , rosterManager(0)
- , vCardManager(0)
- , versionManager(0)
- , q(qq)
- {
- }
- void QXmppClientPrivate::addProperCapability(QXmppPresence& presence)
- {
- QXmppDiscoveryManager* ext = q->findExtension<QXmppDiscoveryManager>();
- if(ext) {
- presence.setCapabilityHash("sha-1");
- presence.setCapabilityNode(ext->clientCapabilitiesNode());
- presence.setCapabilityVer(ext->capabilities().verificationString());
- }
- }
- int QXmppClientPrivate::getNextReconnectTime() const
- {
- if (reconnectionTries < 5)
- return 10 * 1000;
- else if (reconnectionTries < 10)
- return 20 * 1000;
- else if (reconnectionTries < 15)
- return 40 * 1000;
- else
- return 60 * 1000;
- }
- /// \mainpage
- ///
- /// QXmpp is a cross-platform C++ XMPP client library based on the Qt
- /// framework. It tries to use Qt's programming conventions in order to ease
- /// the learning curve for new programmers.
- ///
- /// QXmpp based clients are built using QXmppClient instances which handle the
- /// establishment of the XMPP connection and provide a number of high-level
- /// "managers" to perform specific tasks. You can write your own managers to
- /// extend QXmpp by subclassing QXmppClientExtension.
- ///
- /// <B>Main Class:</B>
- /// - QXmppClient
- ///
- /// <B>Managers to perform specific tasks:</B>
- /// - QXmppRosterManager
- /// - QXmppVCardManager
- /// - QXmppTransferManager
- /// - QXmppMucManager
- /// - QXmppCallManager
- /// - QXmppArchiveManager
- /// - QXmppVersionManager
- /// - QXmppDiscoveryManager
- /// - QXmppEntityTimeManager
- ///
- /// <B>XMPP stanzas:</B> If you are interested in a more low-level API, you can refer to these
- /// classes.
- /// - QXmppIq
- /// - QXmppMessage
- /// - QXmppPresence
- ///
- /// <BR><BR>
- /// <B>Project Details:</B>
- ///
- /// Project Page: http://code.google.com/p/qxmpp/
- /// <BR>
- /// Report Issues: http://code.google.com/p/qxmpp/issues/
- /// <BR>
- /// New Releases: http://code.google.com/p/qxmpp/downloads/
- ///
- /// Creates a QXmppClient object.
- /// \param parent is passed to the QObject's constructor.
- /// The default value is 0.
- QXmppClient::QXmppClient(QObject *parent)
- : QXmppLoggable(parent),
- d(new QXmppClientPrivate(this))
- {
- bool check;
- Q_UNUSED(check);
- d->stream = new QXmppOutgoingClient(this);
- d->addProperCapability(d->clientPresence);
- check = connect(d->stream, SIGNAL(elementReceived(QDomElement,bool&)),
- this, SLOT(_q_elementReceived(QDomElement,bool&)));
- Q_ASSERT(check);
- check = connect(d->stream, SIGNAL(messageReceived(QXmppMessage)),
- this, SIGNAL(messageReceived(QXmppMessage)));
- Q_ASSERT(check);
- check = connect(d->stream, SIGNAL(presenceReceived(QXmppPresence)),
- this, SIGNAL(presenceReceived(QXmppPresence)));
- Q_ASSERT(check);
- check = connect(d->stream, SIGNAL(iqReceived(QXmppIq)),
- this, SIGNAL(iqReceived(QXmppIq)));
- Q_ASSERT(check);
- check = connect(d->stream->socket(), SIGNAL(stateChanged(QAbstractSocket::SocketState)),
- this, SLOT(_q_socketStateChanged(QAbstractSocket::SocketState)));
- Q_ASSERT(check);
- check = connect(d->stream, SIGNAL(connected()),
- this, SLOT(_q_streamConnected()));
- Q_ASSERT(check);
- check = connect(d->stream, SIGNAL(disconnected()),
- this, SLOT(_q_streamDisconnected()));
- Q_ASSERT(check);
- check = connect(d->stream, SIGNAL(error(QXmppClient::Error)),
- this, SLOT(_q_streamError(QXmppClient::Error)));
- Q_ASSERT(check);
- // reconnection
- d->reconnectionTimer = new QTimer(this);
- d->reconnectionTimer->setSingleShot(true);
- connect(d->reconnectionTimer, SIGNAL(timeout()),
- this, SLOT(_q_reconnect()));
- Q_ASSERT(check);
- // logging
- setLogger(QXmppLogger::getLogger());
- // create managers
- // TODO move manager references to d->extensions
- d->rosterManager = new QXmppRosterManager(this);
- addExtension(d->rosterManager);
- d->vCardManager = new QXmppVCardManager;
- addExtension(d->vCardManager);
- d->versionManager = new QXmppVersionManager;
- addExtension(d->versionManager);
- addExtension(new QXmppEntityTimeManager());
- addExtension(new QXmppDiscoveryManager());
- }
- /// Destructor, destroys the QXmppClient object.
- ///
- QXmppClient::~QXmppClient()
- {
- delete d;
- }
- /// Registers a new extension with the client.
- ///
- /// \param extension
- bool QXmppClient::addExtension(QXmppClientExtension* extension)
- {
- if (d->extensions.contains(extension))
- {
- qWarning("Cannot add extension, it has already been added");
- return false;
- }
- extension->setParent(this);
- extension->setClient(this);
- d->extensions << extension;
- return true;
- }
- /// Unregisters the given extension from the client. If the extension
- /// is found, it will be destroyed.
- ///
- /// \param extension
- bool QXmppClient::removeExtension(QXmppClientExtension* extension)
- {
- if (d->extensions.contains(extension))
- {
- d->extensions.removeAll(extension);
- delete extension;
- return true;
- } else {
- qWarning("Cannot remove extension, it was never added");
- return false;
- }
- }
- /// Returns a list containing all the client's extensions.
- ///
- QList<QXmppClientExtension*> QXmppClient::extensions()
- {
- return d->extensions;
- }
- /// Returns a modifiable reference to the current configuration of QXmppClient.
- /// \return Reference to the QXmppClient's configuration for the connection.
- QXmppConfiguration& QXmppClient::configuration()
- {
- return d->stream->configuration();
- }
- /// Attempts to connect to the XMPP server. Server details and other configurations
- /// are specified using the config parameter. Use signals connected(), error(QXmppClient::Error)
- /// and disconnected() to know the status of the connection.
- /// \param config Specifies the configuration object for connecting the XMPP server.
- /// This contains the host name, user, password etc. See QXmppConfiguration for details.
- /// \param initialPresence The initial presence which will be set for this user
- /// after establishing the session. The default value is QXmppPresence::Available
- void QXmppClient::connectToServer(const QXmppConfiguration& config,
- const QXmppPresence& initialPresence)
- {
- d->stream->configuration() = config;
- d->clientPresence = initialPresence;
- d->addProperCapability(d->clientPresence);
- d->stream->connectToHost();
- }
- /// Overloaded function to simply connect to an XMPP server with a JID and password.
- ///
- /// \param jid JID for the account.
- /// \param password Password for the account.
- void QXmppClient::connectToServer(const QString &jid, const QString &password)
- {
- QXmppConfiguration config;
- config.setJid(jid);
- config.setPassword(password);
- connectToServer(config);
- }
- /// After successfully connecting to the server use this function to send
- /// stanzas to the server. This function can solely be used to send various kind
- /// of stanzas to the server. QXmppStanza is a parent class of all the stanzas
- /// QXmppMessage, QXmppPresence, QXmppIq, QXmppBind, QXmppRosterIq, QXmppSession
- /// and QXmppVCard.
- ///
- /// \return Returns true if the packet was sent, false otherwise.
- ///
- /// Following code snippet illustrates how to send a message using this function:
- /// \code
- /// QXmppMessage message(from, to, message);
- /// client.sendPacket(message);
- /// \endcode
- ///
- /// \param packet A valid XMPP stanza. It can be an iq, a message or a presence stanza.
- ///
- bool QXmppClient::sendPacket(const QXmppStanza& packet)
- {
- return d->stream->sendPacket(packet);
- }
- /// Disconnects the client and the current presence of client changes to
- /// QXmppPresence::Unavailable and status text changes to "Logged out".
- ///
- /// \note Make sure that the clientPresence is changed to
- /// QXmppPresence::Available, if you are again calling connectToServer() after
- /// calling the disconnectFromServer() function.
- ///
- void QXmppClient::disconnectFromServer()
- {
- // cancel reconnection
- d->reconnectionTimer->stop();
- d->clientPresence.setType(QXmppPresence::Unavailable);
- d->clientPresence.setStatusText("Logged out");
- if (d->stream->isConnected())
- sendPacket(d->clientPresence);
- d->stream->disconnectFromHost();
- }
- /// Returns true if the client has authenticated with the XMPP server.
- bool QXmppClient::isAuthenticated() const
- {
- return d->stream->isAuthenticated();
- }
- /// Returns true if the client is connected to the XMPP server.
- ///
- bool QXmppClient::isConnected() const
- {
- return d->stream->isConnected();
- }
- /// Returns the reference to QXmppRosterManager object of the client.
- /// \return Reference to the roster object of the connected client. Use this to
- /// get the list of friends in the roster and their presence information.
- ///
- QXmppRosterManager& QXmppClient::rosterManager()
- {
- return *d->rosterManager;
- }
- /// Utility function to send message to all the resources associated with the
- /// specified bareJid. If there are no resources available, that is the contact
- /// is offline or not present in the roster, it will still send a message to
- /// the bareJid.
- ///
- /// \param bareJid bareJid of the receiving entity
- /// \param message Message string to be sent.
- void QXmppClient::sendMessage(const QString& bareJid, const QString& message)
- {
- QStringList resources = rosterManager().getResources(bareJid);
- if(!resources.isEmpty())
- {
- for(int i = 0; i < resources.size(); ++i)
- {
- sendPacket(QXmppMessage("", bareJid + "/" + resources.at(i), message));
- }
- }
- else
- {
- sendPacket(QXmppMessage("", bareJid, message));
- }
- }
- /// Returns the client's current state.
- QXmppClient::State QXmppClient::state() const
- {
- if (d->stream->isConnected())
- return QXmppClient::ConnectedState;
- else if (d->stream->socket()->state() != QAbstractSocket::UnconnectedState &&
- d->stream->socket()->state() != QAbstractSocket::ClosingState)
- return QXmppClient::ConnectingState;
- else
- return QXmppClient::DisconnectedState;
- }
- /// Returns the client's current presence.
- ///
- QXmppPresence QXmppClient::clientPresence() const
- {
- return d->clientPresence;
- }
- /// Changes the presence of the connected client.
- ///
- /// The connection to the server will be updated accordingly:
- ///
- /// \li If the presence type is QXmppPresence::Unavailable, the connection
- /// to the server will be closed.
- ///
- /// \li Otherwise, the connection to the server will be established
- /// as needed.
- ///
- /// \param presence QXmppPresence object
- ///
- void QXmppClient::setClientPresence(const QXmppPresence& presence)
- {
- d->clientPresence = presence;
- d->addProperCapability(d->clientPresence);
- if (presence.type() == QXmppPresence::Unavailable)
- {
- // cancel reconnection
- d->reconnectionTimer->stop();
- // NOTE: we can't call disconnect() because it alters
- // the client presence
- if (d->stream->isConnected())
- sendPacket(d->clientPresence);
- d->stream->disconnectFromHost();
- }
- else if (d->stream->isConnected())
- sendPacket(d->clientPresence);
- else
- connectToServer(d->stream->configuration(), presence);
- }
- /// Returns the socket error if error() is QXmppClient::SocketError.
- ///
- QAbstractSocket::SocketError QXmppClient::socketError()
- {
- return d->stream->socket()->error();
- }
- /// Returns the XMPP stream error if QXmppClient::Error is QXmppClient::XmppStreamError.
- ///
- QXmppStanza::Error::Condition QXmppClient::xmppStreamError()
- {
- return d->stream->xmppStreamError();
- }
- /// Returns the reference to QXmppVCardManager, implementation of XEP-0054.
- /// http://xmpp.org/extensions/xep-0054.html
- ///
- QXmppVCardManager& QXmppClient::vCardManager()
- {
- return *d->vCardManager;
- }
- /// Returns the reference to QXmppVersionManager, implementation of XEP-0092.
- /// http://xmpp.org/extensions/xep-0092.html
- ///
- QXmppVersionManager& QXmppClient::versionManager()
- {
- return *d->versionManager;
- }
- /// Give extensions a chance to handle incoming stanzas.
- ///
- /// \param element
- /// \param handled
- void QXmppClient::_q_elementReceived(const QDomElement &element, bool &handled)
- {
- foreach (QXmppClientExtension *extension, d->extensions)
- {
- if (extension->handleStanza(element))
- {
- handled = true;
- return;
- }
- }
- }
- void QXmppClient::_q_reconnect()
- {
- if (d->stream->configuration().autoReconnectionEnabled()) {
- debug("Reconnecting to server");
- d->stream->connectToHost();
- }
- }
- void QXmppClient::_q_socketStateChanged(QAbstractSocket::SocketState socketState)
- {
- Q_UNUSED(socketState);
- emit stateChanged(state());
- }
- /// At connection establishment, send initial presence.
- void QXmppClient::_q_streamConnected()
- {
- d->receivedConflict = false;
- d->reconnectionTries = 0;
- // notify managers
- emit connected();
- emit stateChanged(QXmppClient::ConnectedState);
- // send initial presence
- if (d->stream->isAuthenticated())
- sendPacket(d->clientPresence);
- }
- void QXmppClient::_q_streamDisconnected()
- {
- // notify managers
- emit disconnected();
- emit stateChanged(QXmppClient::DisconnectedState);
- }
- void QXmppClient::_q_streamError(QXmppClient::Error err)
- {
- if (d->stream->configuration().autoReconnectionEnabled()) {
- if (err == QXmppClient::XmppStreamError) {
- // if we receive a resource conflict, inhibit reconnection
- if (d->stream->xmppStreamError() == QXmppStanza::Error::Conflict)
- d->receivedConflict = true;
- } else if (err == QXmppClient::SocketError && !d->receivedConflict) {
- // schedule reconnect
- d->reconnectionTimer->start(d->getNextReconnectTime());
- } else if (err == QXmppClient::KeepAliveError) {
- // if we got a keepalive error, reconnect in one second
- d->reconnectionTimer->start(1000);
- }
- }
- // notify managers
- emit error(err);
- }
- /// Returns the QXmppLogger associated with the current QXmppClient.
- QXmppLogger *QXmppClient::logger() const
- {
- return d->logger;
- }
- /// Sets the QXmppLogger associated with the current QXmppClient.
- void QXmppClient::setLogger(QXmppLogger *logger)
- {
- if (logger != d->logger) {
- if (d->logger) {
- disconnect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
- d->logger, SLOT(log(QXmppLogger::MessageType,QString)));
- disconnect(this, SIGNAL(setGauge(QString,double)),
- d->logger, SLOT(setGauge(QString,double)));
- disconnect(this, SIGNAL(updateCounter(QString,qint64)),
- d->logger, SLOT(updateCounter(QString,qint64)));
- }
- d->logger = logger;
- if (d->logger) {
- connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
- d->logger, SLOT(log(QXmppLogger::MessageType,QString)));
- connect(this, SIGNAL(setGauge(QString,double)),
- d->logger, SLOT(setGauge(QString,double)));
- connect(this, SIGNAL(updateCounter(QString,qint64)),
- d->logger, SLOT(updateCounter(QString,qint64)));
- }
- emit loggerChanged(d->logger);
- }
- }
|