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.
360 lines
11 KiB
360 lines
11 KiB
// Kopete Oscar Protocol - chat service task
|
|
|
|
// Copyright (C) 2005 Matt Rogers <mattr@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.1 of the License, or (at your option) any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
// 02110-1301 USA
|
|
|
|
|
|
#include "chatservicetask.h"
|
|
|
|
#include <qstring.h>
|
|
#include <kapplication.h>
|
|
#include <kdebug.h>
|
|
#include <qtextcodec.h>
|
|
|
|
#include "connection.h"
|
|
#include "transfer.h"
|
|
#include "buffer.h"
|
|
#include "oscartypes.h"
|
|
|
|
ChatServiceTask::ChatServiceTask( Task* parent, Oscar::WORD exchange, const QString& room )
|
|
: Task( parent ), m_encoding( "us-ascii" )
|
|
{
|
|
m_exchange = exchange;
|
|
m_room = room;
|
|
}
|
|
|
|
ChatServiceTask::~ChatServiceTask()
|
|
{
|
|
|
|
}
|
|
|
|
void ChatServiceTask::setMessage( const Oscar::Message& msg )
|
|
{
|
|
m_message = msg;
|
|
}
|
|
|
|
void ChatServiceTask::setEncoding( const QCString& enc )
|
|
{
|
|
m_encoding = enc;
|
|
}
|
|
|
|
void ChatServiceTask::onGo()
|
|
{
|
|
if ( !m_message )
|
|
{
|
|
setSuccess( true, QString::null );
|
|
return;
|
|
}
|
|
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending '" << m_message.textArray() << "' to the "
|
|
<< m_room << " room" << endl;
|
|
Buffer* b = new Buffer();
|
|
b->addDWord( KApplication::random() ); //use kapp since it's convenient
|
|
b->addDWord( KApplication::random() );
|
|
b->addWord( 0x0003 ); //this be message channel 3 mateys! arrr!!
|
|
b->addDWord( 0x00010000 ); //TLV 1 - this means it's a public message
|
|
b->addDWord( 0x00060000 ); //TLV 6 - enables the server sending you your message back
|
|
|
|
Buffer tlv5;
|
|
TLV type2, type3, type1;
|
|
|
|
type2.type = 0x0002;
|
|
type2.length = 0x0008;
|
|
type2.data = m_encoding;
|
|
|
|
type3.type = 0x0003;
|
|
type3.length = 0x0002;
|
|
type3.data = QCString( "en" ); //hardcode for right now. don't know that we can do others
|
|
|
|
type1.type = 0x0001;
|
|
type1.length = m_message.textArray().size();
|
|
type1.data = m_message.textArray();
|
|
tlv5.addWord( 0x0005 );
|
|
tlv5.addWord( 12 + type1.length + type2.length + type3.length );
|
|
tlv5.addTLV( type1 );
|
|
tlv5.addTLV( type2 );
|
|
tlv5.addTLV( type3 );
|
|
|
|
b->addString( tlv5.buffer(), tlv5.length() );
|
|
|
|
FLAP f = { 0x02, 0, 0 };
|
|
SNAC s = { 0x000E, 0x0005, 0x0000, client()->snacSequence() };
|
|
Transfer* t = createTransfer( f, s, b );
|
|
send( t );
|
|
setSuccess( true );
|
|
}
|
|
|
|
bool ChatServiceTask::forMe( const Transfer* t ) const
|
|
{
|
|
const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( t );
|
|
if ( !st )
|
|
return false;
|
|
|
|
if ( st->snacService() != 0x000E )
|
|
return false;
|
|
|
|
switch ( st->snacSubtype() )
|
|
{
|
|
case 0x0003:
|
|
case 0x0002:
|
|
case 0x0006:
|
|
case 0x0009:
|
|
case 0x0004:
|
|
return true;
|
|
break;
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ChatServiceTask::take( Transfer* t )
|
|
{
|
|
if ( !forMe( t ) )
|
|
return false;
|
|
|
|
SnacTransfer* st = dynamic_cast<SnacTransfer*>( t );
|
|
if ( !st )
|
|
return false;
|
|
|
|
setTransfer( t );
|
|
|
|
switch ( st->snacSubtype() )
|
|
{
|
|
case 0x0002:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Parse room info" << endl;
|
|
parseRoomInfo();
|
|
break;
|
|
case 0x0003:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user joined notification" << endl;
|
|
parseJoinNotification();
|
|
break;
|
|
case 0x0004:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user left notification" << endl;
|
|
parseLeftNotification();
|
|
break;
|
|
case 0x0006:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "message from room to client" << endl;
|
|
parseChatMessage();
|
|
break;
|
|
case 0x0009:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "chat error or data" << endl;
|
|
break;
|
|
};
|
|
|
|
setSuccess( 0, QString::null );
|
|
setTransfer( 0 );
|
|
return true;
|
|
}
|
|
|
|
void ChatServiceTask::parseRoomInfo()
|
|
{
|
|
WORD instance;
|
|
BYTE detailLevel;
|
|
Buffer* b = transfer()->buffer();
|
|
|
|
m_exchange = b->getWord();
|
|
QByteArray cookie( b->getBlock( b->getByte() ) );
|
|
instance = b->getWord();
|
|
|
|
detailLevel = b->getByte();
|
|
|
|
//skip the tlv count, we don't care. Buffer::getTLVList() handles this all
|
|
//correctly anyways
|
|
b->skipBytes( 2 );
|
|
|
|
QValueList<Oscar::TLV> tlvList = b->getTLVList();
|
|
QValueList<Oscar::TLV>::iterator it = tlvList.begin();
|
|
QValueList<Oscar::TLV>::iterator itEnd = tlvList.end();
|
|
for ( ; it != itEnd; ++it )
|
|
{
|
|
switch ( ( *it ).type )
|
|
{
|
|
case 0x006A:
|
|
m_internalRoom = QString( ( *it ).data );
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "room name: " << m_room << endl;
|
|
break;
|
|
case 0x006F:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "num occupants: " << ( *it ).data << endl;
|
|
break;
|
|
case 0x0073:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "occupant list" << endl;
|
|
break;
|
|
case 0x00C9:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "flags" << endl;
|
|
break;
|
|
case 0x00CA: //creation time
|
|
case 0x00D1: //max message length
|
|
case 0x00D3: //room description
|
|
case 0x00D6: //encoding 1
|
|
case 0x00D7: //language 1
|
|
case 0x00D8: //encoding 2
|
|
case 0x00D9: //language 2
|
|
case 0x00DA: //maximum visible message length
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "unhandled TLV type " << ( *it ).type << endl;
|
|
break;
|
|
default:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "unknown TLV type " << ( *it ).type << endl;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ChatServiceTask::parseJoinNotification()
|
|
{
|
|
Buffer* b = transfer()->buffer();
|
|
while ( b->length() > 0 )
|
|
{
|
|
QString sender( b->getBUIN() );
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user name:" << sender << endl;
|
|
WORD warningLevel = b->getWord();
|
|
WORD numTLVs = b->getWord();
|
|
for ( int i = 0; i < numTLVs; i++ )
|
|
{
|
|
TLV t = b->getTLV();
|
|
switch ( t.type )
|
|
{
|
|
case 0x0001:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user class: " << t.data << endl;
|
|
break;
|
|
case 0x000F:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "idle time: " << t.data << endl;
|
|
break;
|
|
case 0x0003:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "signon: " << t.data << endl;
|
|
break;
|
|
}
|
|
}
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "emitted userJoinedChat" << endl;
|
|
emit userJoinedChat( m_exchange, m_room, sender );
|
|
}
|
|
|
|
}
|
|
|
|
void ChatServiceTask::parseLeftNotification()
|
|
{
|
|
Buffer* b = transfer()->buffer();
|
|
while ( b->length() > 0 )
|
|
{
|
|
QString sender( b->getBUIN() );
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user name:" << sender << endl;
|
|
WORD warningLevel = b->getWord();
|
|
WORD numTLVs = b->getWord();
|
|
for ( int i = 0; i < numTLVs; i++ )
|
|
{
|
|
TLV t = b->getTLV();
|
|
switch ( t.type )
|
|
{
|
|
case 0x0001:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user class: " << t.data << endl;
|
|
break;
|
|
case 0x000F:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "idle time: " << t.data << endl;
|
|
break;
|
|
case 0x0003:
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "signon: " << t.data << endl;
|
|
break;
|
|
}
|
|
}
|
|
emit userLeftChat( m_exchange, m_room, sender );
|
|
}
|
|
}
|
|
|
|
void ChatServiceTask::parseChatMessage()
|
|
{
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "have new chat room message" << endl;
|
|
Buffer* b = transfer()->buffer();
|
|
bool whisper = true, reflection = false;
|
|
QByteArray language, encoding, message;
|
|
QString sender;
|
|
QByteArray icbmCookie( b->getBlock( 8 ) );
|
|
b->skipBytes( 2 ); //message channel always 0x03
|
|
QValueList<Oscar::TLV> chatTLVs = b->getTLVList();
|
|
QValueList<Oscar::TLV>::iterator it, itEnd = chatTLVs.end();
|
|
for ( it = chatTLVs.begin(); it != itEnd; ++it )
|
|
{
|
|
switch ( ( *it ).type )
|
|
{
|
|
case 0x0001: //if present, message was sent to the room
|
|
whisper = false;
|
|
break;
|
|
case 0x0006: //enable reflection
|
|
reflection = true;
|
|
break;
|
|
case 0x0005: //the good stuff - the actual message
|
|
{
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "parsing the message" << endl;
|
|
//oooh! look! more TLVS! i love those!
|
|
Buffer b( ( *it ).data );
|
|
while ( b.length() >= 4 )
|
|
{
|
|
TLV t = b.getTLV();
|
|
switch( t.type )
|
|
{
|
|
case 0x0003:
|
|
language = t.data;
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "language: " << language << endl;
|
|
break;
|
|
case 0x0002:
|
|
encoding = t.data;
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "encoding: " << encoding << endl;
|
|
break;
|
|
case 0x0001:
|
|
message = t.data;
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "message: " << message << endl;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 0x0003: //user info
|
|
{
|
|
Buffer b( ( *it ).data );
|
|
sender = QString( b.getBUIN() );
|
|
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got user info. sender is " << sender << endl;
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
QTextCodec* codec = QTextCodec::codecForName( encoding );
|
|
if ( ! codec )
|
|
codec = QTextCodec::codecForMib( 4 );
|
|
QString msgText( codec->toUnicode( message ) );
|
|
Oscar::Message omessage;
|
|
omessage.setReceiver( client()->userId() );
|
|
omessage.setSender( sender );
|
|
omessage.setTimestamp( QDateTime::currentDateTime() );
|
|
omessage.setText( Oscar::Message::UTF8, msgText );
|
|
omessage.setType( 0x03 );
|
|
omessage.setExchange( m_exchange );
|
|
omessage.setChatRoom( m_room );
|
|
emit newChatMessage( omessage );
|
|
}
|
|
|
|
void ChatServiceTask::parseChatError()
|
|
{
|
|
|
|
}
|
|
|
|
|
|
#include "chatservicetask.moc"
|
|
|