1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-03-22 16:59:58 +01:00

Unpimple sipjreen

This commit is contained in:
Dominik Schmidt 2011-05-01 22:33:45 +02:00
parent e863324c40
commit eba947854b
5 changed files with 598 additions and 772 deletions

View File

@ -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

View File

@ -19,30 +19,94 @@
#include "jabber.h"
#include "config.h"
#include "tomahawksettings.h"
#include "tomahawksipmessage.h"
#include "tomahawksipmessagefactory.h"
#include <jreen/jid.h>
#include <jreen/capabilities.h>
#include <jreen/vcardupdate.h>
#include <jreen/vcard.h>
#include <qjson/parser.h>
#include <qjson/serializer.h>
#include <QtPlugin>
#include <QStringList>
#include <QInputDialog>
#include <QLineEdit>
#include <QMessageBox>
#include <QDateTime>
#include <QTimer>
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<Jreen::Capabilities>();
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<QString>)), 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<Jreen::Capabilities>();
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<Jreen::Disco::Info>().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<TomahawkSipMessage>().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<Jreen::JID> 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 )

View File

@ -21,11 +21,29 @@
#define JABBER_H
#include "sip/SipPlugin.h"
#include "jabber_p.h"
#include "../sipdllmacro.h"
#include "avatarmanager.h"
#include <jreen/client.h>
#include <jreen/disco.h>
#include <jreen/message.h>
#include <jreen/messagesession.h>
#include <jreen/stanza.h>
#include <jreen/jreen.h>
#include <jreen/error.h>
#include <jreen/presence.h>
#include <jreen/vcard.h>
#include <jreen/abstractroster.h>
#include <jreen/connection.h>
#include <jreen/mucroom.h>
#include <QNetworkProxy>
#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<Jreen::JID, Jreen::Presence::Type> m_peers;
enum IqContext { NoContext, RequestDisco, RequestedDisco, SipMessageSent, RequestedVCard };
QStringList m_legacy_peers;
AvatarManager *m_avatarManager;
};
#endif

View File

@ -1,576 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Dominik Schmidt <dev@dominik-schmidt.de>
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "jabber_p.h"
#include "tomahawksipmessage.h"
#include "tomahawksipmessagefactory.h"
#include "config.h"
#include "utils/tomahawkutils.h"
#include <jreen/capabilities.h>
#include <jreen/tcpconnection.h>
#include <jreen/vcardupdate.h>
#include <jreen/vcard.h>
#include <qjson/parser.h>
#include <qjson/serializer.h>
#include <QDebug>
#include <QTime>
#include <QTimer>
#include <QString>
#include <QRegExp>
#include <QThread>
#include <QVariant>
#include <QMap>
#include <QCryptographicHash>
#include <QDir>
#include <QFile>
#include <QPixmap>
//remove
#include <QLabel>
#include <QtGui/QLabel>
#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<Jreen::Capabilities>();
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<Jreen::Connection*>(m_client->connection()), SIGNAL(error(const Jreen::Connection::SocketError&)), SLOT(onError(const Jreen::Connection::SocketError&)));
connect(m_client, SIGNAL(serverFeaturesReceived(QSet<QString>)), 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<Jreen::Capabilities>();
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<Jreen::Disco::Info>().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<TomahawkSipMessage>().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 ) );
}

View File

@ -1,114 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Dominik Schmidt <dev@dominik-schmidt.de>
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef JABBER_P_H
#define JABBER_P_H
#include "../sipdllmacro.h"
#include "avatarmanager.h"
#include <jreen/client.h>
#include <jreen/disco.h>
#include <jreen/message.h>
#include <jreen/messagesession.h>
#include <jreen/stanza.h>
#include <jreen/jreen.h>
#include <jreen/error.h>
#include <jreen/presence.h>
#include <jreen/vcard.h>
#include <jreen/abstractroster.h>
#include <jreen/connection.h>
#include <jreen/mucroom.h>
#include <QObject>
#include <QSharedPointer>
#include <QMap>
#include <QNetworkProxy>
#if defined( WIN32 ) || defined( _WIN32 )
#include <windows.h>
#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<Jreen::Presence::Type, QString> m_presences;
QMap<QString, Jreen::Presence::Type> m_peers;
QString m_server;
enum IqContext { NoContext, RequestDisco, RequestedDisco, SipMessageSent, RequestedVCard };
QStringList m_legacy_peers;
AvatarManager *m_avatarManager;
};
#endif // JABBER_H