summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/oscar/liboscar/userdetails.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/oscar/liboscar/userdetails.cpp')
-rw-r--r--kopete/protocols/oscar/liboscar/userdetails.cpp555
1 files changed, 555 insertions, 0 deletions
diff --git a/kopete/protocols/oscar/liboscar/userdetails.cpp b/kopete/protocols/oscar/liboscar/userdetails.cpp
new file mode 100644
index 00000000..db7d4d1d
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/userdetails.cpp
@@ -0,0 +1,555 @@
+/*
+ Kopete Oscar Protocol
+ userdetails.cpp - user details from the extended status packet
+
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 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 "userdetails.h"
+
+#include "buffer.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <qptrlist.h>
+#include "oscarutils.h"
+#include "oscardebug.h"
+
+using namespace Oscar;
+
+UserDetails::UserDetails()
+{
+ m_warningLevel = 0;
+ m_userClass = 0;
+ m_idleTime = 0;
+ m_extendedStatus = 0;
+ m_capabilities = 0;
+ m_dcPort = 0;
+ m_dcType = 0;
+ m_dcProtoVersion = 0;
+ m_dcAuthCookie = 0;
+ m_dcWebFrontPort = 0;
+ m_dcClientFeatures = 0;
+ m_dcLastInfoUpdateTime = 0;
+ m_dcLastExtInfoUpdateTime = 0;
+ m_dcLastExtStatusUpdateTime = 0;
+ m_userClassSpecified = false;
+ m_memberSinceSpecified = false;
+ m_onlineSinceSpecified = false;
+ m_numSecondsOnlineSpecified = false;
+ m_idleTimeSpecified = false;
+ m_extendedStatusSpecified = false;
+ m_capabilitiesSpecified = false;
+ m_dcOutsideSpecified = false;
+ m_dcInsideSpecified = false;
+ m_iconSpecified = false;
+}
+
+
+UserDetails::~UserDetails()
+{
+}
+
+int UserDetails::warningLevel() const
+{
+ return m_warningLevel;
+}
+
+QString UserDetails::userId() const
+{
+ return m_userId;
+}
+
+WORD UserDetails::idleTime() const
+{
+ return m_idleTime;
+}
+
+KNetwork::KIpAddress UserDetails::dcInternalIp() const
+{
+ return m_dcInsideIp;
+}
+
+KNetwork::KIpAddress UserDetails::dcExternalIp() const
+{
+ return m_dcOutsideIp;
+}
+
+DWORD UserDetails::dcPort() const
+{
+ return m_dcPort;
+}
+
+QDateTime UserDetails::onlineSinceTime() const
+{
+ return m_onlineSince;
+}
+
+QDateTime UserDetails::memberSinceTime() const
+{
+ return m_memberSince;
+}
+
+int UserDetails::userClass() const
+{
+ return m_userClass;
+}
+
+DWORD UserDetails::extendedStatus() const
+{
+ return m_extendedStatus;
+}
+
+BYTE UserDetails::iconCheckSumType() const
+{
+ return m_iconChecksumType;
+}
+
+QByteArray UserDetails::buddyIconHash() const
+{
+ return m_md5IconHash;
+}
+
+QString UserDetails::clientName() const
+{
+ if ( !m_clientVersion.isEmpty() )
+ return i18n("Translators: client-name client-version",
+ "%1 %2").arg(m_clientName, m_clientVersion);
+ else
+ return m_clientName;
+}
+
+void UserDetails::fill( Buffer * buffer )
+{
+ BYTE snLen = buffer->getByte();
+ QString user = QString( buffer->getBlock( snLen ) );
+ if ( !user.isEmpty() )
+ m_userId = user;
+ m_warningLevel = buffer->getWord();
+ WORD numTLVs = buffer->getWord();
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Got user info for " << user << endl;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Warning level is " << m_warningLevel << endl;
+#endif
+ //start parsing TLVs
+ for( int i = 0; i < numTLVs; ++i )
+ {
+ TLV t = buffer->getTLV();
+ if ( t )
+ {
+ Buffer b( t.data, t.length );
+ switch( t.type )
+ {
+ case 0x0001: //user class
+ m_userClass = b.getWord();
+ m_userClassSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "User class is " << m_userClass << endl;
+#endif
+ break;
+ case 0x0002: //member since
+ case 0x0005: //member since
+ m_memberSince.setTime_t( b.getDWord() );
+ m_memberSinceSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Member since " << m_memberSince << endl;
+#endif
+ break;
+ case 0x0003: //sigon time
+ m_onlineSince.setTime_t( b.getDWord() );
+ m_onlineSinceSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Signed on at " << m_onlineSince << endl;
+#endif
+ break;
+ case 0x0004: //idle time
+ m_idleTime = b.getWord() * 60;
+#ifdef OSCAR_USERINFO_DEBUG
+ m_idleTimeSpecified = true;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Idle time is " << m_idleTime << endl;
+#endif
+ break;
+ case 0x0006: //extended user status
+ m_extendedStatus = b.getDWord();
+ m_extendedStatusSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Extended status is " << QString::number( m_extendedStatus, 16 ) << endl;
+#endif
+ break;
+ case 0x000A: //external IP address
+ m_dcOutsideIp = KNetwork::KIpAddress( ntohl( b.getDWord() ) );
+ m_dcOutsideSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "External IP address is " << m_dcOutsideIp.toString() << endl;
+#endif
+ break;
+ case 0x000C: //DC info
+ m_dcInsideIp = KNetwork::KIpAddress( ntohl( b.getDWord() ) );
+ m_dcPort = b.getDWord();
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Internal IP address is " << m_dcInsideIp.toString() << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Port number is " << m_dcPort << endl;
+#endif
+ m_dcType = b.getByte();
+ m_dcProtoVersion = b.getWord();
+ m_dcAuthCookie = b.getDWord();
+ m_dcWebFrontPort = b.getDWord();
+ m_dcClientFeatures = b.getDWord();
+ m_dcLastInfoUpdateTime = b.getDWord();
+ m_dcLastExtInfoUpdateTime = b.getDWord();
+ m_dcLastExtStatusUpdateTime = b.getDWord();
+ b.getWord(); //unknown.
+ m_dcInsideSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got DC info" << endl;
+#endif
+ break;
+ case 0x000D: //capability info
+ m_capabilities = Oscar::parseCapabilities( b, m_clientVersion );
+ m_capabilitiesSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got capability info" << endl;
+#endif
+ break;
+ case 0x0010:
+ case 0x000F: //online time
+ m_numSecondsOnline = b.getDWord();
+ m_numSecondsOnlineSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Online for " << m_numSecondsOnline << endl;
+#endif
+ break;
+ case 0x001D:
+ {
+ if ( t.length == 0 )
+ break;
+
+ while ( b.length() > 0 )
+ {
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Icon and available message info" << endl;
+#endif
+ WORD type2 = b.getWord();
+ BYTE number = b.getByte();
+ BYTE length = b.getByte();
+ switch( type2 )
+ {
+ case 0x0000:
+ b.skipBytes(length);
+ break;
+ case 0x0001:
+ if ( length > 0 && ( number == 0x01 || number == 0x00 ) )
+ {
+ m_iconChecksumType = number;
+ m_md5IconHash.duplicate( b.getBlock( length ), length );
+ m_iconSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "checksum:" << m_md5IconHash << endl;
+#endif
+ }
+ else
+ {
+ kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "icon checksum indicated"
+ << " but unable to parse checksum" << endl;
+ b.skipBytes( length );
+ }
+ break;
+ case 0x0002:
+ if ( length > 0 )
+ {
+ m_availableMessage = QString( b.getBSTR() );
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "available message:" << m_availableMessage << endl;
+#endif
+ if ( b.length() >= 4 && b.getWord() == 0x0001 )
+ {
+ b.skipBytes( 2 );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Encoding:" << b.getBSTR() << endl;
+ }
+ }
+ else
+ kdDebug(OSCAR_RAW_DEBUG) << "not enough bytes for available message" << endl;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Unknown TLV, type=" << t.type << ", length=" << t.length
+ << " in userinfo" << endl;
+ break;
+ };
+ //detach buffer and free TLV data
+ b.clear();
+ }
+ }
+
+ //do client detection on fill
+ if ( m_capabilitiesSpecified )
+ detectClient();
+}
+
+void UserDetails::detectClient()
+{
+
+ /* My thanks to mETz for stealing^Wusing this code from SIM.
+ * Client type detection ---
+ * Most of this code is based on sim-icq code
+ * Thanks a lot for all the tests you guys must have made
+ * without sim-icq I would have only checked for the capabilities
+ */
+
+ bool clientMatched = false;
+ if (m_capabilities != 0)
+ {
+ bool clientMatched = false;
+ if (hasCap(CAP_KOPETE))
+ {
+ m_clientName=i18n("Kopete");
+ return;
+ }
+ else if (hasCap(CAP_MICQ))
+ {
+ m_clientName=i18n("MICQ");
+ return;
+ }
+ else if (hasCap(CAP_SIMNEW) || hasCap(CAP_SIMOLD))
+ {
+ m_clientName=i18n("SIM");
+ return;
+ }
+ else if (hasCap(CAP_TRILLIANCRYPT) || hasCap(CAP_TRILLIAN))
+ {
+ m_clientName=i18n("Trillian");
+ return;
+ }
+ else if (hasCap(CAP_MACICQ))
+ {
+ m_clientName=i18n("MacICQ");
+ return;
+ }
+ else if ((m_dcLastInfoUpdateTime & 0xFF7F0000L) == 0x7D000000L)
+ {
+ unsigned ver = m_dcLastInfoUpdateTime & 0xFFFF;
+ if (m_dcLastInfoUpdateTime & 0x00800000L)
+ m_clientName=i18n("Licq SSL");
+ else
+ m_clientName=i18n("Licq");
+
+ if (ver % 10)
+ m_clientVersion.sprintf("%d.%d.%u", ver/1000, (ver/10)%100, ver%10);
+ else
+ m_clientVersion.sprintf("%d.%u", ver/1000, (ver/10)%100);
+ return;
+ }
+ else // some client we could not detect using capabilities
+ {
+
+ clientMatched=true; // default case will set it to false again if we did not find anything
+ switch (m_dcLastInfoUpdateTime)
+ {
+ case 0xFFFFFFFFL: //gaim behaves like official AIM so we can't detect them, only look for miranda
+ {
+ if (m_dcLastExtStatusUpdateTime & 0x80000000)
+ m_clientName=QString::fromLatin1("Miranda alpha");
+ else
+ m_clientName=QString::fromLatin1("Miranda");
+
+ DWORD version = (m_dcLastExtInfoUpdateTime & 0xFFFFFF);
+ BYTE major1 = ((version >> 24) & 0xFF);
+ BYTE major2 = ((version >> 16) & 0xFF);
+ BYTE minor1 = ((version >> 8) & 0xFF);
+ BYTE minor2 = (version & 0xFF);
+ if (minor2 > 0) // w.x.y.z
+ {
+ m_clientVersion.sprintf("%u.%u.%u.%u", major1, major2,
+ minor1, minor2);
+ }
+ else if (minor1 > 0) // w.x.y
+ {
+ m_clientVersion.sprintf("%u.%u.%u", major1, major2, minor1);
+ }
+ else // w.x
+ {
+ m_clientVersion.sprintf("%u.%u", major1, major2);
+ }
+ }
+ break;
+ case 0xFFFFFF8FL:
+ m_clientName = QString::fromLatin1("StrICQ");
+ break;
+ case 0xFFFFFF42L:
+ m_clientName = QString::fromLatin1("mICQ");
+ break;
+ case 0xFFFFFFBEL:
+ m_clientName = QString::fromLatin1("alicq");
+ break;
+ case 0xFFFFFF7FL:
+ m_clientName = QString::fromLatin1("&RQ");
+ break;
+ case 0xFFFFFFABL:
+ m_clientName = QString::fromLatin1("YSM");
+ break;
+ case 0x3AA773EEL:
+ if ((m_dcLastExtStatusUpdateTime == 0x3AA66380L) &&
+ (m_dcLastExtInfoUpdateTime == 0x3A877A42L))
+ {
+ m_clientName=QString::fromLatin1("libicq2000");
+ }
+ break;
+ default:
+ clientMatched=false;
+ break;
+ }
+ }
+ }
+
+ if (!clientMatched) // now the fuzzy clientsearch starts =)
+ {
+ if (hasCap(CAP_TYPING))
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Client protocol version = " << m_dcProtoVersion << endl;
+ switch (m_dcProtoVersion)
+ {
+ case 10:
+ m_clientName=QString::fromLatin1("ICQ 2003b");
+ break;
+ case 9:
+ m_clientName=QString::fromLatin1("ICQ Lite");
+ break;
+ case 8:
+ m_clientName=QString::fromLatin1("Miranda");
+ break;
+ default:
+ m_clientName=QString::fromLatin1("ICQ2go");
+ }
+ }
+ else if (hasCap(CAP_BUDDYICON)) // only gaim seems to advertize this on ICQ
+ {
+ m_clientName = QString::fromLatin1("Gaim");
+ }
+ else if (hasCap(CAP_XTRAZ))
+ {
+ m_clientName = QString::fromLatin1("ICQ 4.0 Lite");
+ }
+ else if ((hasCap(CAP_STR_2001) || hasCap(CAP_ICQSERVERRELAY)) &&
+ hasCap(CAP_IS_2001))
+ {
+ m_clientName = QString::fromLatin1( "ICQ 2001");
+ }
+ else if ((hasCap(CAP_STR_2001) || hasCap(CAP_ICQSERVERRELAY)) &&
+ hasCap(CAP_STR_2002))
+ {
+ m_clientName = QString::fromLatin1("ICQ 2002");
+ }
+ else if (hasCap(CAP_RTFMSGS) && hasCap(CAP_UTF8) &&
+ hasCap(CAP_ICQSERVERRELAY) && hasCap(CAP_ISICQ))
+ {
+ m_clientName = QString::fromLatin1("ICQ 2003a");
+ }
+ else if (hasCap(CAP_ICQSERVERRELAY) && hasCap(CAP_ISICQ))
+ {
+ m_clientName =QString::fromLatin1("ICQ 2001b");
+ }
+ else if ((m_dcProtoVersion == 7) && hasCap(CAP_RTFMSGS))
+ {
+ m_clientName = QString::fromLatin1("GnomeICU");
+ }
+ }
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "detected client as: " << m_clientName
+ << " " << m_clientVersion << endl;
+
+}
+
+bool UserDetails::hasCap( int capNumber ) const
+{
+ bool capPresent = ( ( m_capabilities & ( 1 << capNumber ) ) != 0 );
+ return capPresent;
+}
+
+void UserDetails::merge( const UserDetails& ud )
+{
+ m_userId = ud.m_userId;
+ m_warningLevel = ud.m_warningLevel;
+ if ( ud.m_userClassSpecified )
+ {
+ m_userClass = ud.m_userClass;
+ m_userClassSpecified = true;
+ }
+ if ( ud.m_memberSinceSpecified )
+ {
+ m_memberSince = ud.m_memberSince;
+ m_memberSinceSpecified = true;
+ }
+ if ( ud.m_onlineSinceSpecified )
+ {
+ m_onlineSince = ud.m_onlineSince;
+ m_onlineSinceSpecified = true;
+ }
+ if ( ud.m_numSecondsOnlineSpecified )
+ {
+ m_numSecondsOnline = ud.m_numSecondsOnline;
+ m_numSecondsOnlineSpecified = true;
+ }
+ if ( ud.m_idleTimeSpecified )
+ {
+ m_idleTime = ud.m_idleTime;
+ m_idleTimeSpecified = true;
+ }
+ if ( ud.m_extendedStatusSpecified )
+ {
+ m_extendedStatus = ud.m_extendedStatus;
+ m_extendedStatusSpecified = true;
+ }
+ if ( ud.m_capabilitiesSpecified )
+ {
+ m_capabilities = ud.m_capabilities;
+ m_clientVersion = ud.m_clientVersion;
+ m_clientName = ud.m_clientName;
+ m_capabilitiesSpecified = true;
+ }
+ if ( ud.m_dcOutsideSpecified )
+ {
+ m_dcOutsideIp = ud.m_dcOutsideIp;
+ m_dcOutsideSpecified = true;
+ }
+ if ( ud.m_dcInsideSpecified )
+ {
+ m_dcInsideIp = ud.m_dcInsideIp;
+ m_dcPort = ud.m_dcPort;
+ m_dcType = ud.m_dcType;
+ m_dcProtoVersion = ud.m_dcProtoVersion;
+ m_dcAuthCookie = ud.m_dcAuthCookie;
+ m_dcWebFrontPort = ud.m_dcWebFrontPort;
+ m_dcClientFeatures = ud.m_dcClientFeatures;
+ m_dcLastInfoUpdateTime = ud.m_dcLastInfoUpdateTime;
+ m_dcLastExtInfoUpdateTime = ud.m_dcLastExtInfoUpdateTime;
+ m_dcLastExtStatusUpdateTime = ud.m_dcLastExtStatusUpdateTime;
+ m_dcInsideSpecified = true;
+ }
+ if ( ud.m_iconSpecified )
+ {
+ m_iconChecksumType = ud.m_iconChecksumType;
+ m_md5IconHash = ud.m_md5IconHash;
+ m_iconSpecified = true;
+ }
+ m_availableMessage = ud.m_availableMessage;
+}
+
+//kate: tab-width 4; indent-mode csands;