PageRenderTime 413ms CodeModel.GetById 100ms app.highlight 245ms RepoModel.GetById 57ms app.codeStats 1ms

/src/sip/jabber/jabber.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 1063 lines | 769 code | 211 blank | 83 comment | 96 complexity | e1bf2498d2b06fcd588b517a8d9b0934 MD5 | raw file
   1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
   2 *
   3 *   Copyright 2010-2011, Dominik Schmidt <dev@dominik-schmidt.de>
   4 *   Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
   5 *
   6 *   Tomahawk is free software: you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation, either version 3 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   Tomahawk is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 *   GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "jabber.h"
  21#include "ui_configwidget.h"
  22
  23#include "config.h"
  24
  25#include "tomahawksettings.h"
  26#include "tomahawksipmessage.h"
  27#include "tomahawksipmessagefactory.h"
  28
  29#include <jreen/jid.h>
  30#include <jreen/capabilities.h>
  31#include <jreen/vcardupdate.h>
  32#include <jreen/vcard.h>
  33#include <jreen/directconnection.h>
  34#include <jreen/tcpconnection.h>
  35#include <jreen/softwareversion.h>
  36#include <jreen/iqreply.h>
  37
  38#include <qjson/parser.h>
  39#include <qjson/serializer.h>
  40
  41#include <QtPlugin>
  42#include <QStringList>
  43#include <QDateTime>
  44#include <QTimer>
  45
  46#ifndef ENABLE_HEADLESS
  47    #include <QInputDialog>
  48    #include <QLineEdit>
  49    #include <QMessageBox>
  50#endif
  51
  52#include <utils/tomahawkutilsgui.h>
  53#include "utils/logger.h"
  54
  55using namespace Jreen;
  56
  57SipPlugin*
  58JabberFactory::createPlugin( const QString& pluginId )
  59{
  60    return new JabberPlugin( pluginId.isEmpty() ? generateId() : pluginId );
  61}
  62
  63
  64QIcon
  65JabberFactory::icon() const
  66{
  67    return QIcon( ":/jabber-icon.png" );
  68}
  69
  70
  71JabberPlugin::JabberPlugin( const QString& pluginId )
  72    : SipPlugin( pluginId )
  73    , m_state( Disconnected )
  74#ifndef ENABLE_HEADLESS
  75    , m_menu( 0 )
  76    , m_xmlConsole( 0 )
  77#endif
  78{
  79    qDebug() << Q_FUNC_INFO;
  80
  81    m_currentUsername = accountName();
  82    m_currentServer = readServer();
  83    m_currentPassword = readPassword();
  84    m_currentPort = readPort();
  85
  86#ifndef ENABLE_HEADLESS
  87    m_configWidget = QWeakPointer< QWidget >( new QWidget );
  88    m_ui = new Ui_JabberConfig;
  89    m_ui->setupUi( m_configWidget.data() );
  90    m_configWidget.data()->setVisible( false );
  91
  92    m_ui->jabberUsername->setText( m_currentUsername );
  93    m_ui->jabberPassword->setText( m_currentPassword );
  94    m_ui->jabberServer->setText( m_currentServer );
  95    m_ui->jabberPort->setValue( m_currentPort );
  96    m_ui->jidExistsLabel->hide();
  97    connect( m_ui->jabberUsername, SIGNAL( textChanged( QString ) ), SLOT( onCheckJidExists( QString ) ) );
  98#endif
  99
 100    // setup JID object
 101    Jreen::JID jid = Jreen::JID( accountName() );
 102
 103    // general client setup
 104    m_client = new Jreen::Client( jid, m_currentPassword );
 105    setupClientHelper();
 106
 107    m_client->registerPayload(new TomahawkSipMessageFactory);
 108    m_currentResource = QString::fromAscii( "tomahawk%1" ).arg( QString::number( qrand() % 10000 ) );
 109    m_client->setResource( m_currentResource );
 110
 111#ifndef ENABLE_HEADLESS
 112    // instantiate XmlConsole
 113    if( readXmlConsoleEnabled() )
 114    {
 115        m_xmlConsole = new XmlConsole( m_client );
 116        m_xmlConsole->show();
 117    }
 118#endif
 119    // add VCardUpdate extension to own presence
 120    m_client->presence().addExtension( new Jreen::VCardUpdate() );
 121
 122    // initaliaze the roster
 123    m_roster = new Jreen::SimpleRoster( m_client );
 124
 125#ifndef ENABLE_HEADLESS
 126    // initialize the AvatarManager
 127    m_avatarManager = new AvatarManager( m_client );
 128#endif
 129
 130    // setup disco
 131    m_client->disco()->setSoftwareVersion( "Tomahawk Player", TOMAHAWK_VERSION, CMAKE_SYSTEM );
 132    m_client->disco()->addIdentity( Jreen::Disco::Identity( "client", "type", "tomahawk", "en" ) );
 133    m_client->disco()->addFeature( TOMAHAWK_FEATURE );
 134
 135    // setup caps node
 136    Jreen::Capabilities::Ptr caps = m_client->presence().payload<Jreen::Capabilities>();
 137    caps->setNode( TOMAHAWK_CAP_NODE_NAME );
 138
 139    // print connection parameters
 140    qDebug() << "Our JID set to:" << m_client->jid().full();
 141    qDebug() << "Our Server set to:" << m_client->server();
 142    qDebug() << "Our Port set to" << m_client->port();
 143
 144    // setup slots
 145    connect(m_client, SIGNAL(serverFeaturesReceived(QSet<QString>)), SLOT(onConnect()));
 146    connect(m_client, SIGNAL(disconnected(Jreen::Client::DisconnectReason)), SLOT(onDisconnect(Jreen::Client::DisconnectReason)));
 147    connect(m_client, SIGNAL(messageReceived(Jreen::Message)), SLOT(onNewMessage(Jreen::Message)));
 148
 149    connect(m_client, SIGNAL(iqReceived(Jreen::IQ)), SLOT(onNewIq(Jreen::IQ)));
 150
 151    connect(m_roster, SIGNAL(presenceReceived(Jreen::RosterItem::Ptr,Jreen::Presence)),
 152                      SLOT(onPresenceReceived(Jreen::RosterItem::Ptr,Jreen::Presence)));
 153    connect(m_roster, SIGNAL(subscriptionReceived(Jreen::RosterItem::Ptr,Jreen::Presence)),
 154                      SLOT(onSubscriptionReceived(Jreen::RosterItem::Ptr,Jreen::Presence)));
 155
 156#ifndef ENABLE_HEADLESS
 157    connect(m_avatarManager, SIGNAL(newAvatar(QString)), SLOT(onNewAvatar(QString)));
 158#endif
 159}
 160
 161
 162JabberPlugin::~JabberPlugin()
 163{
 164    delete m_avatarManager;
 165    delete m_roster;
 166#ifndef ENABLE_HEADLESS
 167    delete m_xmlConsole;
 168#endif
 169    delete m_client;
 170    delete m_ui;
 171}
 172
 173
 174const QString
 175JabberPlugin::name() const
 176{
 177    return QString( MYNAME );
 178}
 179
 180
 181const QString
 182JabberPlugin::friendlyName() const
 183{
 184    return QString( "Jabber" );
 185}
 186
 187
 188const QString
 189JabberPlugin::accountName() const
 190{
 191    return TomahawkSettings::instance()->value( pluginId() + "/username" ).toString();
 192}
 193
 194
 195#ifndef ENABLE_HEADLESS
 196QMenu*
 197JabberPlugin::menu()
 198{
 199    return m_menu;
 200}
 201
 202
 203QWidget*
 204JabberPlugin::configWidget()
 205{
 206    return m_configWidget.data();
 207}
 208
 209
 210QIcon
 211JabberPlugin::icon() const
 212{
 213    return QIcon( ":/jabber-icon.png" );
 214}
 215#endif
 216
 217
 218bool
 219JabberPlugin::connectPlugin( bool startup )
 220{
 221    Q_UNUSED( startup );
 222    qDebug() << Q_FUNC_INFO;
 223
 224    if(m_client->isConnected())
 225    {
 226        qDebug() << Q_FUNC_INFO << "Already connected to server, not connecting again...";
 227        return true; //FIXME: should i return false here?!
 228    }
 229
 230    qDebug() << "Connecting to the XMPP server..." << m_client->jid().full();
 231
 232    //FIXME: we're badly workarounding some missing reconnection api here, to be fixed soon
 233    QTimer::singleShot( 1000, m_client, SLOT( connectToServer() ) );
 234
 235    if ( m_client->connection() )
 236        connect(m_client->connection(), SIGNAL(error(SocketError)), SLOT(onError(SocketError)));
 237
 238    m_state = Connecting;
 239    emit stateChanged( m_state );
 240    return true;
 241}
 242
 243
 244void
 245JabberPlugin::disconnectPlugin()
 246{
 247    if (!m_client->isConnected())
 248    {
 249        if ( m_state != Disconnected ) // might be Connecting
 250        {
 251           m_state = Disconnected;
 252           emit stateChanged( m_state );
 253        }
 254        return;
 255    }
 256
 257    //m_roster->deleteLater();
 258    //m_roster = 0;
 259    //m_room->deleteLater();
 260    //m_room = 0;
 261
 262    m_peers.clear();
 263
 264    m_client->disconnectFromServer( true );
 265    m_state = Disconnecting;
 266    emit stateChanged( m_state );
 267}
 268
 269
 270void
 271JabberPlugin::onConnect()
 272{
 273//    qDebug() << Q_FUNC_INFO;
 274
 275    // update jid resource, servers like gtalk use resource binding and may
 276    // have changed our requested /resource
 277    if ( m_client->jid().resource() != m_currentResource )
 278    {
 279        m_currentResource = m_client->jid().resource();
 280        emit jidChanged( m_client->jid().full() );
 281    }
 282
 283    qDebug() << "Connected to jabber as:" << m_client->jid().full();
 284
 285    // set presence to least valid value
 286    m_client->setPresence(Jreen::Presence::XA, "Got Tomahawk? http://gettomahawk.com", -127);
 287
 288    // set ping timeout to 15 secs (TODO: verify if this works)
 289    m_client->setPingInterval(1000);
 290
 291    // load roster
 292    m_roster->load();
 293
 294    //FIXME: this implementation is totally broken atm, so it's disabled to avoid harm :P
 295    // join MUC with bare jid as nickname
 296    //TODO: make the room a list of rooms and make that configurable
 297    QString mucNickname = QString( "tomahawk@conference.qutim.org/" ).append( QString( m_client->jid().bare() ).replace( "@", "-" ) );
 298    //m_room = new Jreen::MUCRoom(m_client, Jreen::JID( mucNickname ) );
 299    //m_room->setHistorySeconds(0);
 300    //m_room->join();
 301
 302    // treat muc participiants like contacts
 303    //connect( m_room, SIGNAL( messageReceived( Jreen::Message, bool ) ), this, SLOT( onNewMessage( Jreen::Message ) ) );
 304    //connect( m_room, SIGNAL( presenceReceived( Jreen::Presence, const Jreen::MUCRoom::Participant* ) ), this, SLOT( onNewPresence( Jreen::Presence ) ) );
 305
 306    m_state = Connected;
 307    emit stateChanged( m_state );
 308
 309    addMenuHelper();
 310}
 311
 312
 313void
 314JabberPlugin::onDisconnect( Jreen::Client::DisconnectReason reason )
 315{
 316    qDebug() << Q_FUNC_INFO;
 317
 318    switch( reason )
 319    {
 320        case Jreen::Client::User:
 321            break;
 322
 323        case Jreen::Client::AuthorizationError:
 324            emit error( SipPlugin::AuthError, errorMessage( reason ) );
 325            break;
 326
 327        case Jreen::Client::HostUnknown:
 328        case Jreen::Client::ItemNotFound:
 329        case Jreen::Client::RemoteStreamError:
 330        case Jreen::Client::RemoteConnectionFailed:
 331        case Jreen::Client::InternalServerError:
 332        case Jreen::Client::SystemShutdown:
 333        case Jreen::Client::Conflict:
 334        case Jreen::Client::Unknown:
 335            emit error( SipPlugin::ConnectionError, errorMessage( reason ) );
 336            break;
 337
 338        default:
 339            qDebug() << "Not all Client::DisconnectReasons checked";
 340            Q_ASSERT(false);
 341            break;
 342    }
 343    m_state = Disconnected;
 344    emit stateChanged( m_state );
 345
 346    removeMenuHelper();
 347
 348    Q_FOREACH(const Jreen::JID &peer, m_peers.keys())
 349    {
 350        handlePeerStatus(peer, Jreen::Presence::Unavailable);
 351    }
 352}
 353
 354
 355void
 356JabberPlugin::onError( const Jreen::Connection::SocketError& e )
 357{
 358    tDebug() << "JABBER error:" << e;
 359}
 360
 361
 362QString
 363JabberPlugin::errorMessage( Jreen::Client::DisconnectReason reason )
 364{
 365    switch( reason )
 366    {
 367        case Jreen::Client::User:
 368            return tr("User Interaction");
 369            break;
 370        case Jreen::Client::HostUnknown:
 371            return tr("Host is unknown");
 372            break;
 373        case Jreen::Client::ItemNotFound:
 374            return tr("Item not found");
 375            break;
 376        case Jreen::Client::AuthorizationError:
 377            return tr("Authorization Error");
 378            break;
 379        case Jreen::Client::RemoteStreamError:
 380            return tr("Remote Stream Error");
 381            break;
 382        case Jreen::Client::RemoteConnectionFailed:
 383            return tr("Remote Connection failed");
 384            break;
 385        case Jreen::Client::InternalServerError:
 386            return tr("Internal Server Error");
 387            break;
 388        case Jreen::Client::SystemShutdown:
 389            return tr("System shutdown");
 390            break;
 391        case Jreen::Client::Conflict:
 392            return tr("Conflict");
 393            break;
 394
 395        case Jreen::Client::Unknown:
 396            return tr("Unknown");
 397            break;
 398
 399        default:
 400            qDebug() << "Not all Client::DisconnectReasons checked";
 401            Q_ASSERT(false);
 402            break;
 403    }
 404
 405    m_state = Disconnected;
 406    emit stateChanged( m_state );
 407
 408    return QString();
 409}
 410
 411
 412void
 413JabberPlugin::sendMsg(const QString& to, const QString& msg)
 414{
 415    qDebug() << Q_FUNC_INFO << to << msg;
 416
 417    if ( !m_client ) {
 418        return;
 419    }
 420
 421    /*******************************************************
 422     * Obsolete this by a SipMessage class
 423     */
 424    QJson::Parser parser;
 425    bool ok;
 426    QVariant v = parser.parse( msg.toAscii(), &ok );
 427    if ( !ok  || v.type() != QVariant::Map )
 428    {
 429        qDebug() << "Invalid JSON in XMPP msg";
 430        return;
 431    }
 432    QVariantMap m = v.toMap();
 433    /*******************************************************/
 434
 435    TomahawkSipMessage *sipMessage;
 436    if(m["visible"].toBool())
 437    {
 438        sipMessage = new TomahawkSipMessage(m["ip"].toString(),
 439                                            m["port"].toInt(),
 440                                            m["uniqname"].toString(),
 441                                            m["key"].toString()
 442                                            );
 443    }
 444    else
 445    {
 446        sipMessage = new TomahawkSipMessage();
 447    }
 448
 449    qDebug() << "Send sip messsage to " << to;
 450    Jreen::IQ iq( Jreen::IQ::Set, to );
 451    iq.addExtension( sipMessage );
 452    Jreen::IQReply *reply = m_client->send(iq);
 453    reply->setData(SipMessageSent);
 454    connect(reply, SIGNAL(received(Jreen::IQ)), SLOT(onNewIq(Jreen::IQ)));
 455}
 456
 457
 458void
 459JabberPlugin::broadcastMsg(const QString& msg)
 460{
 461    qDebug() << Q_FUNC_INFO;
 462
 463    if ( !m_client )
 464        return;
 465
 466    foreach( const Jreen::JID& jid, m_peers.keys() )
 467    {
 468        sendMsg( jid.full(), msg );
 469    }
 470}
 471
 472
 473void
 474JabberPlugin::addContact(const QString& jid, const QString& msg)
 475{
 476    // Add contact to the Tomahawk group on the roster
 477
 478    QString realJid = jid;
 479    if( !realJid.contains( '@' ) )
 480        realJid += defaultSuffix();
 481
 482    m_roster->subscribe( realJid, msg, realJid, QStringList() << "Tomahawk" );
 483
 484    return;
 485}
 486
 487
 488void
 489JabberPlugin::showAddFriendDialog()
 490{
 491#ifndef ENABLE_HEADLESS
 492    bool ok;
 493    QString id = QInputDialog::getText( TomahawkUtils::tomahawkWindow(), tr( "Add Friend" ),
 494                                        tr( "Enter Jabber ID:" ), QLineEdit::Normal, "", &ok ).trimmed();
 495    if ( !ok )
 496        return;
 497
 498    qDebug() << "Attempting to add jabber contact to roster:" << id;
 499    addContact( id );
 500#endif
 501}
 502
 503
 504QString
 505JabberPlugin::defaultSuffix() const
 506{
 507    return "@jabber.org";
 508}
 509
 510
 511void
 512JabberPlugin::showXmlConsole()
 513{
 514#ifndef ENABLE_HEADLESS
 515   m_xmlConsole->show();
 516#endif
 517}
 518
 519
 520void
 521JabberPlugin::checkSettings()
 522{
 523    bool reconnect = false;
 524
 525    QString username, password, server;
 526    int port;
 527
 528    username = accountName();
 529    password = readPassword();
 530    server = readServer();
 531    port = readPort();
 532
 533    if ( m_currentUsername != username )
 534    {
 535        m_currentUsername = username;
 536        reconnect = true;
 537    }
 538    if ( m_currentPassword != password )
 539    {
 540        m_currentPassword = password;
 541        reconnect = true;
 542    }
 543    if ( m_currentServer != server )
 544    {
 545        m_currentServer = server;
 546        reconnect = true;
 547    }
 548    if ( m_currentPort != readPort() )
 549    {
 550        m_currentPort = port;
 551        reconnect = true;
 552    }
 553
 554    if ( !m_currentUsername.contains( '@' ) )
 555    {
 556        m_currentUsername += defaultSuffix();
 557        TomahawkSettings::instance()->setValue( pluginId() + "/username", m_currentUsername );
 558    }
 559
 560    if ( reconnect )
 561    {
 562        qDebug() << Q_FUNC_INFO << "Reconnecting jreen plugin...";
 563        disconnectPlugin();
 564
 565        setupClientHelper();
 566
 567        qDebug() << Q_FUNC_INFO << "Updated settings";
 568        connectPlugin( false );
 569    }
 570}
 571
 572
 573void JabberPlugin::setupClientHelper()
 574{
 575    Jreen::JID jid = Jreen::JID( m_currentUsername );
 576    m_client->setJID( jid );
 577    m_client->setPassword( m_currentPassword );
 578
 579    if( !m_currentServer.isEmpty() )
 580    {
 581        // set explicit server details
 582        m_client->setServer( m_currentServer );
 583        m_client->setPort( m_currentPort );
 584    }
 585    else
 586    {
 587        // let jreen find out server and port via jdns
 588        m_client->setServer( jid.domain() );
 589        m_client->setPort( -1 );
 590    }
 591}
 592
 593
 594void JabberPlugin::addMenuHelper()
 595{
 596#ifndef ENABLE_HEADLESS
 597    if( !m_menu )
 598    {
 599        m_menu = new QMenu( QString( "%1 (" ).arg( friendlyName() ).append( accountName() ).append(")" ) );
 600
 601        QAction* addFriendAction = m_menu->addAction( tr( "Add Friend..." ) );
 602        connect( addFriendAction, SIGNAL( triggered() ), this, SLOT( showAddFriendDialog() ) );
 603
 604        if( readXmlConsoleEnabled() )
 605        {
 606            QAction* showXmlConsoleAction = m_menu->addAction( tr( "XML Console...") );
 607            connect( showXmlConsoleAction, SIGNAL( triggered() ), this, SLOT( showXmlConsole() ) );
 608        }
 609
 610        emit addMenu( m_menu );
 611    }
 612#endif
 613}
 614
 615
 616void JabberPlugin::removeMenuHelper()
 617{
 618#ifndef ENABLE_HEADLESS
 619    if( m_menu )
 620    {
 621        emit removeMenu( m_menu );
 622
 623        delete m_menu;
 624        m_menu = 0;
 625    }
 626#endif
 627}
 628
 629
 630void JabberPlugin::onNewMessage(const Jreen::Message& message)
 631{
 632    if ( m_state != Connected )
 633        return;
 634
 635//    qDebug() << Q_FUNC_INFO << "message type" << message.subtype();
 636
 637    QString from = message.from().full();
 638    QString msg = message.body();
 639
 640    if(msg.isEmpty())
 641        return;
 642
 643    if( message.subtype() == Jreen::Message::Error )
 644    {
 645        tDebug() << Q_FUNC_INFO << "Received error message from " << from << ", not answering... (Condition: "
 646                 << ( message.error().isNull() ? -1 : message.error()->condition() ) << ")";
 647        return;
 648    }
 649
 650    SipInfo info = SipInfo::fromJson( msg );
 651
 652    if ( !info.isValid() )
 653    {
 654        QString to = from;
 655        QString response = QString( tr("I'm sorry -- I'm just an automatic presence used by Tomahawk Player"
 656                                    " (http://gettomahawk.com). If you are getting this message, the person you"
 657                                    " are trying to reach is probably not signed on, so please try again later!") );
 658
 659        // this is not a sip message, so we send it directly through the client
 660        m_client->send( Jreen::Message ( Jreen::Message::Error, Jreen::JID(to), response) );
 661
 662        emit msgReceived( from, msg );
 663        return;
 664    }
 665
 666    qDebug() << Q_FUNC_INFO << "From:" << message.from().full() << ":" << message.body();
 667    emit sipInfoReceived( from, info );
 668}
 669
 670
 671void JabberPlugin::onPresenceReceived( const Jreen::RosterItem::Ptr &item, const Jreen::Presence& presence )
 672{
 673    Q_UNUSED(item);
 674    if ( m_state != Connected )
 675        return;
 676
 677    Jreen::JID jid = presence.from();
 678    QString fulljid( jid.full() );
 679
 680    qDebug() << Q_FUNC_INFO << "* New presence:" << fulljid << presence.subtype();
 681
 682    if( jid == m_client->jid() )
 683        return;
 684
 685    if ( presence.error() ) {
 686        //qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: no" << "presence error";
 687        return;
 688    }
 689
 690    // ignore anyone not Running tomahawk:
 691    Jreen::Capabilities::Ptr caps = presence.payload<Jreen::Capabilities>();
 692    if( caps )
 693    {
 694        qDebug() << Q_FUNC_INFO << fulljid << "Running tomahawk: maybe" << "caps " << caps->node() << "requesting disco...";
 695
 696        // request disco features
 697        QString node = caps->node() + '#' + caps->ver();
 698
 699        Jreen::IQ featuresIq( Jreen::IQ::Get, jid );
 700        featuresIq.addExtension( new Jreen::Disco::Info( node ) );
 701
 702        Jreen::IQReply *reply = m_client->send(featuresIq);
 703        reply->setData(RequestDisco);
 704        connect(reply, SIGNAL(received(Jreen::IQ)), SLOT(onNewIq(Jreen::IQ)));
 705    }
 706    else if( !caps )
 707    {
 708//        qDebug() << Q_FUNC_INFO << "Running tomahawk: no" << "no caps";
 709        if ( presenceMeansOnline( m_peers[ jid ] ) )
 710            handlePeerStatus( jid, Jreen::Presence::Unavailable );
 711    }
 712}
 713
 714
 715void JabberPlugin::onSubscriptionReceived(const Jreen::RosterItem::Ptr& item, const Jreen::Presence& presence)
 716{
 717    if ( m_state != Connected )
 718        return;
 719
 720//    qDebug() << Q_FUNC_INFO << "presence type:" << presence.subtype();
 721    if(item)
 722        qDebug() << Q_FUNC_INFO << presence.from().full() << "subs" << item->subscription() << "ask" << item->ask();
 723    else
 724        qDebug() << Q_FUNC_INFO << "item empty";
 725
 726    // don't do anything if the contact is already subscribed to us
 727    if( presence.subtype() != Jreen::Presence::Subscribe ||
 728        (
 729            item && (item->subscription() == Jreen::RosterItem::From || item->subscription() == Jreen::RosterItem::Both)
 730        )
 731    )
 732    {
 733        return;
 734    }
 735
 736    // check if the requester is already on the roster
 737    if(item &&
 738        (
 739            item->subscription() == Jreen::RosterItem::To ||
 740            ( item->subscription() == Jreen::RosterItem::None && !item->ask().isEmpty() )
 741        )
 742    )
 743    {
 744        qDebug() << Q_FUNC_INFO << presence.from().bare() << "already on the roster so we assume ack'ing subscription request is okay...";
 745        m_roster->allowSubscription(presence.from(), true);
 746
 747        return;
 748    }
 749#ifndef ENABLE_HEADLESS
 750    // preparing the confirm box for the user
 751    QMessageBox *confirmBox = new QMessageBox(
 752                                QMessageBox::Question,
 753                                tr( "Authorize User" ),
 754                                QString( tr( "Do you want to grant <b>%1</b> access to your Collection?" ) ).arg(presence.from().bare()),
 755                                QMessageBox::Yes | QMessageBox::No,
 756                                TomahawkUtils::tomahawkWindow()
 757                              );
 758
 759    // add confirmBox to m_subscriptionConfirmBoxes
 760    m_subscriptionConfirmBoxes.insert( presence.from(), confirmBox );
 761
 762    // display the box and wait for the answer
 763    confirmBox->open( this, SLOT( onSubscriptionRequestConfirmed( int ) ) );
 764#endif
 765}
 766
 767
 768void
 769JabberPlugin::onSubscriptionRequestConfirmed( int result )
 770{
 771#ifndef ENABLE_HEADLESS
 772    qDebug() << Q_FUNC_INFO << result;
 773
 774    QList< QMessageBox* > confirmBoxes = m_subscriptionConfirmBoxes.values();
 775    Jreen::JID jid;
 776
 777    foreach( QMessageBox* currentBox, confirmBoxes )
 778    {
 779        if( currentBox == sender() )
 780        {
 781            jid = m_subscriptionConfirmBoxes.key( currentBox );
 782        }
 783    }
 784
 785    // we got an answer, deleting the box
 786    m_subscriptionConfirmBoxes.remove( jid );
 787    sender()->deleteLater();
 788
 789    QMessageBox::StandardButton allowSubscription = static_cast<QMessageBox::StandardButton>( result );
 790
 791    if ( allowSubscription == QMessageBox::Yes )
 792    {
 793        qDebug() << Q_FUNC_INFO << jid.bare() << "accepted by user, adding to roster";
 794        addContact(jid, "");
 795    }
 796    else
 797    {
 798        qDebug() << Q_FUNC_INFO << jid.bare() << "declined by user";
 799    }
 800
 801    m_roster->allowSubscription( jid, allowSubscription == QMessageBox::Yes );
 802#endif
 803}
 804
 805
 806void JabberPlugin::onNewIq(const Jreen::IQ& iq)
 807{
 808    if ( m_state != Connected )
 809        return;
 810
 811    Jreen::IQReply *reply = qobject_cast<Jreen::IQReply*>(sender());
 812    int context = reply ? reply->data().toInt() : NoContext;
 813
 814    if( context == RequestDisco )
 815    {
 816//        qDebug() << Q_FUNC_INFO << "Received disco IQ...";
 817        Jreen::Disco::Info *discoInfo = iq.payload<Jreen::Disco::Info>().data();
 818        if(!discoInfo)
 819            return;
 820        iq.accept();
 821
 822        Jreen::JID jid = iq.from();
 823
 824        Jreen::DataForm::Ptr form = discoInfo->form();
 825
 826        if(discoInfo->features().contains( TOMAHAWK_FEATURE ))
 827        {
 828            qDebug() << Q_FUNC_INFO << jid.full() << "Running tomahawk/feature enabled: yes";
 829
 830            // the actual presence doesn't matter, it just needs to be "online"
 831            handlePeerStatus( jid, Jreen::Presence::Available );
 832        }
 833    }
 834    else if(context == RequestVersion)
 835    {
 836        Jreen::SoftwareVersion::Ptr softwareVersion = iq.payload<Jreen::SoftwareVersion>();
 837        if( softwareVersion )
 838        {
 839            QString versionString = QString("%1 %2 %3").arg( softwareVersion->name(), softwareVersion->os(), softwareVersion->version() );
 840            qDebug() << Q_FUNC_INFO << "Received software version for " << iq.from().full() << ":" << versionString;
 841            emit softwareVersionReceived( iq.from().full(), versionString );
 842        }
 843    }
 844    else if(context == RequestedDisco)
 845    {
 846        qDebug() << "Sent IQ(Set), what should be happening here?";
 847    }
 848    else if( context == SipMessageSent )
 849    {
 850        qDebug() << "Sent SipMessage... what now?!";
 851    }
 852    /*else if(context == RequestedVCard )
 853    {
 854        qDebug() << "Requested VCard... what now?!";
 855    }*/
 856    else
 857    {
 858        TomahawkSipMessage::Ptr sipMessage = iq.payload<TomahawkSipMessage>();
 859        if(sipMessage)
 860        {
 861            iq.accept();
 862
 863            qDebug() << Q_FUNC_INFO << "Got SipMessage ..."
 864                     << "ip" << sipMessage->ip() << "port" << sipMessage->port() << "uniqname" << sipMessage->uniqname() << "key" << sipMessage->key() << "visible" << sipMessage->visible();
 865
 866            SipInfo info;
 867            info.setVisible( sipMessage->visible() );
 868            if( sipMessage->visible() )
 869            {
 870
 871                QHostInfo hi;
 872                hi.setHostName( sipMessage->ip() );
 873                info.setHost( hi );
 874                info.setPort( sipMessage->port() );
 875                info.setUniqname( sipMessage->uniqname() );
 876                info.setKey( sipMessage->key() );
 877            }
 878
 879            Q_ASSERT( info.isValid() );
 880
 881            qDebug() << Q_FUNC_INFO << "From:" << iq.from().full() << ":" << info;
 882            emit sipInfoReceived( iq.from().full(), info );
 883        }
 884    }
 885}
 886
 887
 888bool JabberPlugin::presenceMeansOnline(Jreen::Presence::Type p)
 889{
 890    switch(p)
 891    {
 892        case Jreen::Presence::Invalid:
 893        case Jreen::Presence::Unavailable:
 894        case Jreen::Presence::Error:
 895            return false;
 896            break;
 897        default:
 898            return true;
 899    }
 900}
 901
 902
 903void JabberPlugin::handlePeerStatus(const Jreen::JID& jid, Jreen::Presence::Type presenceType)
 904{
 905    QString fulljid = jid.full();
 906
 907    // "going offline" event
 908    if ( !presenceMeansOnline( presenceType ) &&
 909         ( !m_peers.contains( jid ) ||
 910           presenceMeansOnline( m_peers.value( jid ) )
 911         )
 912       )
 913    {
 914        m_peers[ jid ] = presenceType;
 915        qDebug() << Q_FUNC_INFO << "* Peer goes offline:" << fulljid;
 916
 917        emit peerOffline( fulljid );
 918        return;
 919    }
 920
 921    // "coming online" event
 922    if( presenceMeansOnline( presenceType ) &&
 923        ( !m_peers.contains( jid ) ||
 924          !presenceMeansOnline( m_peers.value( jid ) )
 925        )
 926       )
 927    {
 928        m_peers[ jid ] = presenceType;
 929        qDebug() << Q_FUNC_INFO << "* Peer goes online:" << fulljid;
 930
 931        emit peerOnline( fulljid );
 932
 933#ifndef ENABLE_HEADLESS
 934        if(!m_avatarManager->avatar(jid.bare()).isNull())
 935            onNewAvatar( jid.bare() );
 936#endif
 937
 938        // request software version
 939        Jreen::IQ versionIq( Jreen::IQ::Get, jid );
 940        versionIq.addExtension( new Jreen::SoftwareVersion() );
 941        Jreen::IQReply *reply = m_client->send(versionIq);
 942        reply->setData(RequestVersion);
 943        connect(reply, SIGNAL(received(Jreen::IQ)), SLOT(onNewIq(Jreen::IQ)));
 944
 945        return;
 946    }
 947
 948    //qDebug() << "Updating presence data for" << fulljid;
 949    m_peers[ jid ] = presenceType;
 950}
 951
 952
 953void JabberPlugin::onNewAvatar(const QString& jid)
 954{
 955#ifndef ENABLE_HEADLESS
 956//    qDebug() << Q_FUNC_INFO << jid;
 957    if ( m_state != Connected )
 958        return;
 959
 960    Q_ASSERT(!m_avatarManager->avatar( jid ).isNull());
 961
 962    // find peers for the jid
 963    QList<Jreen::JID> peers =  m_peers.keys();
 964    foreach(const Jreen::JID &peer, peers)
 965    {
 966        if( peer.bare() == jid )
 967        {
 968            emit avatarReceived ( peer.full(),  m_avatarManager->avatar( jid ) );
 969        }
 970    }
 971
 972    if( jid == m_client->jid().bare() )
 973        // own avatar
 974        emit avatarReceived ( m_avatarManager->avatar( jid ) );
 975    else
 976        // someone else's avatar
 977        emit avatarReceived ( jid,  m_avatarManager->avatar( jid ) );
 978#endif
 979}
 980
 981
 982bool
 983JabberPlugin::readXmlConsoleEnabled()
 984{
 985    return TomahawkSettings::instance()->value( pluginId() + "/xmlconsole", QVariant( false ) ).toBool();
 986}
 987
 988
 989QString
 990JabberPlugin::readPassword()
 991{
 992    return TomahawkSettings::instance()->value( pluginId() + "/password" ).toString();
 993}
 994
 995
 996int
 997JabberPlugin::readPort()
 998{
 999    return TomahawkSettings::instance()->value( pluginId() + "/port", 5222 ).toInt();
1000}
1001
1002
1003QString
1004JabberPlugin::readServer()
1005{
1006    return TomahawkSettings::instance()->value( pluginId() + "/server" ).toString();
1007}
1008
1009
1010void
1011JabberPlugin::onCheckJidExists( QString jid )
1012{
1013    for ( int i=0; i<TomahawkSettings::instance()->sipPlugins().count(); i++ )
1014    {
1015        QString savedUsername = TomahawkSettings::instance()->value(
1016                TomahawkSettings::instance()->sipPlugins().at( i ) + "/username" ).toString();
1017        QStringList splitUserName = TomahawkSettings::instance()->value(
1018                TomahawkSettings::instance()->sipPlugins().at( i ) + "/username" ).toString().split("@");
1019        QString server = TomahawkSettings::instance()->value(
1020                TomahawkSettings::instance()->sipPlugins().at( i ) + "/server" ).toString();
1021
1022        if ( ( savedUsername == jid || splitUserName.contains( jid ) ) &&
1023               server == m_ui->jabberServer->text() && !jid.trimmed().isEmpty() )
1024        {
1025            m_ui->jidExistsLabel->show();
1026            // the already jid exists
1027            emit dataError( true );
1028            return;
1029        }
1030    }
1031    m_ui->jidExistsLabel->hide();
1032    emit dataError( false );
1033}
1034
1035
1036void
1037JabberPlugin::saveConfig()
1038{
1039    TomahawkSettings::instance()->setValue( pluginId() + "/username", m_ui->jabberUsername->text() );
1040    TomahawkSettings::instance()->setValue( pluginId() + "/password", m_ui->jabberPassword->text() );
1041    TomahawkSettings::instance()->setValue( pluginId() + "/port", m_ui->jabberPort->value() );
1042    TomahawkSettings::instance()->setValue( pluginId() + "/server", m_ui->jabberServer->text() );
1043
1044    checkSettings();
1045}
1046
1047
1048void
1049JabberPlugin::deletePlugin()
1050{
1051    TomahawkSettings::instance()->remove( pluginId() );
1052}
1053
1054
1055SipPlugin::ConnectionState
1056JabberPlugin::connectionState() const
1057{
1058    return m_state;
1059}
1060
1061#ifndef GOOGLE_WRAPPER
1062Q_EXPORT_PLUGIN2( sipfactory, JabberFactory )
1063#endif