diff --git a/src/sip/CMakeLists.txt b/src/sip/CMakeLists.txt index 039114f3c..82aa3ae97 100644 --- a/src/sip/CMakeLists.txt +++ b/src/sip/CMakeLists.txt @@ -1,7 +1,10 @@ - -IF(GLOOX_FOUND) +# only build one of them, if ENABLE_JREEN is true, GLOOX_FOUND is automatically set to "false" +IF( GLOOX_FOUND ) ADD_SUBDIRECTORY( jabber ) -ENDIF(GLOOX_FOUND) +ENDIF( GLOOX_FOUND ) +IF( ENABLE_JREEN ) + ADD_SUBDIRECTORY( jreen ) +ENDIF( ENABLE_JREEN) ADD_SUBDIRECTORY( twitter ) ADD_SUBDIRECTORY( zeroconf ) diff --git a/src/sip/jreen/CMakeLists.txt b/src/sip/jreen/CMakeLists.txt new file mode 100644 index 000000000..177bb6025 --- /dev/null +++ b/src/sip/jreen/CMakeLists.txt @@ -0,0 +1,46 @@ +project( tomahawk ) + +include( ${QT_USE_FILE} ) +add_definitions( ${QT_DEFINITIONS} ) +add_definitions( -DQT_PLUGIN ) +add_definitions( -DQT_SHARED ) +add_definitions( -DSIPDLLEXPORT_PRO ) + +set( jabberSources + jabber.cpp + jabber_p.cpp +) + +set( jabberHeaders + jabber.h + jabber_p.h +) + +include_directories( . ${CMAKE_CURRENT_BINARY_DIR} .. + ${QT_INCLUDE_DIR} + ${LIBJREEN_INCLUDE_DIR} +) + +qt4_wrap_cpp( jabberMoc ${jabberHeaders} ) +add_library( sip_jreen SHARED ${jabberSources} ${jabberMoc} ) + +IF( WIN32 ) +SET( OS_SPECIFIC_LINK_LIBRARIES + ${OS_SPECIFIC_LINK_LIBRARIES} + "secur32.dll" + "crypt32.dll" + ${TOMAHAWK_LIBRARIES} +) +ENDIF( WIN32 ) + +target_link_libraries( sip_jreen + ${QT_LIBRARIES} + ${LIBJREEN_LIBRARY} + ${OS_SPECIFIC_LINK_LIBRARIES} +) + +IF( APPLE ) +# SET( CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-undefined dynamic_lookup" ) +ENDIF( APPLE ) + +install( TARGETS sip_jreen DESTINATION lib ) diff --git a/src/sip/jreen/jabber.cpp b/src/sip/jreen/jabber.cpp new file mode 100644 index 000000000..6f02e7af2 --- /dev/null +++ b/src/sip/jreen/jabber.cpp @@ -0,0 +1,146 @@ +#include "jabber.h" + +#include "tomahawksettings.h" + +#include +#include +#include +#include +#include + +JabberPlugin::JabberPlugin() + : p( 0 ) +{ + m_menu = new QMenu(QString("JREEN (").append(accountName()).append(")")); + m_addFriendAction = m_menu->addAction("Add Friend..."); + QAction *connectAction = m_menu->addAction("Connect"); + + connect(m_addFriendAction, SIGNAL(triggered()), + this, SLOT(showAddFriendDialog())); + connect(connectAction, SIGNAL(triggered()), SLOT(connectPlugin())); +} + +JabberPlugin::~JabberPlugin() +{ + delete p; +} + +void +JabberPlugin::setProxy( QNetworkProxy* proxy ) +{ + p->setProxy( proxy ); +} + + +const QString +JabberPlugin::name() +{ + return QString( MYNAME ); +} + +const QString +JabberPlugin::friendlyName() +{ + return QString( "Jabber" ); +} + +const QString +JabberPlugin::accountName() +{ + return TomahawkSettings::instance()->jabberUsername(); +} + +QMenu* +JabberPlugin::menu() +{ + return m_menu; +} + +bool +JabberPlugin::connectPlugin( bool startup ) +{ + qDebug() << Q_FUNC_INFO; + + if ( startup && !TomahawkSettings::instance()->jabberAutoConnect() ) + return false; + + QString jid = TomahawkSettings::instance()->jabberUsername(); + QString server = TomahawkSettings::instance()->jabberServer(); + QString password = TomahawkSettings::instance()->jabberPassword(); + unsigned int port = TomahawkSettings::instance()->jabberPort(); + + 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 ( server.isEmpty() ) + server = splitJid[1]; + + 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, 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() ), SIGNAL( connected() ) ); + QObject::connect( p, SIGNAL( disconnected() ), SIGNAL( disconnected() ) ); + + return true; +} + +void +JabberPlugin::disconnectPlugin() +{ + if ( p ) + p->disconnect(); + + delete p; + p = 0; +} + +void +JabberPlugin::sendMsg(const QString& to, const QString& msg) +{ + if ( p ) + p->sendMsg( to, msg ); +} + +void +JabberPlugin::broadcastMsg(const QString& msg) +{ + if ( p ) + p->broadcastMsg( msg ); +} + +void +JabberPlugin::addContact(const QString& jid, const QString& msg) +{ + if ( p ) + p->addContact( jid, msg ); +} + +void +JabberPlugin::showAddFriendDialog() +{ + bool ok; + QString id = QInputDialog::getText( 0, tr( "Add Friend" ), + tr( "Enter Jabber ID:" ), QLineEdit::Normal, + "", &ok ); + if ( !ok ) + return; + + qDebug() << "Attempting to add jabber contact to roster:" << id; + addContact( id ); +} + +Q_EXPORT_PLUGIN2( sip, JabberPlugin ) diff --git a/src/sip/jreen/jabber.h b/src/sip/jreen/jabber.h new file mode 100644 index 000000000..6792a8e10 --- /dev/null +++ b/src/sip/jreen/jabber.h @@ -0,0 +1,46 @@ +#ifndef JABBER_H +#define JABBER_H + +#include "sip/SipPlugin.h" +#include "jabber_p.h" + +#include "../sipdllmacro.h" + +#define MYNAME "SIPJABBER" + +class SIPDLLEXPORT JabberPlugin : public SipPlugin +{ + Q_OBJECT + Q_INTERFACES( SipPlugin ) + +public: + JabberPlugin(); + virtual ~JabberPlugin(); + + //FIXME: Make this more correct + virtual bool isValid() { return true; } + virtual const QString name(); + virtual const QString friendlyName(); + virtual const QString accountName(); + virtual QMenu* menu(); + + void setProxy( QNetworkProxy* proxy ); + +public slots: + virtual bool connectPlugin( bool startup ); + void disconnectPlugin(); + void sendMsg( const QString& to, const QString& msg ); + void broadcastMsg( const QString &msg ); + void addContact( const QString &jid, const QString& msg = QString() ); + +private slots: + void onAuthError( int, const QString& ); + void showAddFriendDialog(); + +private: + Jabber_p* p; + QMenu* m_menu; + QAction* m_addFriendAction; +}; + +#endif diff --git a/src/sip/jreen/jabber_p.cpp b/src/sip/jreen/jabber_p.cpp new file mode 100644 index 000000000..3e809404a --- /dev/null +++ b/src/sip/jreen/jabber_p.cpp @@ -0,0 +1,320 @@ +#include "jabber_p.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +//remove +#include +#include + +using namespace std; + + +#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( QTime( 0, 0, 0 ).secsTo( QTime::currentTime() ) ); + qsrand(QDateTime::currentDateTime().toTime_t()); + + m_presences[jreen::Presence::Available] = "available"; + m_presences[jreen::Presence::Chat] = "chat"; + m_presences[jreen::Presence::Away] = "away"; + m_presences[jreen::Presence::DND] = "dnd"; + m_presences[jreen::Presence::XA] = "xa"; + m_presences[jreen::Presence::Unavailable] = "unavailable"; + m_presences[jreen::Presence::Probe] = "probe"; + m_presences[jreen::Presence::Error] = "error"; + m_presences[jreen::Presence::Invalid] = "invalid"; + + m_jid = jreen::JID( jid ); + + m_client = new jreen::Client( jid, password ); + m_client->setResource( QString( "tomahawk%1" ).arg( "DOMME" ) ); + + jreen::Capabilities::Ptr caps = m_client->presence().findExtension(); + caps->setNode(TOMAHAWK_CAP_NODE_NAME); + + 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(); + + 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))); + + 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; + if ( QThread::currentThread() != thread() ) + { + qDebug() << Q_FUNC_INFO << "invoking in correct thread, not" + << QThread::currentThread(); + + QMetaObject::invokeMethod( this, "sendMsg", + Qt::QueuedConnection, + Q_ARG( const QString, to ), + Q_ARG( const QString, msg ) + ); + return; + } + + if ( !m_client ) { + return; + } + + qDebug() << Q_FUNC_INFO << to << msg; + jreen::Message m( jreen::Message::Chat, jreen::JID(to), msg); + + m_client->send( m ); // assuming this is threadsafe +} + + +void +Jabber_p::broadcastMsg( const QString &msg ) +{ + qDebug() << Q_FUNC_INFO; + if ( QThread::currentThread() != thread() ) + { + QMetaObject::invokeMethod( this, "broadcastMsg", + Qt::QueuedConnection, + Q_ARG(const QString, msg) + ); + return; + } + + if ( !m_client ) + return; + + foreach( const QString& jidstr, m_peers.keys() ) + { + qDebug() << "Broadcasting to" << jidstr <<"..."; + jreen::Message m(jreen::Message::Chat, jreen::JID(jidstr), msg, ""); + m_client->send( m ); + } +} + + +void +Jabber_p::addContact( const QString& jid, const QString& msg ) +{ + if ( QThread::currentThread() != thread() ) + { + QMetaObject::invokeMethod( this, "addContact", + Qt::QueuedConnection, + Q_ARG(const QString, jid), + Q_ARG(const QString, msg) + ); + return; + } + + //FIXME: implement it when I dont suck that much :-) + //handleSubscription(Subscription()); + 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() ) + { + m_jid.setResource( m_client->jid().resource() ); + QString jidstr( m_jid.full() ); + emit jidChanged( jidstr ); + } + + emit connected(); + qDebug() << "Connected as:" << m_jid.full(); + + m_client->setPresence(jreen::Presence::Available, "Tomahawk-JREEN available", 1); + m_client->disco()->setSoftwareVersion( "Tomahawk JREEN", "0.0.0.0", "Foobar" ); + m_client->setPingInterval(60000); + jreen::AbstractRoster *roster = new jreen::AbstractRoster( m_client ); + roster->load(); +} + + +void +Jabber_p::onDisconnect( jreen::Client::DisconnectReason reason ) +{ + qDebug() << Q_FUNC_INFO << reason; + + QString error; + + switch( reason ) + { + case jreen::Client::User: + break; + case jreen::Client::HostUnknown: + break; + case jreen::Client::ItemNotFound: + break; + case jreen::Client::AuthorizationError: + break; + case jreen::Client::RemoteStreamError: + break; + case jreen::Client::RemoteConnectionFailed: + break; + case jreen::Client::InternalServerError: + break; + case jreen::Client::SystemShutdown: + break; + case jreen::Client::Conflict: + break; + + case jreen::Client::Unknown: + default: + break; + } + + qDebug() << "Connection error msg:" << error; + + emit disconnected(); +} + +void +Jabber_p::onNewMessage( const jreen::Message& m ) +{ + QString from = m.from().full(); + QString msg = m.body(); + + if ( msg.isEmpty() ) + return; + + qDebug() << Q_FUNC_INFO << 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 << "handle presence" << fulljid << presence.subtype(); + + if( jid == m_jid ) + return; + + if ( presence.error() ) { + qDebug() << Q_FUNC_INFO << "presence error: no tomahawk"; + return; + } + + // ignore anyone not running tomahawk: + jreen::Capabilities::Ptr caps = presence.findExtension(); + if ( caps && (caps->node() == TOMAHAWK_CAP_NODE_NAME )) + { + qDebug() << Q_FUNC_INFO << presence.from().full() << "tomahawk detected by caps"; + } + // this is a hack actually as long as gloox based libsip_jabber is around + // remove this as soon as everyone is using jreen + else if( presence.from().resource().startsWith( QLatin1String("tomahawk") ) ) + { + qDebug() << Q_FUNC_INFO << presence.from().full() << "tomahawk detected by resource"; + } + else if( caps && caps->node() != TOMAHAWK_CAP_NODE_NAME ) + { + qDebug() << Q_FUNC_INFO << presence.from().full() << "*no tomahawk* detected by caps!" << caps->node() << presence.from().resource(); + return; + } + else if( !caps ) + { + qDebug() << Q_FUNC_INFO << "no tomahawk detected by resource and !caps"; + return; + } + + qDebug() << Q_FUNC_INFO << fulljid << " is a tomahawk resource."; + + // "going offline" event + if ( !presenceMeansOnline( presence.subtype() ) && + ( !m_peers.contains( fulljid ) || + presenceMeansOnline( m_peers.value( fulljid ) ) + ) + ) + { + m_peers[ fulljid ] = presence.subtype(); + qDebug() << Q_FUNC_INFO << "* Peer goes offline:" << fulljid; + emit peerOffline( fulljid ); + return; + } + + // "coming online" event + if( presenceMeansOnline( presence.subtype() ) && + ( !m_peers.contains( fulljid ) || + !presenceMeansOnline( m_peers.value( fulljid ) ) + ) + ) + { + m_peers[ fulljid ] = presence.subtype(); + qDebug() << Q_FUNC_INFO << "* Peer goes online:" << fulljid; + emit peerOnline( fulljid ); + return; + } + + //qDebug() << "Updating presence data for" << fulljid; + m_peers[ fulljid ] = presence.subtype(); + +} + +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; + } +} diff --git a/src/sip/jreen/jabber_p.h b/src/sip/jreen/jabber_p.h new file mode 100644 index 000000000..44d45e57d --- /dev/null +++ b/src/sip/jreen/jabber_p.h @@ -0,0 +1,82 @@ +/* + This is the Jabber client that the rest of the app sees + Gloox stuff should NOT leak outside this class. + We may replace jreen later, this interface should remain the same. +*/ +#ifndef JABBER_P_H +#define JABBER_P_H + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined( WIN32 ) || defined( _WIN32 ) +# include +#endif + +#include "../sipdllmacro.h" +#include + +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 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(); + void onDestroy(); + +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; + } + +private: + bool presenceMeansOnline( jreen::Presence::Type p ); + jreen::Client *m_client; + jreen::JID m_jid; + QMap m_presences; + QMap m_peers; + QString m_server; +}; + +#endif // JABBER_H