You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1354 lines
38 KiB
1354 lines
38 KiB
15 years ago
|
/*
|
||
|
client.cpp - Kopete Oscar Protocol
|
||
|
|
||
|
Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org>
|
||
|
|
||
|
Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
|
||
|
Based on Iris, Copyright (C) 2003 Justin Karneges
|
||
|
|
||
|
Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
|
||
|
|
||
|
*************************************************************************
|
||
|
* *
|
||
|
* This library is free software; you can redistribute it and/or *
|
||
|
* modify it under the terms of the GNU Lesser General Public *
|
||
|
* License as published by the Free Software Foundation; either *
|
||
|
* version 2 of the License, or (at your option) any later version. *
|
||
|
* *
|
||
|
*************************************************************************
|
||
|
*/
|
||
|
|
||
|
#include "client.h"
|
||
|
|
||
|
#include <qtimer.h>
|
||
|
#include <qtextcodec.h>
|
||
|
|
||
|
#include <kdebug.h> //for kdDebug()
|
||
|
#include <klocale.h>
|
||
|
|
||
|
#include "buddyicontask.h"
|
||
|
#include "clientreadytask.h"
|
||
|
#include "connectionhandler.h"
|
||
|
#include "changevisibilitytask.h"
|
||
|
#include "chatnavservicetask.h"
|
||
|
#include "errortask.h"
|
||
|
#include "icquserinfo.h"
|
||
|
#include "icquserinfotask.h"
|
||
|
#include "logintask.h"
|
||
|
#include "connection.h"
|
||
|
#include "messagereceivertask.h"
|
||
|
#include "onlinenotifiertask.h"
|
||
|
#include "oscarclientstream.h"
|
||
|
#include "oscarconnector.h"
|
||
|
#include "oscarsettings.h"
|
||
|
#include "oscarutils.h"
|
||
|
#include "ownuserinfotask.h"
|
||
|
#include "profiletask.h"
|
||
|
#include "senddcinfotask.h"
|
||
|
#include "sendmessagetask.h"
|
||
|
#include "serverredirecttask.h"
|
||
|
#include "servicesetuptask.h"
|
||
|
#include "ssimanager.h"
|
||
|
#include "ssimodifytask.h"
|
||
|
#include "ssiauthtask.h"
|
||
|
#include "offlinemessagestask.h"
|
||
|
#include "task.h"
|
||
|
#include "typingnotifytask.h"
|
||
|
#include "userinfotask.h"
|
||
|
#include "usersearchtask.h"
|
||
|
#include "warningtask.h"
|
||
|
#include "chatservicetask.h"
|
||
|
#include "rateclassmanager.h"
|
||
|
|
||
|
|
||
|
namespace
|
||
|
{
|
||
|
class DefaultCodecProvider : public Client::CodecProvider
|
||
|
{
|
||
|
public:
|
||
|
virtual QTextCodec* codecForContact( const QString& ) const
|
||
|
{
|
||
|
return QTextCodec::codecForMib( 4 );
|
||
|
}
|
||
|
virtual QTextCodec* codecForAccount() const
|
||
|
{
|
||
|
return QTextCodec::codecForMib( 4 );
|
||
|
}
|
||
|
};
|
||
|
|
||
|
DefaultCodecProvider defaultCodecProvider;
|
||
|
}
|
||
|
|
||
|
class Client::ClientPrivate
|
||
|
{
|
||
|
public:
|
||
|
ClientPrivate() {}
|
||
|
|
||
|
QString host, user, pass;
|
||
|
uint port;
|
||
|
int tzoffset;
|
||
|
bool active;
|
||
|
|
||
|
enum { StageOne, StageTwo };
|
||
|
int stage;
|
||
|
|
||
|
//Protocol specific data
|
||
|
bool isIcq;
|
||
|
bool redirectRequested;
|
||
|
QValueList<WORD> redirectionServices;
|
||
|
WORD currentRedirect;
|
||
|
QByteArray cookie;
|
||
|
DWORD connectAsStatus; // icq only
|
||
|
QString connectWithMessage; // icq only
|
||
|
Oscar::Settings* settings;
|
||
|
|
||
|
//Tasks
|
||
|
ErrorTask* errorTask;
|
||
|
OnlineNotifierTask* onlineNotifier;
|
||
|
OwnUserInfoTask* ownStatusTask;
|
||
|
MessageReceiverTask* messageReceiverTask;
|
||
|
SSIAuthTask* ssiAuthTask;
|
||
|
ICQUserInfoRequestTask* icqInfoTask;
|
||
|
UserInfoTask* userInfoTask;
|
||
|
TypingNotifyTask * typingNotifyTask;
|
||
|
SSIModifyTask* ssiModifyTask;
|
||
|
//Managers
|
||
|
SSIManager* ssiManager;
|
||
|
ConnectionHandler connections;
|
||
|
|
||
|
//Our Userinfo
|
||
|
UserDetails ourDetails;
|
||
|
|
||
|
//Infos
|
||
|
QValueList<int> exchanges;
|
||
|
|
||
|
QString statusMessage; // for away-,DND-message etc...
|
||
|
|
||
|
//away messages
|
||
|
struct AwayMsgRequest
|
||
|
{
|
||
|
QString contact;
|
||
|
ICQStatus contactStatus;
|
||
|
};
|
||
|
QValueList<AwayMsgRequest> awayMsgRequestQueue;
|
||
|
QTimer* awayMsgRequestTimer;
|
||
|
CodecProvider* codecProvider;
|
||
|
|
||
|
const Oscar::ClientVersion* version;
|
||
|
};
|
||
|
|
||
|
Client::Client( QObject* parent )
|
||
|
:QObject( parent, "oscarclient" )
|
||
|
{
|
||
|
m_loginTask = 0L;
|
||
|
m_loginTaskTwo = 0L;
|
||
|
|
||
|
d = new ClientPrivate;
|
||
|
d->tzoffset = 0;
|
||
|
d->active = false;
|
||
|
d->isIcq = false; //default to AIM
|
||
|
d->redirectRequested = false;
|
||
|
d->currentRedirect = 0;
|
||
|
d->connectAsStatus = 0x0; // default to online
|
||
|
d->ssiManager = new SSIManager( this );
|
||
|
d->settings = new Oscar::Settings();
|
||
|
d->errorTask = 0L;
|
||
|
d->onlineNotifier = 0L;
|
||
|
d->ownStatusTask = 0L;
|
||
|
d->messageReceiverTask = 0L;
|
||
|
d->ssiAuthTask = 0L;
|
||
|
d->icqInfoTask = 0L;
|
||
|
d->userInfoTask = 0L;
|
||
|
d->stage = ClientPrivate::StageOne;
|
||
|
d->typingNotifyTask = 0L;
|
||
|
d->ssiModifyTask = 0L;
|
||
|
d->awayMsgRequestTimer = new QTimer();
|
||
|
d->codecProvider = &defaultCodecProvider;
|
||
|
|
||
|
connect( this, SIGNAL( redirectionFinished( WORD ) ),
|
||
|
this, SLOT( checkRedirectionQueue( WORD ) ) );
|
||
|
connect( d->awayMsgRequestTimer, SIGNAL( timeout() ),
|
||
|
this, SLOT( nextICQAwayMessageRequest() ) );
|
||
|
}
|
||
|
|
||
|
Client::~Client()
|
||
|
{
|
||
|
|
||
|
//delete the connections differently than in deleteConnections()
|
||
|
//deleteLater() seems to cause destruction order issues
|
||
|
deleteStaticTasks();
|
||
|
delete d->settings;
|
||
|
delete d->ssiManager;
|
||
|
delete d->awayMsgRequestTimer;
|
||
|
delete d;
|
||
|
}
|
||
|
|
||
|
Oscar::Settings* Client::clientSettings() const
|
||
|
{
|
||
|
return d->settings;
|
||
|
}
|
||
|
|
||
|
void Client::connectToServer( Connection *c, const QString& server, bool auth )
|
||
|
{
|
||
|
d->connections.append( c );
|
||
|
if ( auth == true )
|
||
|
{
|
||
|
m_loginTask = new StageOneLoginTask( c->rootTask() );
|
||
|
connect( m_loginTask, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) );
|
||
|
}
|
||
|
|
||
|
connect( c, SIGNAL( socketError( int, const QString& ) ), this, SLOT( determineDisconnection( int, const QString& ) ) );
|
||
|
c->connectToServer(server, auth);
|
||
|
}
|
||
|
|
||
|
void Client::start( const QString &host, const uint port, const QString &userId, const QString &pass )
|
||
|
{
|
||
|
Q_UNUSED( host );
|
||
|
Q_UNUSED( port );
|
||
|
d->user = userId;
|
||
|
d->pass = pass;
|
||
|
d->stage = ClientPrivate::StageOne;
|
||
|
d->active = false;
|
||
|
}
|
||
|
|
||
|
void Client::close()
|
||
|
{
|
||
|
d->active = false;
|
||
|
d->awayMsgRequestTimer->stop();
|
||
|
d->awayMsgRequestQueue.clear();
|
||
|
d->connections.clear();
|
||
|
deleteStaticTasks();
|
||
|
|
||
|
//don't clear the stored status between stage one and two
|
||
|
if ( d->stage == ClientPrivate::StageTwo )
|
||
|
{
|
||
|
d->connectAsStatus = 0x0;
|
||
|
d->connectWithMessage = QString::null;
|
||
|
}
|
||
|
|
||
|
d->exchanges.clear();
|
||
|
d->redirectRequested = false;
|
||
|
d->currentRedirect = 0;
|
||
|
d->redirectionServices.clear();
|
||
|
d->ssiManager->clear();
|
||
|
}
|
||
|
|
||
|
void Client::setStatus( AIMStatus status, const QString &_message )
|
||
|
{
|
||
|
// AIM: you're away exactly when your away message isn't empty.
|
||
|
// can't use QString::null as a message either; ProfileTask
|
||
|
// interprets null as "don't change".
|
||
|
QString message;
|
||
|
if ( status == Online )
|
||
|
message = QString::fromAscii("");
|
||
|
else
|
||
|
{
|
||
|
if ( _message.isEmpty() )
|
||
|
message = QString::fromAscii(" ");
|
||
|
else
|
||
|
message = _message;
|
||
|
}
|
||
|
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0002 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
ProfileTask* pt = new ProfileTask( c->rootTask() );
|
||
|
pt->setAwayMessage( message );
|
||
|
pt->go( true );
|
||
|
}
|
||
|
|
||
|
void Client::setStatus( DWORD status, const QString &message )
|
||
|
{
|
||
|
// remember the message to reply with, when requested
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Setting status message to "<< message << endl;
|
||
|
d->statusMessage = message;
|
||
|
// ICQ: if we're active, set status. otherwise, just store the status for later.
|
||
|
if ( d->active )
|
||
|
{
|
||
|
//the first connection is always the BOS connection
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0013 );
|
||
|
if ( !c )
|
||
|
return; //TODO trigger an error of some sort?
|
||
|
|
||
|
ChangeVisibilityTask* cvt = new ChangeVisibilityTask( c->rootTask() );
|
||
|
if ( ( status & 0x0100 ) == 0x0100 )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Setting invisible" << endl;
|
||
|
cvt->setVisible( false );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Setting visible" << endl;
|
||
|
cvt->setVisible( true );
|
||
|
}
|
||
|
cvt->go( true );
|
||
|
c = d->connections.connectionForFamily( 0x0002 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
SendDCInfoTask* sdcit = new SendDCInfoTask( c->rootTask(), status );
|
||
|
sdcit->go( true ); //autodelete
|
||
|
// TODO: send away message
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->connectAsStatus = status;
|
||
|
d->connectWithMessage = message;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UserDetails Client::ourInfo() const
|
||
|
{
|
||
|
return d->ourDetails;
|
||
|
}
|
||
|
|
||
|
QString Client::host()
|
||
|
{
|
||
|
return d->host;
|
||
|
}
|
||
|
|
||
|
int Client::port()
|
||
|
{
|
||
|
return d->port;
|
||
|
}
|
||
|
|
||
|
SSIManager* Client::ssiManager() const
|
||
|
{
|
||
|
return d->ssiManager;
|
||
|
}
|
||
|
|
||
|
const Oscar::ClientVersion* Client::version() const
|
||
|
{
|
||
|
return d->version;
|
||
|
}
|
||
|
|
||
|
// SLOTS //
|
||
|
|
||
|
void Client::streamConnected()
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
|
||
|
d->stage = ClientPrivate::StageTwo;
|
||
|
if ( m_loginTaskTwo )
|
||
|
m_loginTaskTwo->go();
|
||
|
}
|
||
|
|
||
|
void Client::lt_loginFinished()
|
||
|
{
|
||
|
/* Check for stage two login first, since we create the stage two
|
||
|
* task when we finish stage one
|
||
|
*/
|
||
|
if ( d->stage == ClientPrivate::StageTwo )
|
||
|
{
|
||
|
//we've finished logging in. start the services setup
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "stage two done. setting up services" << endl;
|
||
|
initializeStaticTasks();
|
||
|
ServiceSetupTask* ssTask = new ServiceSetupTask( d->connections.defaultConnection()->rootTask() );
|
||
|
connect( ssTask, SIGNAL( finished() ), this, SLOT( serviceSetupFinished() ) );
|
||
|
ssTask->go( true ); //fire and forget
|
||
|
m_loginTaskTwo->deleteLater();
|
||
|
m_loginTaskTwo = 0;
|
||
|
}
|
||
|
else if ( d->stage == ClientPrivate::StageOne )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "stage one login done" << endl;
|
||
|
disconnect( m_loginTask, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) );
|
||
|
|
||
|
if ( m_loginTask->statusCode() == 0 ) //we can start stage two
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "no errors from stage one. moving to stage two" << endl;
|
||
|
|
||
|
//cache these values since they'll be deleted when we close the connections (which deletes the tasks)
|
||
|
d->host = m_loginTask->bosServer();
|
||
|
d->port = m_loginTask->bosPort().toUInt();
|
||
|
d->cookie = m_loginTask->loginCookie();
|
||
|
close();
|
||
|
QTimer::singleShot( 100, this, SLOT(startStageTwo() ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "errors reported. not moving to stage two" << endl;
|
||
|
close(); //deletes the connections for us
|
||
|
}
|
||
|
|
||
|
m_loginTask->deleteLater();
|
||
|
m_loginTask = 0;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
void Client::startStageTwo()
|
||
|
{
|
||
|
//create a new connection and set it up
|
||
|
Connection* c = createConnection( d->host, QString::number( d->port ) );
|
||
|
new CloseConnectionTask( c->rootTask() );
|
||
|
|
||
|
//create the new login task
|
||
|
m_loginTaskTwo = new StageTwoLoginTask( c->rootTask() );
|
||
|
m_loginTaskTwo->setCookie( d->cookie );
|
||
|
QObject::connect( m_loginTaskTwo, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) );
|
||
|
|
||
|
|
||
|
//connect
|
||
|
QObject::connect( c, SIGNAL( connected() ), this, SLOT( streamConnected() ) );
|
||
|
connectToServer( c, d->host, false ) ;
|
||
|
|
||
|
}
|
||
|
|
||
|
void Client::serviceSetupFinished()
|
||
|
{
|
||
|
d->active = true;
|
||
|
|
||
|
if ( isIcq() )
|
||
|
setStatus( d->connectAsStatus, d->connectWithMessage );
|
||
|
|
||
|
d->ownStatusTask->go();
|
||
|
|
||
|
if ( isIcq() )
|
||
|
{
|
||
|
//retrieve offline messages
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0015 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
OfflineMessagesTask *offlineMsgTask = new OfflineMessagesTask( c->rootTask() );
|
||
|
connect( offlineMsgTask, SIGNAL( receivedOfflineMessage(const Oscar::Message& ) ),
|
||
|
this, SIGNAL( messageReceived(const Oscar::Message& ) ) );
|
||
|
offlineMsgTask->go( true );
|
||
|
}
|
||
|
|
||
|
emit haveSSIList();
|
||
|
emit loggedIn();
|
||
|
}
|
||
|
|
||
|
void Client::receivedIcqInfo( const QString& contact, unsigned int type )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "received icq info for " << contact
|
||
|
<< " of type " << type << endl;
|
||
|
|
||
|
if ( type == ICQUserInfoRequestTask::Short )
|
||
|
emit receivedIcqShortInfo( contact );
|
||
|
else
|
||
|
emit receivedIcqLongInfo( contact );
|
||
|
}
|
||
|
|
||
|
void Client::receivedInfo( Q_UINT16 sequence )
|
||
|
{
|
||
|
UserDetails details = d->userInfoTask->getInfoFor( sequence );
|
||
|
emit receivedUserInfo( details.userId(), details );
|
||
|
}
|
||
|
|
||
|
void Client::offlineUser( const QString& user, const UserDetails& )
|
||
|
{
|
||
|
emit userIsOffline( user );
|
||
|
}
|
||
|
|
||
|
void Client::haveOwnUserInfo()
|
||
|
{
|
||
|
kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << endl;
|
||
|
UserDetails ud = d->ownStatusTask->getInfo();
|
||
|
d->ourDetails = ud;
|
||
|
emit haveOwnInfo();
|
||
|
}
|
||
|
|
||
|
void Client::setCodecProvider( Client::CodecProvider* codecProvider )
|
||
|
{
|
||
|
d->codecProvider = codecProvider;
|
||
|
}
|
||
|
|
||
|
void Client::setVersion( const Oscar::ClientVersion* version )
|
||
|
{
|
||
|
d->version = version;
|
||
|
}
|
||
|
|
||
|
// INTERNALS //
|
||
|
|
||
|
QString Client::userId() const
|
||
|
{
|
||
|
return d->user;
|
||
|
}
|
||
|
|
||
|
QString Client::password() const
|
||
|
{
|
||
|
return d->pass;
|
||
|
}
|
||
|
|
||
|
QString Client::statusMessage() const
|
||
|
{
|
||
|
return d->statusMessage;
|
||
|
}
|
||
|
|
||
|
void Client::setStatusMessage( const QString &message )
|
||
|
{
|
||
|
d->statusMessage = message;
|
||
|
}
|
||
|
|
||
|
QCString Client::ipAddress() const
|
||
|
{
|
||
|
//!TODO determine ip address
|
||
|
return "127.0.0.1";
|
||
|
}
|
||
|
|
||
|
void Client::notifyTaskError( const Oscar::SNAC& s, int errCode, bool fatal )
|
||
|
{
|
||
|
emit taskError( s, errCode, fatal );
|
||
|
}
|
||
|
|
||
|
void Client::notifySocketError( int errCode, const QString& msg )
|
||
|
{
|
||
|
emit socketError( errCode, msg );
|
||
|
}
|
||
|
|
||
|
void Client::sendMessage( const Oscar::Message& msg, bool isAuto)
|
||
|
{
|
||
|
Connection* c = 0L;
|
||
|
if ( msg.type() == 0x0003 )
|
||
|
{
|
||
|
c = d->connections.connectionForChatRoom( msg.exchange(), msg.chatRoom() );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending message to chat room" << endl;
|
||
|
ChatServiceTask* cst = new ChatServiceTask( c->rootTask(), msg.exchange(), msg.chatRoom() );
|
||
|
cst->setMessage( msg );
|
||
|
cst->setEncoding( d->codecProvider->codecForAccount()->name() );
|
||
|
cst->go( true );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
c = d->connections.connectionForFamily( 0x0004 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
SendMessageTask *sendMsgTask = new SendMessageTask( c->rootTask() );
|
||
|
// Set whether or not the message is an automated response
|
||
|
sendMsgTask->setAutoResponse( isAuto );
|
||
|
sendMsgTask->setMessage( msg );
|
||
|
sendMsgTask->go( true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Client::receivedMessage( const Oscar::Message& msg )
|
||
|
{
|
||
|
if ( msg.type() == 2 && !msg.hasProperty( Oscar::Message::AutoResponse ) )
|
||
|
{
|
||
|
// type 2 message needs an autoresponse, regardless of type
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0004 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
Oscar::Message response ( msg );
|
||
|
if ( msg.hasProperty( Oscar::Message::StatusMessageRequest ) )
|
||
|
{
|
||
|
QTextCodec* codec = d->codecProvider->codecForContact( msg.sender() );
|
||
|
response.setText( Oscar::Message::UserDefined, statusMessage(), codec );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
response.setEncoding( Oscar::Message::UserDefined );
|
||
|
response.setTextArray( QByteArray() );
|
||
|
}
|
||
|
response.setReceiver( msg.sender() );
|
||
|
response.addProperty( Oscar::Message::AutoResponse );
|
||
|
SendMessageTask *sendMsgTask = new SendMessageTask( c->rootTask() );
|
||
|
sendMsgTask->setMessage( response );
|
||
|
sendMsgTask->go( true );
|
||
|
}
|
||
|
if ( msg.hasProperty( Oscar::Message::StatusMessageRequest ) )
|
||
|
{
|
||
|
if ( msg.hasProperty( Oscar::Message::AutoResponse ) )
|
||
|
{
|
||
|
// we got a response to a status message request.
|
||
|
QString awayMessage( msg.text( d->codecProvider->codecForContact( msg.sender() ) ) );
|
||
|
kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received an away message: " << awayMessage << endl;
|
||
|
emit receivedAwayMessage( msg.sender(), awayMessage );
|
||
|
}
|
||
|
}
|
||
|
else if ( ! msg.hasProperty( Oscar::Message::AutoResponse ) )
|
||
|
{
|
||
|
// Filter out miranda's invisible check
|
||
|
if ( msg.messageType() == 0x0004 && msg.textArray().isEmpty() )
|
||
|
return;
|
||
|
|
||
|
// let application handle it
|
||
|
kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Emitting receivedMessage" << endl;
|
||
|
emit messageReceived( msg );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Client::requestAuth( const QString& contactid, const QString& reason )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0013 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
d->ssiAuthTask->sendAuthRequest( contactid, reason );
|
||
|
}
|
||
|
|
||
|
void Client::sendAuth( const QString& contactid, const QString& reason, bool auth )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0013 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
d->ssiAuthTask->sendAuthReply( contactid, reason, auth );
|
||
|
}
|
||
|
|
||
|
bool Client::isActive() const
|
||
|
{
|
||
|
return d->active;
|
||
|
}
|
||
|
|
||
|
bool Client::isIcq() const
|
||
|
{
|
||
|
return d->isIcq;
|
||
|
}
|
||
|
|
||
|
void Client::setIsIcq( bool isIcq )
|
||
|
{
|
||
|
d->isIcq = isIcq;
|
||
|
}
|
||
|
|
||
|
void Client::debug( const QString& str )
|
||
|
{
|
||
|
Q_UNUSED(str);
|
||
|
// qDebug( "CLIENT: %s", str.ascii() );
|
||
|
}
|
||
|
|
||
|
void Client::initializeStaticTasks()
|
||
|
{
|
||
|
//set up the extra tasks
|
||
|
Connection* c = d->connections.defaultConnection();
|
||
|
if ( !c )
|
||
|
return;
|
||
|
d->errorTask = new ErrorTask( c->rootTask() );
|
||
|
d->onlineNotifier = new OnlineNotifierTask( c->rootTask() );
|
||
|
d->ownStatusTask = new OwnUserInfoTask( c->rootTask() );
|
||
|
d->messageReceiverTask = new MessageReceiverTask( c->rootTask() );
|
||
|
d->ssiAuthTask = new SSIAuthTask( c->rootTask() );
|
||
|
d->icqInfoTask = new ICQUserInfoRequestTask( c->rootTask() );
|
||
|
d->userInfoTask = new UserInfoTask( c->rootTask() );
|
||
|
d->typingNotifyTask = new TypingNotifyTask( c->rootTask() );
|
||
|
d->ssiModifyTask = new SSIModifyTask( c->rootTask(), true );
|
||
|
|
||
|
connect( d->onlineNotifier, SIGNAL( userIsOnline( const QString&, const UserDetails& ) ),
|
||
|
this, SIGNAL( receivedUserInfo( const QString&, const UserDetails& ) ) );
|
||
|
connect( d->onlineNotifier, SIGNAL( userIsOffline( const QString&, const UserDetails& ) ),
|
||
|
this, SLOT( offlineUser( const QString&, const UserDetails & ) ) );
|
||
|
|
||
|
connect( d->ownStatusTask, SIGNAL( gotInfo() ), this, SLOT( haveOwnUserInfo() ) );
|
||
|
connect( d->ownStatusTask, SIGNAL( buddyIconUploadRequested() ), this,
|
||
|
SIGNAL( iconNeedsUploading() ) );
|
||
|
|
||
|
connect( d->messageReceiverTask, SIGNAL( receivedMessage( const Oscar::Message& ) ),
|
||
|
this, SLOT( receivedMessage( const Oscar::Message& ) ) );
|
||
|
|
||
|
connect( d->ssiAuthTask, SIGNAL( authRequested( const QString&, const QString& ) ),
|
||
|
this, SIGNAL( authRequestReceived( const QString&, const QString& ) ) );
|
||
|
connect( d->ssiAuthTask, SIGNAL( authReplied( const QString&, const QString&, bool ) ),
|
||
|
this, SIGNAL( authReplyReceived( const QString&, const QString&, bool ) ) );
|
||
|
|
||
|
connect( d->icqInfoTask, SIGNAL( receivedInfoFor( const QString&, unsigned int ) ),
|
||
|
this, SLOT( receivedIcqInfo( const QString&, unsigned int ) ) );
|
||
|
|
||
|
connect( d->userInfoTask, SIGNAL( receivedProfile( const QString&, const QString& ) ),
|
||
|
this, SIGNAL( receivedProfile( const QString&, const QString& ) ) );
|
||
|
connect( d->userInfoTask, SIGNAL( receivedAwayMessage( const QString&, const QString& ) ),
|
||
|
this, SIGNAL( receivedAwayMessage( const QString&, const QString& ) ) );
|
||
|
connect( d->typingNotifyTask, SIGNAL( typingStarted( const QString& ) ),
|
||
|
this, SIGNAL( userStartedTyping( const QString& ) ) );
|
||
|
connect( d->typingNotifyTask, SIGNAL( typingFinished( const QString& ) ),
|
||
|
this, SIGNAL( userStoppedTyping( const QString& ) ) );
|
||
|
}
|
||
|
|
||
|
void Client::removeGroup( const QString& groupName )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0013 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Removing group " << groupName << " from SSI" << endl;
|
||
|
SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
|
||
|
if ( ssimt->removeGroup( groupName ) )
|
||
|
ssimt->go( true );
|
||
|
else
|
||
|
delete ssimt;
|
||
|
}
|
||
|
|
||
|
void Client::addGroup( const QString& groupName )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0013 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding group " << groupName << " to SSI" << endl;
|
||
|
SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
|
||
|
if ( ssimt->addGroup( groupName ) )
|
||
|
ssimt->go( true );
|
||
|
else
|
||
|
delete ssimt;
|
||
|
}
|
||
|
|
||
|
void Client::addContact( const QString& contactName, const QString& groupName )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0013 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding contact " << contactName << " to SSI in group " << groupName << endl;
|
||
|
SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
|
||
|
if ( ssimt->addContact( contactName, groupName ) )
|
||
|
ssimt->go( true );
|
||
|
else
|
||
|
delete ssimt;
|
||
|
}
|
||
|
|
||
|
void Client::removeContact( const QString& contactName )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0013 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Removing contact " << contactName << " from SSI" << endl;
|
||
|
SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
|
||
|
if ( ssimt->removeContact( contactName ) )
|
||
|
ssimt->go( true );
|
||
|
else
|
||
|
delete ssimt;
|
||
|
}
|
||
|
|
||
|
void Client::renameGroup( const QString & oldGroupName, const QString & newGroupName )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0013 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Renaming group " << oldGroupName << " to " << newGroupName << endl;
|
||
|
SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
|
||
|
if ( ssimt->renameGroup( oldGroupName, newGroupName ) )
|
||
|
ssimt->go( true );
|
||
|
else
|
||
|
delete ssimt;
|
||
|
}
|
||
|
|
||
|
void Client::modifySSIItem( const Oscar::SSI& oldItem, const Oscar::SSI& newItem )
|
||
|
{
|
||
|
int action = 0; //0 modify, 1 add, 2 remove TODO cleanup!
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0013 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
if ( !oldItem && newItem )
|
||
|
action = 1;
|
||
|
if ( oldItem && !newItem )
|
||
|
action = 2;
|
||
|
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Add/Mod/Del item on server" << endl;
|
||
|
SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
|
||
|
switch ( action )
|
||
|
{
|
||
|
case 0:
|
||
|
if ( ssimt->modifyItem( oldItem, newItem ) )
|
||
|
ssimt->go( true );
|
||
|
else
|
||
|
delete ssimt;
|
||
|
break;
|
||
|
case 1:
|
||
|
if ( ssimt->addItem( newItem ) )
|
||
|
ssimt->go( true );
|
||
|
else
|
||
|
delete ssimt;
|
||
|
break;
|
||
|
case 2:
|
||
|
if ( ssimt->removeItem( oldItem ) )
|
||
|
ssimt->go( true );
|
||
|
else
|
||
|
delete ssimt;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Client::changeContactGroup( const QString& contact, const QString& newGroupName )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0013 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Changing " << contact << "'s group to "
|
||
|
<< newGroupName << endl;
|
||
|
SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
|
||
|
if ( ssimt->changeGroup( contact, newGroupName ) )
|
||
|
ssimt->go( true );
|
||
|
else
|
||
|
delete ssimt;
|
||
|
}
|
||
|
|
||
|
void Client::requestFullInfo( const QString& contactId )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0015 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
d->icqInfoTask->setUser( contactId );
|
||
|
d->icqInfoTask->setType( ICQUserInfoRequestTask::Long );
|
||
|
d->icqInfoTask->go();
|
||
|
}
|
||
|
|
||
|
void Client::requestShortInfo( const QString& contactId )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0015 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
d->icqInfoTask->setUser( contactId );
|
||
|
d->icqInfoTask->setType( ICQUserInfoRequestTask::Short );
|
||
|
d->icqInfoTask->go();
|
||
|
}
|
||
|
|
||
|
void Client::sendWarning( const QString& contact, bool anonymous )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0004 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
WarningTask* warnTask = new WarningTask( c->rootTask() );
|
||
|
warnTask->setContact( contact );
|
||
|
warnTask->setAnonymous( anonymous );
|
||
|
QObject::connect( warnTask, SIGNAL( userWarned( const QString&, Q_UINT16, Q_UINT16 ) ),
|
||
|
this, SIGNAL( userWarned( const QString&, Q_UINT16, Q_UINT16 ) ) );
|
||
|
warnTask->go( true );
|
||
|
}
|
||
|
|
||
|
ICQGeneralUserInfo Client::getGeneralInfo( const QString& contact )
|
||
|
{
|
||
|
return d->icqInfoTask->generalInfoFor( contact );
|
||
|
}
|
||
|
|
||
|
ICQWorkUserInfo Client::getWorkInfo( const QString& contact )
|
||
|
{
|
||
|
return d->icqInfoTask->workInfoFor( contact );
|
||
|
}
|
||
|
|
||
|
ICQEmailInfo Client::getEmailInfo( const QString& contact )
|
||
|
{
|
||
|
return d->icqInfoTask->emailInfoFor( contact );
|
||
|
}
|
||
|
|
||
|
ICQMoreUserInfo Client::getMoreInfo( const QString& contact )
|
||
|
{
|
||
|
return d->icqInfoTask->moreInfoFor( contact );
|
||
|
}
|
||
|
|
||
|
ICQInterestInfo Client::getInterestInfo( const QString& contact )
|
||
|
{
|
||
|
return d->icqInfoTask->interestInfoFor( contact );
|
||
|
}
|
||
|
|
||
|
ICQShortInfo Client::getShortInfo( const QString& contact )
|
||
|
{
|
||
|
return d->icqInfoTask->shortInfoFor( contact );
|
||
|
}
|
||
|
|
||
|
QValueList<int> Client::chatExchangeList() const
|
||
|
{
|
||
|
return d->exchanges;
|
||
|
}
|
||
|
|
||
|
void Client::setChatExchangeList( const QValueList<int>& exchanges )
|
||
|
{
|
||
|
d->exchanges = exchanges;
|
||
|
}
|
||
|
|
||
|
void Client::requestAIMProfile( const QString& contact )
|
||
|
{
|
||
|
d->userInfoTask->requestInfoFor( contact, UserInfoTask::Profile );
|
||
|
}
|
||
|
|
||
|
void Client::requestAIMAwayMessage( const QString& contact )
|
||
|
{
|
||
|
d->userInfoTask->requestInfoFor( contact, UserInfoTask::AwayMessage );
|
||
|
}
|
||
|
|
||
|
void Client::requestICQAwayMessage( const QString& contact, ICQStatus contactStatus )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "requesting away message for " << contact << endl;
|
||
|
Oscar::Message msg;
|
||
|
msg.setType( 2 );
|
||
|
msg.setReceiver( contact );
|
||
|
msg.addProperty( Oscar::Message::StatusMessageRequest );
|
||
|
switch ( contactStatus )
|
||
|
{
|
||
|
case ICQAway:
|
||
|
msg.setMessageType( 0xE8 ); // away
|
||
|
break;
|
||
|
case ICQOccupied:
|
||
|
msg.setMessageType( 0xE9 ); // occupied
|
||
|
break;
|
||
|
case ICQNotAvailable:
|
||
|
msg.setMessageType( 0xEA ); // not awailable
|
||
|
break;
|
||
|
case ICQDoNotDisturb:
|
||
|
msg.setMessageType( 0xEB ); // do not disturb
|
||
|
break;
|
||
|
case ICQFreeForChat:
|
||
|
msg.setMessageType( 0xEC ); // free for chat
|
||
|
break;
|
||
|
default:
|
||
|
// may be a good way to deal with possible error and lack of online status message?
|
||
|
emit receivedAwayMessage( contact, "Sorry, this protocol does not support this type of status message" );
|
||
|
return;
|
||
|
}
|
||
|
sendMessage( msg );
|
||
|
}
|
||
|
|
||
|
void Client::addICQAwayMessageRequest( const QString& contact, ICQStatus contactStatus )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "adding away message request for "
|
||
|
<< contact << " to queue" << endl;
|
||
|
|
||
|
//remove old request if still exists
|
||
|
removeICQAwayMessageRequest( contact );
|
||
|
|
||
|
ClientPrivate::AwayMsgRequest amr = { contact, contactStatus };
|
||
|
d->awayMsgRequestQueue.prepend( amr );
|
||
|
|
||
|
if ( !d->awayMsgRequestTimer->isActive() )
|
||
|
d->awayMsgRequestTimer->start( 1000 );
|
||
|
}
|
||
|
|
||
|
void Client::removeICQAwayMessageRequest( const QString& contact )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "removing away message request for "
|
||
|
<< contact << " from queue" << endl;
|
||
|
|
||
|
QValueList<ClientPrivate::AwayMsgRequest>::iterator it = d->awayMsgRequestQueue.begin();
|
||
|
while ( it != d->awayMsgRequestQueue.end() )
|
||
|
{
|
||
|
if ( (*it).contact == contact )
|
||
|
it = d->awayMsgRequestQueue.erase( it );
|
||
|
else
|
||
|
it++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Client::nextICQAwayMessageRequest()
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "request queue count " << d->awayMsgRequestQueue.count() << endl;
|
||
|
|
||
|
if ( d->awayMsgRequestQueue.empty() )
|
||
|
{
|
||
|
d->awayMsgRequestTimer->stop();
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0004 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
SNAC s = { 0x0004, 0x0006, 0x0000, 0x00000000 };
|
||
|
//get time needed to restore level to initial
|
||
|
//for some reason when we are long under initial level
|
||
|
//icq server will start to block our messages
|
||
|
int time = c->rateManager()->timeToInitialLevel( s );
|
||
|
if ( time > 0 )
|
||
|
{
|
||
|
d->awayMsgRequestTimer->changeInterval( time );
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
d->awayMsgRequestTimer->changeInterval( 5000 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ClientPrivate::AwayMsgRequest amr;
|
||
|
|
||
|
amr = d->awayMsgRequestQueue.back();
|
||
|
d->awayMsgRequestQueue.pop_back();
|
||
|
requestICQAwayMessage( amr.contact, amr.contactStatus );
|
||
|
}
|
||
|
|
||
|
void Client::requestStatusInfo( const QString& contact )
|
||
|
{
|
||
|
d->userInfoTask->requestInfoFor( contact, UserInfoTask::General );
|
||
|
}
|
||
|
|
||
|
void Client::whitePagesSearch( const ICQWPSearchInfo& info )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0015 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
UserSearchTask* ust = new UserSearchTask( c->rootTask() );
|
||
|
connect( ust, SIGNAL( foundUser( const ICQSearchResult& ) ),
|
||
|
this, SIGNAL( gotSearchResults( const ICQSearchResult& ) ) );
|
||
|
connect( ust, SIGNAL( searchFinished( int ) ), this, SIGNAL( endOfSearch( int ) ) );
|
||
|
ust->go( true ); //onGo does nothing in this task. This is just here so autodelete works
|
||
|
ust->searchWhitePages( info );
|
||
|
}
|
||
|
|
||
|
void Client::uinSearch( const QString& uin )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0015 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
UserSearchTask* ust = new UserSearchTask( c->rootTask() );
|
||
|
connect( ust, SIGNAL( foundUser( const ICQSearchResult& ) ),
|
||
|
this, SIGNAL( gotSearchResults( const ICQSearchResult& ) ) );
|
||
|
connect( ust, SIGNAL( searchFinished( int ) ), this, SIGNAL( endOfSearch( int ) ) );
|
||
|
ust->go( true ); //onGo does nothing in this task. This is just here so autodelete works
|
||
|
ust->searchUserByUIN( uin );
|
||
|
}
|
||
|
|
||
|
void Client::updateProfile( const QString& profile )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0002 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
ProfileTask* pt = new ProfileTask( c->rootTask() );
|
||
|
pt->setProfileText( profile );
|
||
|
pt->go(true);
|
||
|
}
|
||
|
|
||
|
void Client::sendTyping( const QString & contact, bool typing )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0004 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
d->typingNotifyTask->setParams( contact, ( typing ? TypingNotifyTask::Begin : TypingNotifyTask::Finished ) );
|
||
|
d->typingNotifyTask->go( false ); // don't delete the task after sending
|
||
|
}
|
||
|
|
||
|
void Client::connectToIconServer()
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0010 );
|
||
|
if ( c )
|
||
|
return;
|
||
|
|
||
|
requestServerRedirect( 0x0010 );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void Client::setIgnore( const QString& user, bool ignore )
|
||
|
{
|
||
|
Oscar::SSI item = ssiManager()->findItem( user, ROSTER_IGNORE );
|
||
|
if ( item && !ignore )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << user << " from ignore list" << endl;
|
||
|
this->modifySSIItem( item, Oscar::SSI() );
|
||
|
}
|
||
|
else if ( !item && ignore )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << user << " to ignore list" << endl;
|
||
|
Oscar::SSI s( user, 0, ssiManager()->nextContactId(), ROSTER_IGNORE, QValueList<TLV>() );
|
||
|
this->modifySSIItem( Oscar::SSI(), s );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Client::setVisibleTo( const QString& user, bool visible )
|
||
|
{
|
||
|
Oscar::SSI item = ssiManager()->findItem( user, ROSTER_VISIBLE );
|
||
|
if ( item && !visible )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << user << " from visible list" << endl;
|
||
|
this->modifySSIItem( item, Oscar::SSI() );
|
||
|
}
|
||
|
else if ( !item && visible )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << user << " to visible list" << endl;
|
||
|
Oscar::SSI s( user, 0, ssiManager()->nextContactId(), ROSTER_VISIBLE, QValueList<TLV>() );
|
||
|
this->modifySSIItem( Oscar::SSI(), s );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Client::setInvisibleTo( const QString& user, bool invisible )
|
||
|
{
|
||
|
Oscar::SSI item = ssiManager()->findItem( user, ROSTER_INVISIBLE );
|
||
|
if ( item && !invisible )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << user << " from invisible list" << endl;
|
||
|
this->modifySSIItem( item, Oscar::SSI() );
|
||
|
}
|
||
|
else if ( !item && invisible )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << user << " to invisible list" << endl;
|
||
|
Oscar::SSI s( user, 0, ssiManager()->nextContactId(), ROSTER_INVISIBLE, QValueList<TLV>() );
|
||
|
this->modifySSIItem( Oscar::SSI(), s );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Client::requestBuddyIcon( const QString& user, const QByteArray& hash, BYTE hashType )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0010 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
BuddyIconTask* bit = new BuddyIconTask( c->rootTask() );
|
||
|
connect( bit, SIGNAL( haveIcon( const QString&, QByteArray ) ),
|
||
|
this, SIGNAL( haveIconForContact( const QString&, QByteArray ) ) );
|
||
|
bit->requestIconFor( user );
|
||
|
bit->setHashType( hashType );
|
||
|
bit->setHash( hash );
|
||
|
bit->go( true );
|
||
|
}
|
||
|
|
||
|
void Client::requestServerRedirect( WORD family, WORD exchange,
|
||
|
QByteArray cookie, WORD instance,
|
||
|
const QString& room )
|
||
|
{
|
||
|
//making the assumption that family 2 will always be the BOS connection
|
||
|
//use it instead since we can't query for family 1
|
||
|
Connection* c = d->connections.connectionForFamily( family );
|
||
|
if ( c && family != 0x000E )
|
||
|
return; //we already have the connection
|
||
|
|
||
|
c = d->connections.connectionForFamily( 0x0002 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
if ( d->redirectionServices.findIndex( family ) == -1 )
|
||
|
d->redirectionServices.append( family ); //don't add families twice
|
||
|
|
||
|
if ( d->currentRedirect != 0 )
|
||
|
return; //we're already doing one redirection
|
||
|
|
||
|
d->currentRedirect = family;
|
||
|
|
||
|
//FIXME. this won't work if we have to defer the connection because we're
|
||
|
//already connecting to something
|
||
|
ServerRedirectTask* srt = new ServerRedirectTask( c->rootTask() );
|
||
|
if ( family == 0x000E )
|
||
|
{
|
||
|
srt->setChatParams( exchange, cookie, instance );
|
||
|
srt->setChatRoom( room );
|
||
|
}
|
||
|
|
||
|
connect( srt, SIGNAL( haveServer( const QString&, const QByteArray&, WORD ) ),
|
||
|
this, SLOT( haveServerForRedirect( const QString&, const QByteArray&, WORD ) ) );
|
||
|
srt->setService( family );
|
||
|
srt->go( true );
|
||
|
}
|
||
|
|
||
|
void Client::haveServerForRedirect( const QString& host, const QByteArray& cookie, WORD )
|
||
|
{
|
||
|
//nasty sender() usage to get the task with chat room info
|
||
|
QObject* o = const_cast<QObject*>( sender() );
|
||
|
ServerRedirectTask* srt = dynamic_cast<ServerRedirectTask*>( o );
|
||
|
|
||
|
//create a new connection and set it up
|
||
|
int colonPos = host.find(':');
|
||
|
QString realHost, realPort;
|
||
|
if ( colonPos != -1 )
|
||
|
{
|
||
|
realHost = host.left( colonPos );
|
||
|
realPort = host.right(4); //we only need 4 bytes
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
realHost = host;
|
||
|
realPort = QString::fromLatin1("5190");
|
||
|
}
|
||
|
|
||
|
Connection* c = createConnection( realHost, realPort );
|
||
|
//create the new login task
|
||
|
m_loginTaskTwo = new StageTwoLoginTask( c->rootTask() );
|
||
|
m_loginTaskTwo->setCookie( cookie );
|
||
|
QObject::connect( m_loginTaskTwo, SIGNAL( finished() ), this, SLOT( serverRedirectFinished() ) );
|
||
|
|
||
|
//connect
|
||
|
connectToServer( c, d->host, false );
|
||
|
QObject::connect( c, SIGNAL( connected() ), this, SLOT( streamConnected() ) );
|
||
|
|
||
|
if ( srt )
|
||
|
d->connections.addChatInfoForConnection( c, srt->chatExchange(), srt->chatRoomName() );
|
||
|
}
|
||
|
|
||
|
void Client::serverRedirectFinished()
|
||
|
{
|
||
|
if ( m_loginTaskTwo->statusCode() == 0 )
|
||
|
{ //stage two was successful
|
||
|
Connection* c = d->connections.connectionForFamily( d->currentRedirect );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
ClientReadyTask* crt = new ClientReadyTask( c->rootTask() );
|
||
|
crt->setFamilies( c->supportedFamilies() );
|
||
|
crt->go( true );
|
||
|
}
|
||
|
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "redirection finished for service "
|
||
|
<< d->currentRedirect << endl;
|
||
|
|
||
|
if ( d->currentRedirect == 0x0010 )
|
||
|
emit iconServerConnected();
|
||
|
|
||
|
if ( d->currentRedirect == 0x000D )
|
||
|
{
|
||
|
connect( this, SIGNAL( chatNavigationConnected() ),
|
||
|
this, SLOT( requestChatNavLimits() ) );
|
||
|
emit chatNavigationConnected();
|
||
|
}
|
||
|
|
||
|
if ( d->currentRedirect == 0x000E )
|
||
|
{
|
||
|
//HACK! such abuse! think of a better way
|
||
|
if ( !m_loginTaskTwo )
|
||
|
{
|
||
|
kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "no login task to get connection from!" << endl;
|
||
|
emit redirectionFinished( d->currentRedirect );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Connection* c = m_loginTaskTwo->client();
|
||
|
QString roomName = d->connections.chatRoomForConnection( c );
|
||
|
WORD exchange = d->connections.exchangeForConnection( c );
|
||
|
if ( c )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting up chat connection" << endl;
|
||
|
ChatServiceTask* cst = new ChatServiceTask( c->rootTask(), exchange, roomName );
|
||
|
connect( cst, SIGNAL( userJoinedChat( Oscar::WORD, const QString&, const QString& ) ),
|
||
|
this, SIGNAL( userJoinedChat( Oscar::WORD, const QString&, const QString& ) ) );
|
||
|
connect( cst, SIGNAL( userLeftChat( Oscar::WORD, const QString&, const QString& ) ),
|
||
|
this, SIGNAL( userLeftChat( Oscar::WORD, const QString&, const QString& ) ) );
|
||
|
connect( cst, SIGNAL( newChatMessage( const Oscar::Message& ) ),
|
||
|
this, SIGNAL( messageReceived( const Oscar::Message& ) ) );
|
||
|
}
|
||
|
emit chatRoomConnected( exchange, roomName );
|
||
|
}
|
||
|
|
||
|
emit redirectionFinished( d->currentRedirect );
|
||
|
|
||
|
}
|
||
|
|
||
|
void Client::checkRedirectionQueue( WORD family )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "checking redirection queue" << endl;
|
||
|
d->redirectionServices.remove( family );
|
||
|
d->currentRedirect = 0;
|
||
|
if ( !d->redirectionServices.isEmpty() )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "scheduling new redirection" << endl;
|
||
|
requestServerRedirect( d->redirectionServices.front() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Client::requestChatNavLimits()
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x000D );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "requesting chat nav service limits" << endl;
|
||
|
ChatNavServiceTask* cnst = new ChatNavServiceTask( c->rootTask() );
|
||
|
cnst->setRequestType( ChatNavServiceTask::Limits );
|
||
|
QObject::connect( cnst, SIGNAL( haveChatExchanges( const QValueList<int>& ) ),
|
||
|
this, SLOT( setChatExchangeList( const QValueList<int>& ) ) );
|
||
|
cnst->go( true ); //autodelete
|
||
|
|
||
|
}
|
||
|
|
||
|
void Client::determineDisconnection( int code, const QString& string )
|
||
|
{
|
||
|
if ( !sender() )
|
||
|
return;
|
||
|
|
||
|
//yay for the sender() hack!
|
||
|
QObject* obj = const_cast<QObject*>( sender() );
|
||
|
Connection* c = dynamic_cast<Connection*>( obj );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
if ( c->isSupported( 0x0002 ) ||
|
||
|
d->stage == ClientPrivate::StageOne ) //emit on login
|
||
|
{
|
||
|
emit socketError( code, string );
|
||
|
}
|
||
|
|
||
|
//connection is deleted. deleteLater() is used
|
||
|
d->connections.remove( c );
|
||
|
c = 0;
|
||
|
}
|
||
|
|
||
|
void Client::sendBuddyIcon( const QByteArray& iconData )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0010 );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "icon length is " << iconData.size() << endl;
|
||
|
BuddyIconTask* bit = new BuddyIconTask( c->rootTask() );
|
||
|
bit->uploadIcon( iconData.size(), iconData );
|
||
|
bit->go( true );
|
||
|
}
|
||
|
|
||
|
void Client::joinChatRoom( const QString& roomName, int exchange )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x000D );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "joining the chat room '" << roomName
|
||
|
<< "' on exchange " << exchange << endl;
|
||
|
ChatNavServiceTask* cnst = new ChatNavServiceTask( c->rootTask() );
|
||
|
connect( cnst, SIGNAL( connectChat( WORD, QByteArray, WORD, const QString& ) ),
|
||
|
this, SLOT( setupChatConnection( WORD, QByteArray, WORD, const QString& ) ) );
|
||
|
cnst->createRoom( exchange, roomName );
|
||
|
|
||
|
}
|
||
|
|
||
|
void Client::setupChatConnection( WORD exchange, QByteArray cookie, WORD instance, const QString& room )
|
||
|
{
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "cookie is:" << cookie << endl;
|
||
|
QByteArray realCookie( cookie );
|
||
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "connection to chat room" << endl;
|
||
|
requestServerRedirect( 0x000E, exchange, realCookie, instance, room );
|
||
|
}
|
||
|
|
||
|
void Client::disconnectChatRoom( WORD exchange, const QString& room )
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForChatRoom( exchange, room );
|
||
|
if ( !c )
|
||
|
return;
|
||
|
|
||
|
d->connections.remove( c );
|
||
|
c = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
Connection* Client::createConnection( const QString& host, const QString& port )
|
||
|
{
|
||
|
KNetworkConnector* knc = new KNetworkConnector( 0 );
|
||
|
knc->setOptHostPort( host, port.toUInt() );
|
||
|
ClientStream* cs = new ClientStream( knc, 0 );
|
||
|
cs->setNoopTime( 60000 );
|
||
|
Connection* c = new Connection( knc, cs, "BOS" );
|
||
|
cs->setConnection( c );
|
||
|
c->setClient( this );
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
void Client::deleteStaticTasks()
|
||
|
{
|
||
|
delete d->errorTask;
|
||
|
delete d->onlineNotifier;
|
||
|
delete d->ownStatusTask;
|
||
|
delete d->messageReceiverTask;
|
||
|
delete d->ssiAuthTask;
|
||
|
delete d->icqInfoTask;
|
||
|
delete d->userInfoTask;
|
||
|
delete d->typingNotifyTask;
|
||
|
delete d->ssiModifyTask;
|
||
|
|
||
|
d->errorTask = 0;
|
||
|
d->onlineNotifier = 0;
|
||
|
d->ownStatusTask = 0;
|
||
|
d->messageReceiverTask = 0;
|
||
|
d->ssiAuthTask = 0;
|
||
|
d->icqInfoTask = 0;
|
||
|
d->userInfoTask = 0;
|
||
|
d->typingNotifyTask = 0;
|
||
|
d->ssiModifyTask = 0;
|
||
|
}
|
||
|
|
||
|
bool Client::hasIconConnection( ) const
|
||
|
{
|
||
|
Connection* c = d->connections.connectionForFamily( 0x0010 );
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
#include "client.moc"
|
||
|
//kate: tab-width 4; indent-mode csands; space-indent off; replace-tabs off;
|