From eba947854b865798d395649c9dbe4797d82f73dd Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Sun, 1 May 2011 22:33:45 +0200 Subject: [PATCH] Unpimple sipjreen --- src/sip/jreen/CMakeLists.txt | 2 - src/sip/jreen/jabber.cpp | 621 ++++++++++++++++++++++++++++++----- src/sip/jreen/jabber.h | 57 +++- src/sip/jreen/jabber_p.cpp | 576 -------------------------------- src/sip/jreen/jabber_p.h | 114 ------- 5 files changed, 598 insertions(+), 772 deletions(-) delete mode 100644 src/sip/jreen/jabber_p.cpp delete mode 100644 src/sip/jreen/jabber_p.h diff --git a/src/sip/jreen/CMakeLists.txt b/src/sip/jreen/CMakeLists.txt index e0aa1b0d0..3cc053545 100644 --- a/src/sip/jreen/CMakeLists.txt +++ b/src/sip/jreen/CMakeLists.txt @@ -8,7 +8,6 @@ add_definitions( -DSIPDLLEXPORT_PRO ) set( jabberSources jabber.cpp - jabber_p.cpp tomahawksipmessage.cpp tomahawksipmessagefactory.cpp avatarmanager.cpp @@ -16,7 +15,6 @@ set( jabberSources set( jabberHeaders jabber.h - jabber_p.h tomahawksipmessage.h tomahawksipmessagefactory.h avatarmanager.h diff --git a/src/sip/jreen/jabber.cpp b/src/sip/jreen/jabber.cpp index e01c5d93e..ad279bf79 100644 --- a/src/sip/jreen/jabber.cpp +++ b/src/sip/jreen/jabber.cpp @@ -19,30 +19,94 @@ #include "jabber.h" +#include "config.h" + #include "tomahawksettings.h" +#include "tomahawksipmessage.h" +#include "tomahawksipmessagefactory.h" + +#include +#include +#include +#include + +#include +#include #include #include #include #include #include +#include +#include JabberPlugin::JabberPlugin() - : p( 0 ) - , m_menu( 0 ) + : m_menu( 0 ) , m_addFriendAction( 0 ) + , m_connected(false) { + qDebug() << Q_FUNC_INFO; + + qsrand(QDateTime::currentDateTime().toTime_t()); + + // load settings + m_currentUsername = TomahawkSettings::instance()->jabberUsername(); + m_currentServer = TomahawkSettings::instance()->jabberServer(); + m_currentPassword = TomahawkSettings::instance()->jabberPassword(); + m_currentPort = TomahawkSettings::instance()->jabberPort(); + + // setup JID object + Jreen::JID jid = Jreen::JID( accountName() ); + + // general client setup + m_client = new Jreen::Client( jid, m_currentPassword ); + m_client->registerStanzaExtension(new TomahawkSipMessageFactory); + m_currentResource = QString::fromAscii( "tomahawk%1" ).arg( QString::number( qrand() % 10000 ) ); + m_client->setResource( m_currentResource ); + + // add VCardUpdate extension to own presence + m_client->presence().addExtension( new Jreen::VCardUpdate() ); + + // initialize the AvatarManager + m_avatarManager = new AvatarManager( m_client ); + + // setup disco + m_client->disco()->setSoftwareVersion( "Tomahawk Player", TOMAHAWK_VERSION, CMAKE_SYSTEM ); + m_client->disco()->addIdentity( Jreen::Disco::Identity( "client", "type", "tomahawk", "en" ) ); + m_client->disco()->addFeature( TOMAHAWK_FEATURE ); + + // setup caps node, legacy peer detection - used before 0.1 + Jreen::Capabilities::Ptr caps = m_client->presence().findExtension(); + caps->setNode( TOMAHAWK_CAP_NODE_NAME ); + //FIXME: caps->setVersion( TOMAHAWK_VERSION ); + + // print connection parameters + qDebug() << "Our JID set to:" << m_client->jid().full(); + qDebug() << "Our Server set to:" << m_client->server(); + qDebug() << "Our Port set to" << m_client->port(); + + // setup slots + connect(m_client->connection(), SIGNAL(error(SocketError)), SLOT(onError(SocketError))); + connect(m_client, SIGNAL(serverFeaturesReceived(QSet)), SLOT(onConnect())); + connect(m_client, SIGNAL(disconnected(Jreen::Client::DisconnectReason)), SLOT(onDisconnect(Jreen::Client::DisconnectReason))); + connect(m_client, SIGNAL(destroyed(QObject*)), this, SLOT(onDestroy())); + connect(m_client, SIGNAL(newMessage(Jreen::Message)), SLOT(onNewMessage(Jreen::Message))); + connect(m_client, SIGNAL(newPresence(Jreen::Presence)), SLOT(onNewPresence(Jreen::Presence))); + connect(m_client, SIGNAL(newIQ(Jreen::IQ)), SLOT(onNewIq(Jreen::IQ))); + + connect(m_avatarManager, SIGNAL(newAvatar(QString)), SLOT(onNewAvatar(QString))); } JabberPlugin::~JabberPlugin() { - delete p; + } void JabberPlugin::setProxy( QNetworkProxy* proxy ) { - p->setProxy( proxy ); + qDebug() << Q_FUNC_INFO << "Not implemented"; } @@ -78,37 +142,13 @@ JabberPlugin::connectPlugin( bool startup ) if ( startup && !TomahawkSettings::instance()->jabberAutoConnect() ) return false; - QString jid = m_currentUsername = TomahawkSettings::instance()->jabberUsername(); - QString server = m_currentServer = TomahawkSettings::instance()->jabberServer(); - QString password = m_currentPassword = TomahawkSettings::instance()->jabberPassword(); - unsigned int port = m_currentPort = TomahawkSettings::instance()->jabberPort(); + qDebug() << "Connecting to the XMPP server..." << m_connected; + qDebug() << m_client->jid().full(); + //m_client->setServer( m_client->jid().domain() ); + qDebug() << m_client->server() << m_client->port(); - QStringList splitJid = jid.split( '@', QString::SkipEmptyParts ); - if ( splitJid.size() < 2 ) - { - qDebug() << "JID did not have an @ in it, could not find a server part"; - return false; - } - - if ( port < 1 || port > 65535 || jid.isEmpty() || password.isEmpty() ) - { - qDebug() << "Jabber credentials look wrong, not connecting"; - return false; - } - - delete p; - p = new Jabber_p( jid, password, ( server.isEmpty() ? QString() : server ), port ); - - QObject::connect( p, SIGNAL( peerOnline( QString ) ), SIGNAL( peerOnline( QString ) ) ); - QObject::connect( p, SIGNAL( peerOffline( QString ) ), SIGNAL( peerOffline( QString ) ) ); - QObject::connect( p, SIGNAL( msgReceived( QString, QString ) ), SIGNAL( msgReceived( QString, QString ) ) ); - - QObject::connect( p, SIGNAL( connected() ), SLOT( onConnected() ) ); - QObject::connect( p, SIGNAL( disconnected() ), SLOT( onDisconnected() ) ); - - QObject::connect( p, SIGNAL( authError( int, QString ) ), SLOT( onAuthError( int, QString ) ) ); - QObject::connect( p, SIGNAL( avatarReceived( QString, QPixmap ) ), SIGNAL( avatarReceived( QString, QPixmap ) ) ); - QObject::connect( p, SIGNAL( avatarReceived( QPixmap ) ), SIGNAL( avatarReceived( QPixmap) ) ); + QTimer::singleShot(1000, m_client, SLOT( connectToServer() ) ); + //m_client->connectToServer(); return true; } @@ -116,54 +156,130 @@ JabberPlugin::connectPlugin( bool startup ) void JabberPlugin::disconnectPlugin() { - onDisconnected(); + qDebug() << Q_FUNC_INFO << m_connected; - if ( p ) - p->disconnect(); + if(!m_connected) + return; - delete p; - p = 0; + foreach(const Jreen::JID &peer, m_peers.keys()) + { + handlePeerStatus(peer, Jreen::Presence::Unavailable); + } + + + //m_roster->deleteLater(); + //m_roster = 0; + //m_room->deleteLater(); + //m_room = 0; + + m_peers.clear(); + m_legacy_peers.clear(); + + m_client->disconnectFromServer(true); } void -JabberPlugin::onConnected() +JabberPlugin::onConnect() { - if( !m_menu ) { - m_menu = new QMenu( QString( "JREEN (" ).append( accountName() ).append(")" ) ); - m_addFriendAction = m_menu->addAction( "Add Friend..." ); - QAction *connectAction = m_menu->addAction( "Connect" ); + qDebug() << Q_FUNC_INFO; - connect( m_addFriendAction, SIGNAL(triggered() ), - this, SLOT( showAddFriendDialog() ) ); - connect( connectAction, SIGNAL( triggered() ), SLOT( connectPlugin() ) ); - - emit addMenu( m_menu ); + // update jid resource, servers like gtalk use resource binding and may + // have changed our requested /resource + if ( m_client->jid().resource() != m_currentResource ) + { + m_currentResource = m_client->jid().resource(); + emit jidChanged( m_client->jid().full() ); } emit connected(); + + qDebug() << "Connected as:" << m_client->jid().full(); + + // set presence to least valid value + m_client->setPresence(Jreen::Presence::XA, "Got Tomahawk? http://gettomahawk.com", -127); + + // set ping timeout to 15 secs (TODO: verify if this works) + m_client->setPingInterval(1000); + + // load roster + m_roster = new Jreen::SimpleRoster( m_client ); + m_roster->load(); + + //FIXME: this implementation is totally broken atm, so it's disabled to avoid harm :P + // join MUC with bare jid as nickname + //TODO: make the room a list of rooms and make that configurable + QString mucNickname = QString( "tomahawk@conference.qutim.org/" ).append( QString( m_client->jid().bare() ).replace( "@", "-" ) ); + //m_room = new Jreen::MUCRoom(m_client, Jreen::JID( mucNickname ) ); + //m_room->setHistorySeconds(0); + //m_room->join(); + + // treat muc participiants like contacts + //connect( m_room, SIGNAL( messageReceived( Jreen::Message, bool ) ), this, SLOT( onNewMessage( Jreen::Message ) ) ); + //connect( m_room, SIGNAL( presenceReceived( Jreen::Presence, const Jreen::MUCRoom::Participant* ) ), this, SLOT( onNewPresence( Jreen::Presence ) ) ); + + m_connected = true; } void -JabberPlugin::onDisconnected() +JabberPlugin::onDisconnect( Jreen::Client::DisconnectReason reason ) { - if( m_menu && m_addFriendAction ) { - emit removeMenu( m_menu ); + qDebug() << Q_FUNC_INFO; - delete m_menu; - m_menu = 0; - m_addFriendAction = 0; // deleted by menu + QString errorMessage; + bool reconnect = false; + int reconnectInSeconds = 0; + + switch( reason ) + { + case Jreen::Client::User: + errorMessage = "User Interaction"; + break; + case Jreen::Client::HostUnknown: + errorMessage = "Host is unknown"; + break; + case Jreen::Client::ItemNotFound: + errorMessage = "Item not found"; + break; + case Jreen::Client::AuthorizationError: + errorMessage = "Authorization Error"; + break; + case Jreen::Client::RemoteStreamError: + errorMessage = "Remote Stream Error"; + reconnect = true; + break; + case Jreen::Client::RemoteConnectionFailed: + errorMessage = "Remote Connection failed"; + break; + case Jreen::Client::InternalServerError: + errorMessage = "Internal Server Error"; + reconnect = true; + break; + case Jreen::Client::SystemShutdown: + errorMessage = "System shutdown"; + reconnect = true; + reconnectInSeconds = 60; + break; + case Jreen::Client::Conflict: + errorMessage = "Conflict"; + break; + + case Jreen::Client::Unknown: + errorMessage = "Unknown"; + break; + + default: + qDebug() << "Not all Client::DisconnectReasons checked"; + Q_ASSERT(false); + break; } - emit disconnected(); -} - -void -JabberPlugin::onAuthError( int code, const QString& msg ) -{ - switch( code ) + switch( reason ) { + case Jreen::Client::User: + break; + case Jreen::Client::AuthorizationError: - emit error( SipPlugin::AuthError, msg ); + emit error( SipPlugin::AuthError, errorMessage ); break; case Jreen::Client::HostUnknown: @@ -174,7 +290,7 @@ JabberPlugin::onAuthError( int code, const QString& msg ) case Jreen::Client::SystemShutdown: case Jreen::Client::Conflict: case Jreen::Client::Unknown: - emit error( SipPlugin::ConnectionError, msg ); + emit error( SipPlugin::ConnectionError, errorMessage ); break; default: @@ -182,27 +298,99 @@ JabberPlugin::onAuthError( int code, const QString& msg ) Q_ASSERT(false); break; } + + emit disconnected(); + removeMenuHelper(); + + if(reconnect) + QTimer::singleShot(reconnectInSeconds*1000, this, SLOT(connectPlugin())); + + m_connected = false; +} + +void +JabberPlugin::onAuthError( int code, const QString& msg ) +{ + } void JabberPlugin::sendMsg(const QString& to, const QString& msg) { - if ( p ) - p->sendMsg( to, msg ); + qDebug() << Q_FUNC_INFO << to << msg; + + if ( !m_client ) { + return; + } + + if( m_legacy_peers.contains( to ) ) + { + qDebug() << Q_FUNC_INFO << to << "Send legacy message" << msg; + Jreen::Message m( Jreen::Message::Chat, Jreen::JID(to), msg); + m_client->send( m ); + + return; + } + + + /******************************************************* + * Obsolete this by a SipMessage class + */ + QJson::Parser parser; + bool ok; + QVariant v = parser.parse( msg.toAscii(), &ok ); + if ( !ok || v.type() != QVariant::Map ) + { + qDebug() << "Invalid JSON in XMPP msg"; + return; + } + QVariantMap m = v.toMap(); + /*******************************************************/ + + TomahawkSipMessage *sipMessage; + if(m["visible"].toBool()) + { + sipMessage = new TomahawkSipMessage(m["ip"].toString(), + m["port"].toInt(), + m["uniqname"].toString(), + m["key"].toString(), + m["visible"].toBool() + ); + } + else + { + sipMessage = new TomahawkSipMessage(); + } + + + qDebug() << "Send sip messsage to " << to; + Jreen::IQ iq( Jreen::IQ::Set, to ); + iq.addExtension( sipMessage ); + + m_client->send( iq, this, SLOT( onNewIq( Jreen::IQ, int ) ), SipMessageSent ); } void JabberPlugin::broadcastMsg(const QString& msg) { - if ( p ) - p->broadcastMsg( msg ); + qDebug() << Q_FUNC_INFO; + + if ( !m_client ) + return; + + foreach( const Jreen::JID& jid, m_peers.keys() ) + { + sendMsg( jid.full(), msg ); + } } void JabberPlugin::addContact(const QString& jid, const QString& msg) { - if ( p ) - p->addContact( jid, msg ); + // Add contact to the Tomahawk group on the roster + m_roster->add( jid, jid, QStringList() << "Tomahawk" ); + + return; } void @@ -232,16 +420,299 @@ JabberPlugin::checkSettings() if ( m_currentPort != TomahawkSettings::instance()->jabberPort() ) reconnect = true; - m_currentUsername = TomahawkSettings::instance()->jabberUsername(); - m_currentPassword = TomahawkSettings::instance()->jabberPassword(); - m_currentServer = TomahawkSettings::instance()->jabberServer(); - m_currentPort = TomahawkSettings::instance()->jabberPort(); - if ( reconnect && ( p || TomahawkSettings::instance()->jabberAutoConnect() ) ) + if ( reconnect ) { + qDebug() << Q_FUNC_INFO << "Reconnecting jreen plugin..."; disconnectPlugin(); + + m_currentUsername = TomahawkSettings::instance()->jabberUsername(); + m_currentPassword = TomahawkSettings::instance()->jabberPassword(); + m_currentServer = TomahawkSettings::instance()->jabberServer(); + m_currentPort = TomahawkSettings::instance()->jabberPort(); + + Jreen::JID jid = Jreen::JID( accountName() ); + m_client->setJID( jid ); + m_client->setPassword( m_currentPassword ); + m_client->setServer( m_currentServer ); + m_client->setPort( m_currentPort ); + + qDebug() << Q_FUNC_INFO << "Updated settings"; connectPlugin( false ); } } +void JabberPlugin::addMenuHelper() +{ + if( !m_menu ) + { + m_menu = new QMenu( QString( "JREEN (" ).append( accountName() ).append(")" ) ); + m_addFriendAction = m_menu->addAction( "Add Friend..." ); + + connect( m_addFriendAction, SIGNAL( triggered() ), this, SLOT( showAddFriendDialog() ) ); + + emit addMenu( m_menu ); + } +} + +void JabberPlugin::removeMenuHelper() +{ + if( m_menu && m_addFriendAction ) + { + emit removeMenu( m_menu ); + + delete m_menu; + m_menu = 0; + m_addFriendAction = 0; // deleted by menu + } +} + +void JabberPlugin::onNewMessage(const Jreen::Message& message) +{ + QString from = message.from().full(); + QString msg = message.body(); + + if ( msg.isEmpty() ) + return; + + QJson::Parser parser; + bool ok; + QVariant v = parser.parse( msg.toAscii(), &ok ); + if ( !ok || v.type() != QVariant::Map ) + { + QString to = from; + QString response = QString( tr("I'm sorry -- I'm just an automatic presence used by Tomahawk Player" + " (http://gettomahawk.com). If you are getting this message, the person you" + " are trying to reach is probably not signed on, so please try again later!") ); + + // this is not a sip message, so we send it directly through the client + m_client->send( Jreen::Message ( Jreen::Message::Chat, Jreen::JID(to), response) ); + + return; + } + + qDebug() << Q_FUNC_INFO << "From:" << message.from().full() << ":" << message.body(); + emit msgReceived( from, msg ); +} + + +void JabberPlugin::onNewPresence( const Jreen::Presence& presence) +{ + Jreen::JID jid = presence.from(); + QString fulljid( jid.full() ); + + + qDebug() << Q_FUNC_INFO << "* New presence: " << fulljid << presence.subtype(); + + if( jid == m_client->jid() ) + return; + + if ( presence.error() ) { + //qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: no" << "presence error"; + return; + } + + // ignore anyone not Running tomahawk: + Jreen::Capabilities::Ptr caps = presence.findExtension(); + if ( caps && ( caps->node() == TOMAHAWK_CAP_NODE_NAME ) ) + { + // must be a jreen resource, implementation in gloox was broken + qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: yes" << "caps " << caps->node(); + handlePeerStatus( jid, presence.subtype() ); + } + else if( caps ) + { + qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: maybe" << "caps " << caps->node() + << "requesting disco.."; + + // request disco features + QString node = caps->node() + '#' + caps->ver(); + + Jreen::IQ iq( Jreen::IQ::Get, jid ); + iq.addExtension( new Jreen::Disco::Info( node ) ); + + m_client->send( iq, this, SLOT( onNewIq( Jreen::IQ, int ) ), RequestDisco ); + } + else if( !caps ) + { + qDebug() << Q_FUNC_INFO << "Running tomahawk: no" << "no caps"; + } +} + +void JabberPlugin::onNewIq(const Jreen::IQ& iq, int context) +{ + if( context == RequestDisco ) + { + qDebug() << Q_FUNC_INFO << "Received disco IQ..."; + Jreen::Disco::Info *discoInfo = iq.findExtension().data(); + if(!discoInfo) + return; + iq.accept(); + + Jreen::JID jid = iq.from(); + + Jreen::DataForm::Ptr form = discoInfo->form(); + + if(discoInfo->features().contains( TOMAHAWK_FEATURE )) + { + qDebug() << Q_FUNC_INFO << jid.full() << "Running tomahawk/feature enabled: yes"; + + // the actual presence doesn't matter, it just needs to be "online" + handlePeerStatus( jid, Jreen::Presence::Available ); + } + else + { + qDebug() << Q_FUNC_INFO << jid.full() << "Running tomahawk/feature enabled: no"; + + //LEGACY: accept resources starting with tomahawk too + if( jid.resource().startsWith("tomahawk") ) + { + qDebug() << Q_FUNC_INFO << jid.full() << "Detected legacy tomahawk.."; + + // add to legacy peers, so we can send text messages instead of iqs + m_legacy_peers.append( jid ); + + handlePeerStatus( jid, Jreen::Presence::Available ); + } + } + } + else if(context == RequestedDisco) + { + qDebug() << "Sent IQ(Set), what should be happening here?"; + } + else if(context == SipMessageSent ) + { + qDebug() << "Sent SipMessage... what now?!"; + } + /*else if(context == RequestedVCard ) + { + qDebug() << "Requested VCard... what now?!"; + }*/ + else + { + + TomahawkSipMessage *sipMessage = iq.findExtension().data(); + if(sipMessage) + { + iq.accept(); + + qDebug() << Q_FUNC_INFO << "Got SipMessage ..."; + qDebug() << "ip" << sipMessage->ip(); + qDebug() << "port" << sipMessage->port(); + qDebug() << "uniqname" << sipMessage->uniqname(); + qDebug() << "key" << sipMessage->key(); + qDebug() << "visible" << sipMessage->visible(); + + + QVariantMap m; + if( sipMessage->visible() ) + { + m["visible"] = true; + m["ip"] = sipMessage->ip(); + m["port"] = sipMessage->port(); + m["key"] = sipMessage->key(); + m["uniqname"] = sipMessage->uniqname(); + } + else + { + m["visible"] = false; + } + + + QJson::Serializer ser; + QByteArray ba = ser.serialize( m ); + QString msg = QString::fromAscii( ba ); + + QString from = iq.from().full(); + qDebug() << Q_FUNC_INFO << "From:" << from << ":" << msg; + emit msgReceived( from, msg ); + } + } +} + +bool JabberPlugin::presenceMeansOnline(Jreen::Presence::Type p) +{ + switch(p) + { + case Jreen::Presence::Invalid: + case Jreen::Presence::Unavailable: + case Jreen::Presence::Error: + return false; + break; + default: + return true; + } +} + +void JabberPlugin::handlePeerStatus(const Jreen::JID& jid, Jreen::Presence::Type presenceType) +{ + QString fulljid = jid.full(); + + // "going offline" event + if ( !presenceMeansOnline( presenceType ) && + ( !m_peers.contains( jid ) || + presenceMeansOnline( m_peers.value( jid ) ) + ) + ) + { + m_peers[ jid ] = presenceType; + qDebug() << Q_FUNC_INFO << "* Peer goes offline:" << fulljid; + + // remove peer from legacy peers + if( m_legacy_peers.contains( jid ) ) + { + m_legacy_peers.removeAll( jid ); + } + + emit peerOffline( fulljid ); + return; + } + + // "coming online" event + if( presenceMeansOnline( presenceType ) && + ( !m_peers.contains( jid ) || + !presenceMeansOnline( m_peers.value( jid ) ) + ) + ) + { + m_peers[ jid ] = presenceType; + qDebug() << Q_FUNC_INFO << "* Peer goes online:" << fulljid; + + emit peerOnline( fulljid ); + + if(!m_avatarManager->avatar(jid.bare()).isNull()) + onNewAvatar( jid.bare() ); + + return; + } + + //qDebug() << "Updating presence data for" << fulljid; + m_peers[ jid ] = presenceType; +} + +void JabberPlugin::onNewAvatar(const QString& jid) +{ + qDebug() << Q_FUNC_INFO << jid; + Q_ASSERT(!m_avatarManager->avatar( jid ).isNull()); + + // find peers for the jid + QList peers = m_peers.keys(); + foreach(const Jreen::JID &peer, peers) + { + if( peer.bare() == jid ) + { + emit avatarReceived ( peer.full(), m_avatarManager->avatar( jid ) ); + } + } + + if( jid == m_client->jid().bare() ) + // own avatar + emit avatarReceived ( m_avatarManager->avatar( jid ) ); + else + // someone else's avatar + emit avatarReceived ( jid, m_avatarManager->avatar( jid ) ); +} + + + Q_EXPORT_PLUGIN2( sip, JabberPlugin ) diff --git a/src/sip/jreen/jabber.h b/src/sip/jreen/jabber.h index 75996381d..17d4cc31b 100644 --- a/src/sip/jreen/jabber.h +++ b/src/sip/jreen/jabber.h @@ -21,11 +21,29 @@ #define JABBER_H #include "sip/SipPlugin.h" -#include "jabber_p.h" -#include "../sipdllmacro.h" +#include "avatarmanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include #define MYNAME "SIPJREEN" +#define TOMAHAWK_FEATURE QLatin1String( "tomahawk:sip:v1" ) +#define TOMAHAWK_CAP_NODE_NAME QLatin1String( "http://tomahawk-player.org/" ) + +#include "../sipdllmacro.h" class SIPDLLEXPORT JabberPlugin : public SipPlugin { @@ -44,6 +62,8 @@ public: virtual QMenu* menu(); void setProxy( QNetworkProxy* proxy ); +signals: + void jidChanged( const QString& ); public slots: virtual bool connectPlugin( bool startup ); @@ -55,12 +75,28 @@ public slots: private slots: void showAddFriendDialog(); - void onConnected(); - void onDisconnected(); + void onConnect(); + void onDisconnect(Jreen::Client::DisconnectReason reason); void onAuthError(int code, const QString &msg); + void onNewPresence( const Jreen::Presence& presence ); + void onNewMessage( const Jreen::Message& message ); + void onError( const Jreen::Connection::SocketError& e ) + { + qDebug() << e; + } + void onNewIq( const Jreen::IQ &iq, int context = NoContext ); + void onNewAvatar( const QString &jid ); + private: - Jabber_p* p; + void addMenuHelper(); + void removeMenuHelper(); + + bool presenceMeansOnline( Jreen::Presence::Type p ); + void handlePeerStatus( const Jreen::JID &jid, Jreen::Presence::Type presenceType ); + + bool m_connected; + QMenu* m_menu; QAction* m_addFriendAction; @@ -68,6 +104,17 @@ private: QString m_currentPassword; QString m_currentServer; unsigned int m_currentPort; + QString m_currentResource; + + // sort out + Jreen::Client *m_client; + + Jreen::MUCRoom *m_room; + Jreen::SimpleRoster *m_roster; + QHash m_peers; + enum IqContext { NoContext, RequestDisco, RequestedDisco, SipMessageSent, RequestedVCard }; + QStringList m_legacy_peers; + AvatarManager *m_avatarManager; }; #endif diff --git a/src/sip/jreen/jabber_p.cpp b/src/sip/jreen/jabber_p.cpp deleted file mode 100644 index c7c9e02f9..000000000 --- a/src/sip/jreen/jabber_p.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Dominik Schmidt - * Copyright 2010-2011, Christian Muehlhaeuser - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#include "jabber_p.h" -#include "tomahawksipmessage.h" -#include "tomahawksipmessagefactory.h" - -#include "config.h" -#include "utils/tomahawkutils.h" - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//remove -#include -#include - -#define TOMAHAWK_FEATURE QLatin1String( "tomahawk:sip:v1" ) - -#define TOMAHAWK_CAP_NODE_NAME QLatin1String( "http://tomahawk-player.org/" ) - -Jabber_p::Jabber_p( const QString& jid, const QString& password, const QString& server, const int port ) - : QObject() - , m_server() -{ - qDebug() << Q_FUNC_INFO; - qsrand(QDateTime::currentDateTime().toTime_t()); - - // setup JID object - m_jid = Jreen::JID( jid ); - - // general client setup - m_client = new Jreen::Client( jid, password ); - if ( !server.isEmpty() ) - { - m_client->setServer( server ); - m_client->setPort( port ); - } - m_client->registerStanzaExtension(new TomahawkSipMessageFactory); - m_client->setResource( QString( "tomahawk%1" ).arg( QString::number( qrand() % 10000 ) ) ); - - // add VCardUpdate extension to own presence - m_client->presence().addExtension( new Jreen::VCardUpdate() ); - - // initialize the AvatarManager - m_avatarManager = new AvatarManager(m_client); - - // setup disco - m_client->disco()->setSoftwareVersion( "Tomahawk Player", TOMAHAWK_VERSION, CMAKE_SYSTEM ); - m_client->disco()->addIdentity( Jreen::Disco::Identity( "client", "type", "tomahawk", "en" ) ); - m_client->disco()->addFeature( TOMAHAWK_FEATURE ); - - // setup caps node, legacy peer detection - used before 0.1 - Jreen::Capabilities::Ptr caps = m_client->presence().findExtension(); - caps->setNode( TOMAHAWK_CAP_NODE_NAME ); - - // print connection parameters - qDebug() << "Our JID set to:" << m_client->jid().full(); - qDebug() << "Our Server set to:" << m_client->server(); - qDebug() << "Our Port set to" << m_client->port(); - - m_client->setConnectionImpl( new Jreen::TcpConnection( m_client->server(), m_client->port() ) ); - - // setup slots - connect(qobject_cast(m_client->connection()), SIGNAL(error(const Jreen::Connection::SocketError&)), SLOT(onError(const Jreen::Connection::SocketError&))); - connect(m_client, SIGNAL(serverFeaturesReceived(QSet)), SLOT(onConnect())); - connect(m_client, SIGNAL(disconnected(Jreen::Client::DisconnectReason)), SLOT(onDisconnect(Jreen::Client::DisconnectReason))); - connect(m_client, SIGNAL(destroyed(QObject*)), this, SLOT(onDestroy(QObject*))); - connect(m_client, SIGNAL(newMessage(Jreen::Message)), SLOT(onNewMessage(Jreen::Message))); - connect(m_client, SIGNAL(newPresence(Jreen::Presence)), SLOT(onNewPresence(Jreen::Presence))); - connect(m_client, SIGNAL(newIQ(Jreen::IQ)), SLOT(onNewIq(Jreen::IQ))); - - connect(m_avatarManager, SIGNAL(newAvatar(QString)), SLOT(onNewAvatar(QString))); - - - // connect - qDebug() << "Connecting to the XMPP server..."; - m_client->connectToServer(); -} - - -Jabber_p::~Jabber_p() -{ - delete m_client; -} - -void -Jabber_p::setProxy( QNetworkProxy* proxy ) -{ - qDebug() << Q_FUNC_INFO << "NOT IMPLEMENTED"; -} - -void -Jabber_p::disconnect() -{ - if ( m_client ) - { - m_client->disconnect(); - } -} - - -void -Jabber_p::sendMsg( const QString& to, const QString& msg ) -{ - qDebug() << Q_FUNC_INFO << to << msg; - - if ( !m_client ) { - return; - } - - if( m_legacy_peers.contains( to ) ) - { - qDebug() << Q_FUNC_INFO << to << "Send legacy message" << msg; - Jreen::Message m( Jreen::Message::Chat, Jreen::JID(to), msg); - m_client->send( m ); - - return; - } - - - /******************************************************* - * Obsolete this by a SipMessage class - */ - QJson::Parser parser; - bool ok; - QVariant v = parser.parse( msg.toAscii(), &ok ); - if ( !ok || v.type() != QVariant::Map ) - { - qDebug() << "Invalid JSON in XMPP msg"; - return; - } - QVariantMap m = v.toMap(); - /*******************************************************/ - - TomahawkSipMessage *sipMessage; - if(m["visible"].toBool()) - { - sipMessage = new TomahawkSipMessage(m["ip"].toString(), - m["port"].toInt(), - m["uniqname"].toString(), - m["key"].toString(), - m["visible"].toBool() - ); - } - else - { - sipMessage = new TomahawkSipMessage(); - } - - - qDebug() << "Send sip messsage to " << to; - Jreen::IQ iq( Jreen::IQ::Set, to ); - iq.addExtension( sipMessage ); - - m_client->send( iq, this, SLOT( onNewIq( Jreen::IQ, int ) ), SipMessageSent ); -} - - -void -Jabber_p::broadcastMsg( const QString &msg ) -{ - qDebug() << Q_FUNC_INFO; - - if ( !m_client ) - return; - - foreach( const QString& jidstr, m_peers.keys() ) - { - sendMsg( jidstr, msg ); - } -} - - -void -Jabber_p::addContact( const QString& jid, const QString& msg ) -{ - // Add contact to the Tomahawk group on the roster - m_roster->add( jid, jid, QStringList() << "Tomahawk" ); - - return; -} - -void -Jabber_p::onConnect() -{ - qDebug() << Q_FUNC_INFO; - - // update jid resource, servers like gtalk use resource binding and may - // have changed our requested /resource - if ( m_client->jid().resource() != m_jid.resource() ) - { - // TODO: check if this is still neccessary with jreen - m_jid.setResource( m_client->jid().resource() ); - QString jidstr( m_jid.full() ); - emit jidChanged( jidstr ); - } - - emit connected(); - qDebug() << "Connected as:" << m_jid.full(); - - // set presence to least valid value - m_client->setPresence(Jreen::Presence::XA, "Got Tomahawk? http://gettomahawk.com", -127); - - // set ping timeout to 15 secs (TODO: verify if this works) - m_client->setPingInterval(15000); - - // load roster - m_roster = new Jreen::SimpleRoster( m_client ); - m_roster->load(); - - //FIXME: this implementation is totally broken atm, so it's disabled to avoid harm :P - // join MUC with bare jid as nickname - //TODO: make the room a list of rooms and make that configurable - QString mucNickname = QString( "tomahawk@conference.qutim.org/" ).append( QString( m_jid.bare() ).replace( "@", "-" ) ); - m_room = new Jreen::MUCRoom(m_client, Jreen::JID( mucNickname ) ); - //m_room->setHistorySeconds(0); - //m_room->join(); - - // treat muc participiants like contacts - connect( m_room, SIGNAL( messageReceived( Jreen::Message, bool ) ), this, SLOT( onNewMessage( Jreen::Message ) ) ); - connect( m_room, SIGNAL( presenceReceived( Jreen::Presence, const Jreen::MUCRoom::Participant* ) ), this, SLOT( onNewPresence( Jreen::Presence ) ) ); -} - - -void -Jabber_p::onDisconnect( Jreen::Client::DisconnectReason reason ) -{ - QString error; - bool reconnect = false; - int reconnectInSeconds = 0; - - switch( reason ) - { - case Jreen::Client::User: - error = "User Interaction"; - break; - case Jreen::Client::HostUnknown: - error = "Host is unknown"; - break; - case Jreen::Client::ItemNotFound: - error = "Item not found"; - break; - case Jreen::Client::AuthorizationError: - error = "Authorization Error"; - break; - case Jreen::Client::RemoteStreamError: - error = "Remote Stream Error"; - reconnect = true; - break; - case Jreen::Client::RemoteConnectionFailed: - error = "Remote Connection failed"; - break; - case Jreen::Client::InternalServerError: - error = "Internal Server Error"; - reconnect = true; - break; - case Jreen::Client::SystemShutdown: - error = "System shutdown"; - reconnect = true; - reconnectInSeconds = 60; - break; - case Jreen::Client::Conflict: - error = "Conflict"; - break; - - case Jreen::Client::Unknown: - error = "Unknown"; - break; - - default: - qDebug() << "Not all Client::DisconnectReasons checked"; - Q_ASSERT(false); - break; - } - - qDebug() << "Disconnected from server:" << error; - if( reason != Jreen::Client::User ) - { - emit authError( reason, error ); - } - - if(reconnect) - QTimer::singleShot(reconnectInSeconds*1000, m_client, SLOT(connectToServer())); - - emit disconnected(); -} - -void -Jabber_p::onNewMessage( const Jreen::Message& m ) -{ - QString from = m.from().full(); - QString msg = m.body(); - - if ( msg.isEmpty() ) - return; - - QJson::Parser parser; - bool ok; - QVariant v = parser.parse( msg.toAscii(), &ok ); - if ( !ok || v.type() != QVariant::Map ) - { - if ( m.from().domain().contains( "googlemail." ) - || m.from().domain().contains( "gmail." ) - || m.from().domain().contains( "gtalk." ) - ) - return; - - QString to = from; - QString response = QString( tr("I'm sorry -- I'm just an automatic presence used by Tomahawk Player" - " (http://gettomahawk.com). If you are getting this message, the person you" - " are trying to reach is probably not signed on, so please try again later!") ); - - // this is not a sip message, so we send it directly through the client - m_client->send( Jreen::Message ( Jreen::Message::Chat, Jreen::JID(to), response) ); - - return; - } - - qDebug() << Q_FUNC_INFO << "From:" << m.from().full() << ":" << m.body(); - emit msgReceived( from, msg ); -} - - -void Jabber_p::onNewPresence( const Jreen::Presence& presence) -{ - Jreen::JID jid = presence.from(); - QString fulljid( jid.full() ); - - - qDebug() << Q_FUNC_INFO << "* New presence: " << fulljid << presence.subtype(); - - if( jid == m_jid ) - return; - - if ( presence.error() ) { - //qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: no" << "presence error"; - return; - } - - // ignore anyone not Running tomahawk: - Jreen::Capabilities::Ptr caps = presence.findExtension(); - if ( caps && ( caps->node() == TOMAHAWK_CAP_NODE_NAME ) ) - { - // must be a jreen resource, implementation in gloox was broken - qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: yes" << "caps " << caps->node(); - handlePeerStatus( fulljid, presence.subtype() ); - } - else if( caps ) - { - qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: maybe" << "caps " << caps->node() - << "requesting disco.."; - - // request disco features - QString node = caps->node() + '#' + caps->ver(); - - Jreen::IQ iq( Jreen::IQ::Get, jid ); - iq.addExtension( new Jreen::Disco::Info( node ) ); - - m_client->send( iq, this, SLOT( onNewIq( Jreen::IQ, int ) ), RequestDisco ); - } - else if( !caps ) - { - qDebug() << Q_FUNC_INFO << "Running tomahawk: no" << "no caps"; - } -} - -void -Jabber_p::onNewIq( const Jreen::IQ &iq, int context ) -{ - if( context == RequestDisco ) - { - qDebug() << Q_FUNC_INFO << "Received disco IQ..."; - Jreen::Disco::Info *discoInfo = iq.findExtension().data(); - if(!discoInfo) - return; - iq.accept(); - - QString fulljid = iq.from().full(); - Jreen::DataForm::Ptr form = discoInfo->form(); - - if(discoInfo->features().contains( TOMAHAWK_FEATURE )) - { - qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk/feature enabled: yes"; - - // the actual presence doesn't matter, it just needs to be "online" - handlePeerStatus( fulljid, Jreen::Presence::Available ); - } - else - { - qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk/feature enabled: no"; - - //LEGACY: accept resources starting with tomahawk too - if( iq.from().resource().startsWith("tomahawk") ) - { - qDebug() << Q_FUNC_INFO << fulljid << "Detected legacy tomahawk.."; - - // add to legacy peers, so we can send text messages instead of iqs - m_legacy_peers.append( fulljid ); - - handlePeerStatus( fulljid, Jreen::Presence::Available ); - } - } - } - else if(context == RequestedDisco) - { - qDebug() << "Sent IQ(Set), what should be happening here?"; - } - else if(context == SipMessageSent ) - { - qDebug() << "Sent SipMessage... what now?!"; - } - /*else if(context == RequestedVCard ) - { - qDebug() << "Requested VCard... what now?!"; - }*/ - else - { - - TomahawkSipMessage *sipMessage = iq.findExtension().data(); - if(sipMessage) - { - iq.accept(); - - qDebug() << Q_FUNC_INFO << "Got SipMessage ..."; - qDebug() << "ip" << sipMessage->ip(); - qDebug() << "port" << sipMessage->port(); - qDebug() << "uniqname" << sipMessage->uniqname(); - qDebug() << "key" << sipMessage->key(); - qDebug() << "visible" << sipMessage->visible(); - - - QVariantMap m; - if( sipMessage->visible() ) - { - m["visible"] = true; - m["ip"] = sipMessage->ip(); - m["port"] = sipMessage->port(); - m["key"] = sipMessage->key(); - m["uniqname"] = sipMessage->uniqname(); - } - else - { - m["visible"] = false; - } - - - QJson::Serializer ser; - QByteArray ba = ser.serialize( m ); - QString msg = QString::fromAscii( ba ); - - QString from = iq.from().full(); - qDebug() << Q_FUNC_INFO << "From:" << from << ":" << msg; - emit msgReceived( from, msg ); - } - } -} - -bool -Jabber_p::presenceMeansOnline( Jreen::Presence::Type p ) -{ - switch(p) - { - case Jreen::Presence::Invalid: - case Jreen::Presence::Unavailable: - case Jreen::Presence::Error: - return false; - break; - default: - return true; - } -} - -void -Jabber_p::handlePeerStatus( const Jreen::JID& jid, Jreen::Presence::Type presenceType ) -{ - QString fulljid = jid.full(); - - // "going offline" event - if ( !presenceMeansOnline( presenceType ) && - ( !m_peers.contains( fulljid ) || - presenceMeansOnline( m_peers.value( fulljid ) ) - ) - ) - { - m_peers[ fulljid ] = presenceType; - qDebug() << Q_FUNC_INFO << "* Peer goes offline:" << fulljid; - - // remove peer from legacy peers - if( m_legacy_peers.contains( fulljid ) ) - { - m_legacy_peers.removeAll( fulljid ); - } - - emit peerOffline( fulljid ); - return; - } - - // "coming online" event - if( presenceMeansOnline( presenceType ) && - ( !m_peers.contains( fulljid ) || - !presenceMeansOnline( m_peers.value( fulljid ) ) - ) - ) - { - m_peers[ fulljid ] = presenceType; - qDebug() << Q_FUNC_INFO << "* Peer goes online:" << fulljid; - - emit peerOnline( fulljid ); - - if(!m_avatarManager->avatar(jid.bare()).isNull()) - onNewAvatar( jid.bare() ); - - return; - } - - //qDebug() << "Updating presence data for" << fulljid; - m_peers[ fulljid ] = presenceType; -} - -void Jabber_p::onNewAvatar(const QString& jid) -{ - qDebug() << Q_FUNC_INFO << jid; - Q_ASSERT(!m_avatarManager->avatar( jid ).isNull()); - - // find peers for the jid - QStringList peers = m_peers.keys(); - foreach(const QString &peer, peers) - { - if( peer.startsWith(jid) ) - { - emit avatarReceived ( peer, m_avatarManager->avatar( jid ) ); - } - } - - if( jid == m_client->jid().bare() ) - // own avatar - emit avatarReceived ( m_avatarManager->avatar( jid ) ); - else - // someone else's avatar - emit avatarReceived ( jid, m_avatarManager->avatar( jid ) ); -} diff --git a/src/sip/jreen/jabber_p.h b/src/sip/jreen/jabber_p.h deleted file mode 100644 index c8c77a2ee..000000000 --- a/src/sip/jreen/jabber_p.h +++ /dev/null @@ -1,114 +0,0 @@ -/* === This file is part of Tomahawk Player - === - * - * Copyright 2010-2011, Dominik Schmidt - * Copyright 2010-2011, Christian Muehlhaeuser - * - * Tomahawk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Tomahawk 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Tomahawk. If not, see . - */ - -#ifndef JABBER_P_H -#define JABBER_P_H - -#include "../sipdllmacro.h" - -#include "avatarmanager.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -#if defined( WIN32 ) || defined( _WIN32 ) -#include -#endif - -class SIPDLLEXPORT Jabber_p : - public QObject -{ -Q_OBJECT - -public: - explicit Jabber_p( const QString& jid, const QString& password, const QString& server = "", const int port = -1 ); - virtual ~Jabber_p(); - - void setProxy( QNetworkProxy* proxy ); - -signals: - void msgReceived( const QString&, const QString& ); //from, msg - void peerOnline( const QString& ); - void peerOffline( const QString& ); - void connected(); - void disconnected(); - void jidChanged( const QString& ); - void avatarReceived( const QPixmap& avatar ); - void avatarReceived( const QString&, const QPixmap& avatar ); - void authError( int, const QString& ); - -public slots: - void sendMsg( const QString& to, const QString& msg ); - void broadcastMsg( const QString& msg ); - void addContact( const QString& jid, const QString& msg = QString() ); - void disconnect(); - - void onDisconnect(Jreen::Client::DisconnectReason reason); - void onConnect(); - -private slots: - virtual void onNewPresence( const Jreen::Presence& presence ); - virtual void onNewMessage( const Jreen::Message& msg ); - virtual void onError( const Jreen::Connection::SocketError& e ) - { - qDebug() << e; - } - virtual void onDestroy( QObject */*object*/ ) - { - qDebug() << Q_FUNC_INFO; - } - virtual void onNewIq( const Jreen::IQ &iq, int context = NoContext ); - virtual void onNewAvatar( const QString &jid ); - -private: - bool presenceMeansOnline( Jreen::Presence::Type p ); - void handlePeerStatus( const Jreen::JID &jid, Jreen::Presence::Type presenceType ); - - Jreen::Client *m_client; - Jreen::MUCRoom *m_room; - Jreen::SimpleRoster *m_roster; - Jreen::JID m_jid; - QMap m_presences; - QMap m_peers; - QString m_server; - - enum IqContext { NoContext, RequestDisco, RequestedDisco, SipMessageSent, RequestedVCard }; - - QStringList m_legacy_peers; - - AvatarManager *m_avatarManager; -}; - -#endif // JABBER_H