|
|
|
/*
|
|
|
|
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 <tqtimer.h>
|
|
|
|
#include <tqtextcodec.h>
|
|
|
|
|
|
|
|
#include <kdebug.h> //for kdDebug()
|
|
|
|
#include <tdelocale.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 TQTextCodec* codecForContact( const TQString& ) const
|
|
|
|
{
|
|
|
|
return TQTextCodec::codecForMib( 4 );
|
|
|
|
}
|
|
|
|
virtual TQTextCodec* codecForAccount() const
|
|
|
|
{
|
|
|
|
return TQTextCodec::codecForMib( 4 );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
DefaultCodecProvider defaultCodecProvider;
|
|
|
|
}
|
|
|
|
|
|
|
|
class Client::ClientPrivate
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ClientPrivate() {}
|
|
|
|
|
|
|
|
TQString host, user, pass;
|
|
|
|
uint port;
|
|
|
|
int tzoffset;
|
|
|
|
bool active;
|
|
|
|
|
|
|
|
enum { StageOne, StageTwo };
|
|
|
|
int stage;
|
|
|
|
|
|
|
|
//Protocol specific data
|
|
|
|
bool isIcq;
|
|
|
|
bool redirectRequested;
|
|
|
|
TQValueList<WORD> redirectionServices;
|
|
|
|
WORD currentRedirect;
|
|
|
|
TQByteArray cookie;
|
|
|
|
DWORD connectAsStatus; // icq only
|
|
|
|
TQString 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
|
|
|
|
TQValueList<int> exchanges;
|
|
|
|
|
|
|
|
TQString statusMessage; // for away-,DND-message etc...
|
|
|
|
|
|
|
|
//away messages
|
|
|
|
struct AwayMsgRequest
|
|
|
|
{
|
|
|
|
TQString contact;
|
|
|
|
ICQStatus contactStatus;
|
|
|
|
};
|
|
|
|
TQValueList<AwayMsgRequest> awayMsgRequestQueue;
|
|
|
|
TQTimer* awayMsgRequestTimer;
|
|
|
|
CodecProvider* codecProvider;
|
|
|
|
|
|
|
|
const Oscar::ClientVersion* version;
|
|
|
|
};
|
|
|
|
|
|
|
|
Client::Client( TQObject* parent )
|
|
|
|
:TQObject( 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 TQTimer();
|
|
|
|
d->codecProvider = &defaultCodecProvider;
|
|
|
|
|
|
|
|
connect( this, TQT_SIGNAL( redirectionFinished( WORD ) ),
|
|
|
|
this, TQT_SLOT( checkRedirectionQueue( WORD ) ) );
|
|
|
|
connect( d->awayMsgRequestTimer, TQT_SIGNAL( timeout() ),
|
|
|
|
this, TQT_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 TQString& server, bool auth )
|
|
|
|
{
|
|
|
|
d->connections.append( c );
|
|
|
|
if ( auth == true )
|
|
|
|
{
|
|
|
|
m_loginTask = new StageOneLoginTask( c->rootTask() );
|
|
|
|
connect( m_loginTask, TQT_SIGNAL( finished() ), this, TQT_SLOT( lt_loginFinished() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
connect( c, TQT_SIGNAL( socketError( int, const TQString& ) ), this, TQT_SLOT( determineDisconnection( int, const TQString& ) ) );
|
|
|
|
c->connectToServer(server, auth);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::start( const TQString &host, const uint port, const TQString &userId, const TQString &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 = TQString();
|
|
|
|
}
|
|
|
|
|
|
|
|
d->exchanges.clear();
|
|
|
|
d->redirectRequested = false;
|
|
|
|
d->currentRedirect = 0;
|
|
|
|
d->redirectionServices.clear();
|
|
|
|
d->ssiManager->clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::setStatus( AIMStatus status, const TQString &_message )
|
|
|
|
{
|
|
|
|
// AIM: you're away exactly when your away message isn't empty.
|
|
|
|
// can't use TQString() as a message either; ProfileTask
|
|
|
|
// interprets null as "don't change".
|
|
|
|
TQString message;
|
|
|
|
if ( status == Online )
|
|
|
|
message = TQString::fromAscii("");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( _message.isEmpty() )
|
|
|
|
message = TQString::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 TQString &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;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString 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, TQT_SIGNAL( finished() ), this, TQT_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, TQT_SIGNAL( finished() ), this, TQT_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();
|
|
|
|
TQTimer::singleShot( 100, this, TQT_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, TQString::number( d->port ) );
|
|
|
|
new CloseConnectionTask( c->rootTask() );
|
|
|
|
|
|
|
|
//create the new login task
|
|
|
|
m_loginTaskTwo = new StageTwoLoginTask( c->rootTask() );
|
|
|
|
m_loginTaskTwo->setCookie( d->cookie );
|
|
|
|
TQObject::connect( m_loginTaskTwo, TQT_SIGNAL( finished() ), this, TQT_SLOT( lt_loginFinished() ) );
|
|
|
|
|
|
|
|
|
|
|
|
//connect
|
|
|
|
TQObject::connect( c, TQT_SIGNAL( connected() ), this, TQT_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, TQT_SIGNAL( receivedOfflineMessage(const Oscar::Message& ) ),
|
|
|
|
this, TQT_SIGNAL( messageReceived(const Oscar::Message& ) ) );
|
|
|
|
offlineMsgTask->go( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
emit haveSSIList();
|
|
|
|
emit loggedIn();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::receivedIcqInfo( const TQString& 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( TQ_UINT16 sequence )
|
|
|
|
{
|
|
|
|
UserDetails details = d->userInfoTask->getInfoFor( sequence );
|
|
|
|
emit receivedUserInfo( details.userId(), details );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::offlineUser( const TQString& 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 //
|
|
|
|
|
|
|
|
TQString Client::userId() const
|
|
|
|
{
|
|
|
|
return d->user;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString Client::password() const
|
|
|
|
{
|
|
|
|
return d->pass;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString Client::statusMessage() const
|
|
|
|
{
|
|
|
|
return d->statusMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::setStatusMessage( const TQString &message )
|
|
|
|
{
|
|
|
|
d->statusMessage = message;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQCString 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 TQString& 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 ) )
|
|
|
|
{
|
|
|
|
TQTextCodec* codec = d->codecProvider->codecForContact( msg.sender() );
|
|
|
|
response.setText( Oscar::Message::UserDefined, statusMessage(), codec );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
response.setEncoding( Oscar::Message::UserDefined );
|
|
|
|
response.setTextArray( TQByteArray() );
|
|
|
|
}
|
|
|
|
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.
|
|
|
|
TQString 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 TQString& contactid, const TQString& reason )
|
|
|
|
{
|
|
|
|
Connection* c = d->connections.connectionForFamily( 0x0013 );
|
|
|
|
if ( !c )
|
|
|
|
return;
|
|
|
|
d->ssiAuthTask->sendAuthRequest( contactid, reason );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::sendAuth( const TQString& contactid, const TQString& 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 TQString& str )
|
|
|
|
{
|
|
|
|
Q_UNUSED(str);
|
|
|
|
// tqDebug( "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, TQT_SIGNAL( userIsOnline( const TQString&, const UserDetails& ) ),
|
|
|
|
this, TQT_SIGNAL( receivedUserInfo( const TQString&, const UserDetails& ) ) );
|
|
|
|
connect( d->onlineNotifier, TQT_SIGNAL( userIsOffline( const TQString&, const UserDetails& ) ),
|
|
|
|
this, TQT_SLOT( offlineUser( const TQString&, const UserDetails & ) ) );
|
|
|
|
|
|
|
|
connect( d->ownStatusTask, TQT_SIGNAL( gotInfo() ), this, TQT_SLOT( haveOwnUserInfo() ) );
|
|
|
|
connect( d->ownStatusTask, TQT_SIGNAL( buddyIconUploadRequested() ), this,
|
|
|
|
TQT_SIGNAL( iconNeedsUploading() ) );
|
|
|
|
|
|
|
|
connect( d->messageReceiverTask, TQT_SIGNAL( receivedMessage( const Oscar::Message& ) ),
|
|
|
|
this, TQT_SLOT( receivedMessage( const Oscar::Message& ) ) );
|
|
|
|
|
|
|
|
connect( d->ssiAuthTask, TQT_SIGNAL( authRequested( const TQString&, const TQString& ) ),
|
|
|
|
this, TQT_SIGNAL( authRequestReceived( const TQString&, const TQString& ) ) );
|
|
|
|
connect( d->ssiAuthTask, TQT_SIGNAL( authReplied( const TQString&, const TQString&, bool ) ),
|
|
|
|
this, TQT_SIGNAL( authReplyReceived( const TQString&, const TQString&, bool ) ) );
|
|
|
|
|
|
|
|
connect( d->icqInfoTask, TQT_SIGNAL( receivedInfoFor( const TQString&, unsigned int ) ),
|
|
|
|
this, TQT_SLOT( receivedIcqInfo( const TQString&, unsigned int ) ) );
|
|
|
|
|
|
|
|
connect( d->userInfoTask, TQT_SIGNAL( receivedProfile( const TQString&, const TQString& ) ),
|
|
|
|
this, TQT_SIGNAL( receivedProfile( const TQString&, const TQString& ) ) );
|
|
|
|
connect( d->userInfoTask, TQT_SIGNAL( receivedAwayMessage( const TQString&, const TQString& ) ),
|
|
|
|
this, TQT_SIGNAL( receivedAwayMessage( const TQString&, const TQString& ) ) );
|
|
|
|
connect( d->typingNotifyTask, TQT_SIGNAL( typingStarted( const TQString& ) ),
|
|
|
|
this, TQT_SIGNAL( userStartedTyping( const TQString& ) ) );
|
|
|
|
connect( d->typingNotifyTask, TQT_SIGNAL( typingFinished( const TQString& ) ),
|
|
|
|
this, TQT_SIGNAL( userStoppedTyping( const TQString& ) ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::removeGroup( const TQString& 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 TQString& 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 TQString& contactName, const TQString& 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 TQString& 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 TQString & oldGroupName, const TQString & 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 TQString& contact, const TQString& 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 TQString& 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 TQString& 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 TQString& contact, bool anonymous )
|
|
|
|
{
|
|
|
|
Connection* c = d->connections.connectionForFamily( 0x0004 );
|
|
|
|
if ( !c )
|
|
|
|
return;
|
|
|
|
WarningTask* warnTask = new WarningTask( c->rootTask() );
|
|
|
|
warnTask->setContact( contact );
|
|
|
|
warnTask->setAnonymous( anonymous );
|
|
|
|
TQObject::connect( warnTask, TQT_SIGNAL( userWarned( const TQString&, TQ_UINT16, TQ_UINT16 ) ),
|
|
|
|
this, TQT_SIGNAL( userWarned( const TQString&, TQ_UINT16, TQ_UINT16 ) ) );
|
|
|
|
warnTask->go( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
ICQGeneralUserInfo Client::getGeneralInfo( const TQString& contact )
|
|
|
|
{
|
|
|
|
return d->icqInfoTask->generalInfoFor( contact );
|
|
|
|
}
|
|
|
|
|
|
|
|
ICQWorkUserInfo Client::getWorkInfo( const TQString& contact )
|
|
|
|
{
|
|
|
|
return d->icqInfoTask->workInfoFor( contact );
|
|
|
|
}
|
|
|
|
|
|
|
|
ICQEmailInfo Client::getEmailInfo( const TQString& contact )
|
|
|
|
{
|
|
|
|
return d->icqInfoTask->emailInfoFor( contact );
|
|
|
|
}
|
|
|
|
|
|
|
|
ICQMoreUserInfo Client::getMoreInfo( const TQString& contact )
|
|
|
|
{
|
|
|
|
return d->icqInfoTask->moreInfoFor( contact );
|
|
|
|
}
|
|
|
|
|
|
|
|
ICQInterestInfo Client::getInterestInfo( const TQString& contact )
|
|
|
|
{
|
|
|
|
return d->icqInfoTask->interestInfoFor( contact );
|
|
|
|
}
|
|
|
|
|
|
|
|
ICQShortInfo Client::getShortInfo( const TQString& contact )
|
|
|
|
{
|
|
|
|
return d->icqInfoTask->shortInfoFor( contact );
|
|
|
|
}
|
|
|
|
|
|
|
|
TQValueList<int> Client::chatExchangeList() const
|
|
|
|
{
|
|
|
|
return d->exchanges;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::setChatExchangeList( const TQValueList<int>& exchanges )
|
|
|
|
{
|
|
|
|
d->exchanges = exchanges;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::requestAIMProfile( const TQString& contact )
|
|
|
|
{
|
|
|
|
d->userInfoTask->requestInfoFor( contact, UserInfoTask::Profile );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::requestAIMAwayMessage( const TQString& contact )
|
|
|
|
{
|
|
|
|
d->userInfoTask->requestInfoFor( contact, UserInfoTask::AwayMessage );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::requestICQAwayMessage( const TQString& 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 TQString& 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 TQString& contact )
|
|
|
|
{
|
|
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "removing away message request for "
|
|
|
|
<< contact << " from queue" << endl;
|
|
|
|
|
|
|
|
TQValueList<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 TQString& 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, TQT_SIGNAL( foundUser( const ICQSearchResult& ) ),
|
|
|
|
this, TQT_SIGNAL( gotSearchResults( const ICQSearchResult& ) ) );
|
|
|
|
connect( ust, TQT_SIGNAL( searchFinished( int ) ), this, TQT_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 TQString& uin )
|
|
|
|
{
|
|
|
|
Connection* c = d->connections.connectionForFamily( 0x0015 );
|
|
|
|
if ( !c )
|
|
|
|
return;
|
|
|
|
UserSearchTask* ust = new UserSearchTask( c->rootTask() );
|
|
|
|
connect( ust, TQT_SIGNAL( foundUser( const ICQSearchResult& ) ),
|
|
|
|
this, TQT_SIGNAL( gotSearchResults( const ICQSearchResult& ) ) );
|
|
|
|
connect( ust, TQT_SIGNAL( searchFinished( int ) ), this, TQT_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 TQString& 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 TQString & 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 TQString& 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, TQValueList<TLV>() );
|
|
|
|
this->modifySSIItem( Oscar::SSI(), s );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::setVisibleTo( const TQString& 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, TQValueList<TLV>() );
|
|
|
|
this->modifySSIItem( Oscar::SSI(), s );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::setInvisibleTo( const TQString& 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, TQValueList<TLV>() );
|
|
|
|
this->modifySSIItem( Oscar::SSI(), s );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::requestBuddyIcon( const TQString& user, const TQByteArray& hash, BYTE hashType )
|
|
|
|
{
|
|
|
|
Connection* c = d->connections.connectionForFamily( 0x0010 );
|
|
|
|
if ( !c )
|
|
|
|
return;
|
|
|
|
|
|
|
|
BuddyIconTask* bit = new BuddyIconTask( c->rootTask() );
|
|
|
|
connect( bit, TQT_SIGNAL( haveIcon( const TQString&, TQByteArray ) ),
|
|
|
|
this, TQT_SIGNAL( haveIconForContact( const TQString&, TQByteArray ) ) );
|
|
|
|
bit->requestIconFor( user );
|
|
|
|
bit->setHashType( hashType );
|
|
|
|
bit->setHash( hash );
|
|
|
|
bit->go( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::requestServerRedirect( WORD family, WORD exchange,
|
|
|
|
TQByteArray cookie, WORD instance,
|
|
|
|
const TQString& 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, TQT_SIGNAL( haveServer( const TQString&, const TQByteArray&, WORD ) ),
|
|
|
|
this, TQT_SLOT( haveServerForRedirect( const TQString&, const TQByteArray&, WORD ) ) );
|
|
|
|
srt->setService( family );
|
|
|
|
srt->go( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::haveServerForRedirect( const TQString& host, const TQByteArray& cookie, WORD )
|
|
|
|
{
|
|
|
|
//nasty sender() usage to get the task with chat room info
|
|
|
|
TQObject* o = TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>( sender() ));
|
|
|
|
ServerRedirectTask* srt = dynamic_cast<ServerRedirectTask*>( o );
|
|
|
|
|
|
|
|
//create a new connection and set it up
|
|
|
|
int colonPos = host.find(':');
|
|
|
|
TQString realHost, realPort;
|
|
|
|
if ( colonPos != -1 )
|
|
|
|
{
|
|
|
|
realHost = host.left( colonPos );
|
|
|
|
realPort = host.right(4); //we only need 4 bytes
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
realHost = host;
|
|
|
|
realPort = TQString::fromLatin1("5190");
|
|
|
|
}
|
|
|
|
|
|
|
|
Connection* c = createConnection( realHost, realPort );
|
|
|
|
//create the new login task
|
|
|
|
m_loginTaskTwo = new StageTwoLoginTask( c->rootTask() );
|
|
|
|
m_loginTaskTwo->setCookie( cookie );
|
|
|
|
TQObject::connect( m_loginTaskTwo, TQT_SIGNAL( finished() ), this, TQT_SLOT( serverRedirectFinished() ) );
|
|
|
|
|
|
|
|
//connect
|
|
|
|
connectToServer( c, d->host, false );
|
|
|
|
TQObject::connect( c, TQT_SIGNAL( connected() ), this, TQT_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, TQT_SIGNAL( chatNavigationConnected() ),
|
|
|
|
this, TQT_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();
|
|
|
|
TQString 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, TQT_SIGNAL( userJoinedChat( Oscar::WORD, const TQString&, const TQString& ) ),
|
|
|
|
this, TQT_SIGNAL( userJoinedChat( Oscar::WORD, const TQString&, const TQString& ) ) );
|
|
|
|
connect( cst, TQT_SIGNAL( userLeftChat( Oscar::WORD, const TQString&, const TQString& ) ),
|
|
|
|
this, TQT_SIGNAL( userLeftChat( Oscar::WORD, const TQString&, const TQString& ) ) );
|
|
|
|
connect( cst, TQT_SIGNAL( newChatMessage( const Oscar::Message& ) ),
|
|
|
|
this, TQT_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 );
|
|
|
|
TQObject::connect( cnst, TQT_SIGNAL( haveChatExchanges( const TQValueList<int>& ) ),
|
|
|
|
this, TQT_SLOT( setChatExchangeList( const TQValueList<int>& ) ) );
|
|
|
|
cnst->go( true ); //autodelete
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::determineDisconnection( int code, const TQString& string )
|
|
|
|
{
|
|
|
|
if ( !sender() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
//yay for the sender() hack!
|
|
|
|
TQObject* obj = TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>( 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 TQByteArray& 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 TQString& 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, TQT_SIGNAL( connectChat( WORD, TQByteArray, WORD, const TQString& ) ),
|
|
|
|
this, TQT_SLOT( setupChatConnection( WORD, TQByteArray, WORD, const TQString& ) ) );
|
|
|
|
cnst->createRoom( exchange, roomName );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Client::setupChatConnection( WORD exchange, TQByteArray cookie, WORD instance, const TQString& room )
|
|
|
|
{
|
|
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "cookie is:" << cookie << endl;
|
|
|
|
TQByteArray 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 TQString& room )
|
|
|
|
{
|
|
|
|
Connection* c = d->connections.connectionForChatRoom( exchange, room );
|
|
|
|
if ( !c )
|
|
|
|
return;
|
|
|
|
|
|
|
|
d->connections.remove( c );
|
|
|
|
c = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Connection* Client::createConnection( const TQString& host, const TQString& 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;
|