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.
tdenetwork/kopete/protocols/oscar/liboscar/messagereceivertask.cpp

462 lines
13 KiB

/*
messagereceivertask.cpp - Incoming OSCAR Messaging Handler
Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
*************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
*************************************************************************
*/
#include "messagereceivertask.h"
#include <tqtextcodec.h>
#include <kdebug.h>
#include "transfer.h"
#include "buffer.h"
#include "connection.h"
#include "oscarutils.h"
#include "userdetails.h"
MessageReceiverTask::MessageReceiverTask( Task* parent ) : Task( parent )
{
}
MessageReceiverTask::~MessageReceiverTask()
{
}
bool MessageReceiverTask::forMe( const Transfer* transfer ) const
{
const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
if ( !st )
return false;
if ( st->snacService() == 0x0004 )
{
WORD subtype = st->snacSubtype();
switch ( subtype )
{
case 0x0007:
case 0x000B:
return true;
break;
default:
return false;
break;
}
}
else
return false;
}
bool MessageReceiverTask::take( Transfer* transfer )
{
if ( forMe( transfer ) )
{
const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
if ( !st )
return false;
m_currentSnacSubtype = st->snacSubtype();
Buffer* b = transfer->buffer();
m_icbmCookie = b->getBlock( 8 );
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "icbm cookie is " << m_icbmCookie << endl;
m_channel = b->getWord();
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "channel is " << m_channel << endl;
if ( m_currentSnacSubtype == 0x0007 )
{
UserDetails ud;
ud.fill( b );
m_fromUser = ud.userId();
switch( m_channel )
{
case 0x0001:
setTransfer( transfer );
handleType1Message();
setTransfer( 0 );
return true;
break;
case 0x0002:
setTransfer( transfer );
handleType2Message();
setTransfer( 0 );
return true;
break;
case 0x0004:
setTransfer( transfer );
handleType4Message();
setTransfer( 0 );
return true;
break;
default:
kdWarning(OSCAR_RAW_DEBUG) << "A message was received on an unknown channel. Channel is " << m_channel << endl;
return false;
break;
}
}
else
{
int screenNameLength = b->getByte();
m_fromUser = TQString( b->getBlock( screenNameLength ) );
setTransfer( transfer );
handleAutoResponse();
setTransfer( 0 );
return true;
}
}
return false;
}
void MessageReceiverTask::handleType1Message()
{
Oscar::Message msg;
TQValueList<TLV> messageTLVList = transfer()->buffer()->getTLVList();
TLV t = Oscar::findTLV( messageTLVList, 0x0002 );
if ( !t )
{
kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "Received a message packet with no message!" << endl;
return;
}
Buffer messageBuffer( t.data );
TQValueList<TLV> innerTLVList = messageBuffer.getTLVList();
TQValueList<TLV>::iterator it = innerTLVList.begin(), listEnd = innerTLVList.end();
for ( ; (*it); ++it )
{
switch ( ( *it ).type )
{
case 0x0501:
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got features tlv. length: "
<< ( *it ).length << " data: " << ( *it ).data << endl;
break;
case 0x0101:
{
Buffer message( ( *it ).data );
m_charSet = message.getWord();
m_subCharSet = message.getWord();
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Message charset: " << m_charSet
<< " message subcharset: " << m_subCharSet << endl;
if ( m_charSet == 0x0002 )
msg.setEncoding( Oscar::Message::UCS2 );
else
msg.setEncoding( Oscar::Message::UserDefined );
//message length is buffer length - length of ( charset + subcharset ) */
int msgLength = ( *it ).length - 4;
TQByteArray msgArray( message.getBlock( msgLength ) );
msg.setTextArray( msgArray );
break;
} //end case
default:
kdDebug(OSCAR_RAW_DEBUG) << "Ignoring TLV of type " << ( *it ).type << endl;
break;
} //end switch
}
TLV autoResponse = Oscar::findTLV( messageTLVList, 0x0004 );
if ( autoResponse )
{
kdDebug(OSCAR_RAW_DEBUG) << "auto response message" << endl;
msg.addProperty( Oscar::Message::AutoResponse );
}
else
msg.addProperty( Oscar::Message::Normal );
msg.setSender( m_fromUser );
msg.setReceiver( client()->userId() );
msg.setTimestamp( TQDateTime::currentDateTime() );
msg.setType( 0x01 );
emit receivedMessage( msg );
}
void MessageReceiverTask::handleType2Message()
{
kdDebug(14151) << k_funcinfo << "Received Type 2 message. Trying to handle it..." << endl;
Oscar::Message msg;
TQValueList<TLV> messageTLVList = transfer()->buffer()->getTLVList();
TLV t = Oscar::findTLV( messageTLVList, 0x0005 );
if ( !t )
{
kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "Received a channel 2 message packet with no message!" << endl;
return;
}
Buffer messageBuffer( t.data );
kdDebug(14151) << k_funcinfo << "Buffer length is " << messageBuffer.length() << endl;
// request type
int requestType = messageBuffer.getWord();
kdDebug(14151) << k_funcinfo << "Request type (0 - request, 1 - cancel, 2 - accept): " << requestType << endl;
// skip the message id cookie, already handled above
messageBuffer.skipBytes( 8 );
// next is capability identifier (GUID). skip for now
messageBuffer.skipBytes( 16 );
while( messageBuffer.length() > 0 )
{
TLV tlv = messageBuffer.getTLV();
switch ( tlv.type )
{
case 0x0004:
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got external ip: "
<< tlv.length << " data: " << tlv.data << endl;
break;
case 0x0005:
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got listening port: "
<< tlv.length << " data: " << tlv.data << endl;
break;
case 0x000A:
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got Acktype: " // 0x0001 normal message, 2 Abort Request, 3 Acknowledge request
<< tlv.length << " data: " << tlv.data << endl;
break;
case 0x000B:
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got unknown TLV 0x000B: "
<< tlv.length << " data: " << tlv.data << endl;
break;
case 0x000F:
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got unknown empty TLV 0x000F" << endl;
break;
case 0x2711:
{
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got a TLV 2711" << endl;
Buffer tlv2711Buffer( tlv.data );
parseRendezvousData( &tlv2711Buffer, &msg );
if ( msg.messageType() == 0x1A )
{
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received plugin message" << endl;
break;
}
switch ( requestType )
{
case 0x00: // some request
emit receivedMessage( msg );
break;
case 0x01:
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received Abort Mesage" << endl;
break;
case 0x02:
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received OK Message" << endl;
break;
default:
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received unknown request type: " << requestType << endl;
break;
}
break;
} //end case
default:
kdDebug(OSCAR_RAW_DEBUG) << "Ignoring TLV of type " << tlv.type << endl;
break;
} //end switch
}//end while
}
void MessageReceiverTask::handleType4Message()
{
TLV tlv5 = transfer()->buffer()->getTLV();
kdDebug(14151) << k_funcinfo << "The first TLV is of type " << tlv5.type << endl;
if (tlv5.type != 0x0005)
{
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Aborting because first TLV != TLV(5)" << endl;
return;
}
Buffer tlv5buffer(tlv5.data, tlv5.length);
DWORD uin = tlv5buffer.getLEDWord(); // little endian for no sane reason!
if ( TQString::number(uin) != m_fromUser )
kdWarning(14151) << k_funcinfo << "message uin does not match uin found in packet header!" << endl;
BYTE msgType = tlv5buffer.getByte();
BYTE msgFlags = tlv5buffer.getByte();
kdDebug(14151) << k_funcinfo << "Received server message. type = " << msgType
<< ", flags = " << msgFlags << endl;
//handle the special user types
Oscar::Message msg;
TQString msgSender;
switch ( msgType )
{
case 0x0D:
msgSender = "ICQ Web Express";
msg.addProperty( Oscar::Message::WWP );
break;
case 0x0E:
msgSender = "ICQ Email Express";
msg.addProperty( Oscar::Message::EMail );
break;
default:
msgSender = m_fromUser;
break;
};
TQCString msgText = tlv5buffer.getLNTS();
int msgLength = msgText.size();
if ( msgType == 0x0D || msgType == 0x0E )
{
for ( int i = 0; i < msgLength; i++ )
{
if ( msgText[i] == (char)0xFE )
msgText[i] = 0x20;
}
}
switch ( msgFlags )
{
case 0x03:
msg.addProperty( Oscar::Message::AutoResponse );
break;
case 0x01:
msg.addProperty( Oscar::Message::Normal );
break;
default:
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Not handling message flag " << msgFlags << endl;
break;
}
msg.setType( 0x04 );
msg.setTimestamp( TQDateTime::currentDateTime() );
msg.setSender( msgSender );
msg.setReceiver( client()->userId() );
msg.setEncoding( Oscar::Message::UserDefined );
msg.setTextArray( msgText );
emit receivedMessage( msg );
}
void MessageReceiverTask::handleAutoResponse()
{
kdDebug(14151) << k_funcinfo << "Received auto response. Trying to handle it..." << endl;
Oscar::Message msg;
msg.addProperty( Oscar::Message::AutoResponse );
Buffer* b = transfer()->buffer();
// reason code
int reasonCode = b->getWord();
kdDebug(14151) << k_funcinfo << "Reason code (1 - channel not supported, 2 - busted payload, 3 - channel specific data): " << reasonCode << endl;
parseRendezvousData( b, &msg );
emit receivedMessage( msg );
}
void MessageReceiverTask::parseRendezvousData( Buffer* b, Oscar::Message* msg )
{
int length1 = b->getLEWord();
if ( length1 != 0x001B )
{ // all real messages (actually their header) seem to have length 0x1B
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Weired Message length. Bailing out!" << endl;
return;
}
int protocolVersion = b->getLEWord(); // the extended data protocol version, there are quite a few...
// plugin (for file transfer & stuff, all zeros for regular message
b->skipBytes( 16 );
// unknown
b->skipBytes( 2 );
// client capablities
b->skipBytes( 4 );
// unknown
b->skipBytes( 1 );
// (down)counter: basically just some number, ICQ counts down, miranda up, doesnt matter.
// BUT: when sending auto response on channel 2, like with the icbm cookie, we need to send the same value!
int channel2Counter = b->getLEWord();
// the next one is length (of a counter + following all-zero field), but also seems to indicate what type of message this is
int length2 = b->getLEWord();
// the only length usable ATM is 0x000E, which is a message
switch( length2 )
{
case 0x000E:
{
int cookie = b->getLEWord();
for ( int i = 0; i < 12; i++ )
{ // 12 bytes all zeros
b->getByte();
}
// now starts the real message
// TODO if type is PLAIN, there is (might be?) an additional TLV with color and font information at the end...
uint messageType = b->getByte();
int flags = b->getByte();
int status = b->getLEWord(); // don't know what status this is or what to use it for
int priority = b->getLEWord(); // don't know what that's good for either
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Message type is: " << messageType << endl;
TQCString msgText( b->getLELNTS() );
Oscar::Message::Encoding encoding = Oscar::Message::UserDefined;
int fgcolor = 0x00000000;
int bgcolor = 0x00ffffff;
// Don't parse plugin message
if ( b->length() >= 8 && messageType != 0x1A )
{
fgcolor = b->getLEDWord();
bgcolor = b->getLEDWord();
while ( b->length() >= 4 )
{
int capLength = b->getLEDWord();
if ( b->length() < capLength )
break;
TQByteArray cap( b->getBlock( capLength ) );
if ( qstrncmp ( cap.data(), "{0946134E-4C7F-11D1-8222-444553540000}", capLength ) == 0 )
encoding = Oscar::Message::UTF8;
}
}
msg->setEncoding( encoding );
msg->setTextArray( msgText );
if ( ( messageType & 0xF0 ) == 0xE0 ) // check higher byte for value E -> status message request
msg->addProperty( Oscar::Message::StatusMessageRequest );
else
msg->addProperty( Oscar::Message::Request );
msg->setSender( m_fromUser );
msg->setReceiver( client()->userId() );
msg->setTimestamp( TQDateTime::currentDateTime() );
msg->setType( 0x02 );
msg->setIcbmCookie( m_icbmCookie );
msg->setProtocolVersion( protocolVersion );
msg->setChannel2Counter( channel2Counter );
msg->setMessageType( messageType );
break;
}
default:
kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got unknown message with length2 " << length2 << endl;
}
}
TQTextCodec* MessageReceiverTask::guessCodec( const TQCString& string )
{
Q_UNUSED( string );
return 0;
}
#include "messagereceivertask.moc"
//kate: indent-mode csands;